CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Team
    • Issues (Github)
    • YouTube Channel
    • Get Involved
    • Bakery
    • Featured Resources
    • Newsletter
    • Certification
    • My CakePHP
    • CakeFest
    • Facebook
    • Twitter
    • Help & Support
    • Forum
    • Stack Overflow
    • IRC
    • Slack
    • Paid Support
CakePHP

C CakePHP 3.8 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.8
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Namespaces

  • Cake
    • Auth
      • Storage
    • Cache
      • Engine
    • Collection
      • Iterator
    • Command
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
      • Retry
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
      • Cookie
      • Exception
      • Middleware
      • Session
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • Fixture
      • Stub
    • Utility
      • Exception
    • Validation
    • View
      • Exception
      • Form
      • Helper
      • Widget
  • None

Classes

  • AbstractTransport
  • Email
  • Mailer
  • TransportFactory
  • TransportRegistry

Traits

  • MailerAwareTrait
   1: <?php
   2: /**
   3:  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
   4:  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
   5:  *
   6:  * Licensed under The MIT License
   7:  * For full copyright and license information, please see the LICENSE.txt
   8:  * Redistributions of files must retain the above copyright notice.
   9:  *
  10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11:  * @link          https://cakephp.org CakePHP(tm) Project
  12:  * @since         2.0.0
  13:  * @license       https://opensource.org/licenses/mit-license.php MIT License
  14:  */
  15: namespace Cake\Mailer;
  16: 
  17: use BadMethodCallException;
  18: use Cake\Core\Configure;
  19: use Cake\Core\StaticConfigTrait;
  20: use Cake\Filesystem\File;
  21: use Cake\Http\Client\FormDataPart;
  22: use Cake\Log\Log;
  23: use Cake\Utility\Hash;
  24: use Cake\Utility\Security;
  25: use Cake\Utility\Text;
  26: use Cake\View\ViewVarsTrait;
  27: use Closure;
  28: use Exception;
  29: use InvalidArgumentException;
  30: use JsonSerializable;
  31: use LogicException;
  32: use PDO;
  33: use RuntimeException;
  34: use Serializable;
  35: use SimpleXMLElement;
  36: 
  37: /**
  38:  * CakePHP Email class.
  39:  *
  40:  * This class is used for sending Internet Message Format based
  41:  * on the standard outlined in https://www.rfc-editor.org/rfc/rfc2822.txt
  42:  *
  43:  * ### Configuration
  44:  *
  45:  * Configuration for Email is managed by Email::config() and Email::configTransport().
  46:  * Email::config() can be used to add or read a configuration profile for Email instances.
  47:  * Once made configuration profiles can be used to re-use across various email messages your
  48:  * application sends.
  49:  */
  50: class Email implements JsonSerializable, Serializable
  51: {
  52:     use StaticConfigTrait;
  53:     use ViewVarsTrait;
  54: 
  55:     /**
  56:      * Line length - no should more - RFC 2822 - 2.1.1
  57:      *
  58:      * @var int
  59:      */
  60:     const LINE_LENGTH_SHOULD = 78;
  61: 
  62:     /**
  63:      * Line length - no must more - RFC 2822 - 2.1.1
  64:      *
  65:      * @var int
  66:      */
  67:     const LINE_LENGTH_MUST = 998;
  68: 
  69:     /**
  70:      * Type of message - HTML
  71:      *
  72:      * @var string
  73:      */
  74:     const MESSAGE_HTML = 'html';
  75: 
  76:     /**
  77:      * Type of message - TEXT
  78:      *
  79:      * @var string
  80:      */
  81:     const MESSAGE_TEXT = 'text';
  82: 
  83:     /**
  84:      * Holds the regex pattern for email validation
  85:      *
  86:      * @var string
  87:      */
  88:     const EMAIL_PATTERN = '/^((?:[\p{L}0-9.!#$%&\'*+\/=?^_`{|}~-]+)*@[\p{L}0-9-._]+)$/ui';
  89: 
  90:     /**
  91:      * Recipient of the email
  92:      *
  93:      * @var array
  94:      */
  95:     protected $_to = [];
  96: 
  97:     /**
  98:      * The mail which the email is sent from
  99:      *
 100:      * @var array
 101:      */
 102:     protected $_from = [];
 103: 
 104:     /**
 105:      * The sender email
 106:      *
 107:      * @var array
 108:      */
 109:     protected $_sender = [];
 110: 
 111:     /**
 112:      * The email the recipient will reply to
 113:      *
 114:      * @var array
 115:      */
 116:     protected $_replyTo = [];
 117: 
 118:     /**
 119:      * The read receipt email
 120:      *
 121:      * @var array
 122:      */
 123:     protected $_readReceipt = [];
 124: 
 125:     /**
 126:      * The mail that will be used in case of any errors like
 127:      * - Remote mailserver down
 128:      * - Remote user has exceeded his quota
 129:      * - Unknown user
 130:      *
 131:      * @var array
 132:      */
 133:     protected $_returnPath = [];
 134: 
 135:     /**
 136:      * Carbon Copy
 137:      *
 138:      * List of email's that should receive a copy of the email.
 139:      * The Recipient WILL be able to see this list
 140:      *
 141:      * @var array
 142:      */
 143:     protected $_cc = [];
 144: 
 145:     /**
 146:      * Blind Carbon Copy
 147:      *
 148:      * List of email's that should receive a copy of the email.
 149:      * The Recipient WILL NOT be able to see this list
 150:      *
 151:      * @var array
 152:      */
 153:     protected $_bcc = [];
 154: 
 155:     /**
 156:      * Message ID
 157:      *
 158:      * @var bool|string
 159:      */
 160:     protected $_messageId = true;
 161: 
 162:     /**
 163:      * Domain for messageId generation.
 164:      * Needs to be manually set for CLI mailing as env('HTTP_HOST') is empty
 165:      *
 166:      * @var string
 167:      */
 168:     protected $_domain;
 169: 
 170:     /**
 171:      * The subject of the email
 172:      *
 173:      * @var string
 174:      */
 175:     protected $_subject = '';
 176: 
 177:     /**
 178:      * Associative array of a user defined headers
 179:      * Keys will be prefixed 'X-' as per RFC2822 Section 4.7.5
 180:      *
 181:      * @var array
 182:      */
 183:     protected $_headers = [];
 184: 
 185:     /**
 186:      * Text message
 187:      *
 188:      * @var string
 189:      */
 190:     protected $_textMessage = '';
 191: 
 192:     /**
 193:      * Html message
 194:      *
 195:      * @var string
 196:      */
 197:     protected $_htmlMessage = '';
 198: 
 199:     /**
 200:      * Final message to send
 201:      *
 202:      * @var array
 203:      */
 204:     protected $_message = [];
 205: 
 206:     /**
 207:      * Available formats to be sent.
 208:      *
 209:      * @var array
 210:      */
 211:     protected $_emailFormatAvailable = ['text', 'html', 'both'];
 212: 
 213:     /**
 214:      * What format should the email be sent in
 215:      *
 216:      * @var string
 217:      */
 218:     protected $_emailFormat = 'text';
 219: 
 220:     /**
 221:      * The transport instance to use for sending mail.
 222:      *
 223:      * @var \Cake\Mailer\AbstractTransport|null
 224:      */
 225:     protected $_transport;
 226: 
 227:     /**
 228:      * Charset the email body is sent in
 229:      *
 230:      * @var string
 231:      */
 232:     public $charset = 'utf-8';
 233: 
 234:     /**
 235:      * Charset the email header is sent in
 236:      * If null, the $charset property will be used as default
 237:      *
 238:      * @var string|null
 239:      */
 240:     public $headerCharset;
 241: 
 242:     /**
 243:      * The email transfer encoding used.
 244:      * If null, the $charset property is used for determined the transfer encoding.
 245:      *
 246:      * @var string|null
 247:      */
 248:     protected $transferEncoding;
 249: 
 250:     /**
 251:      * Available encoding to be set for transfer.
 252:      *
 253:      * @var array
 254:      */
 255:     protected $_transferEncodingAvailable = [
 256:         '7bit',
 257:         '8bit',
 258:         'base64',
 259:         'binary',
 260:         'quoted-printable'
 261:     ];
 262: 
 263:     /**
 264:      * The application wide charset, used to encode headers and body
 265:      *
 266:      * @var string|null
 267:      */
 268:     protected $_appCharset;
 269: 
 270:     /**
 271:      * List of files that should be attached to the email.
 272:      *
 273:      * Only absolute paths
 274:      *
 275:      * @var array
 276:      */
 277:     protected $_attachments = [];
 278: 
 279:     /**
 280:      * If set, boundary to use for multipart mime messages
 281:      *
 282:      * @var string|null
 283:      */
 284:     protected $_boundary;
 285: 
 286:     /**
 287:      * Contains the optional priority of the email.
 288:      *
 289:      * @var int|null
 290:      */
 291:     protected $_priority;
 292: 
 293:     /**
 294:      * An array mapping url schemes to fully qualified Transport class names.
 295:      * Unused.
 296:      *
 297:      * @var array
 298:      * @deprecated 3.7.0 This property is unused and will be removed in 4.0.0.
 299:      */
 300:     protected static $_dsnClassMap = [];
 301: 
 302:     /**
 303:      * A copy of the configuration profile for this
 304:      * instance. This copy can be modified with Email::profile().
 305:      *
 306:      * @var array
 307:      */
 308:     protected $_profile = [];
 309: 
 310:     /**
 311:      * 8Bit character sets
 312:      *
 313:      * @var array
 314:      */
 315:     protected $_charset8bit = ['UTF-8', 'SHIFT_JIS'];
 316: 
 317:     /**
 318:      * Define Content-Type charset name
 319:      *
 320:      * @var array
 321:      */
 322:     protected $_contentTypeCharset = [
 323:         'ISO-2022-JP-MS' => 'ISO-2022-JP'
 324:     ];
 325: 
 326:     /**
 327:      * Regex for email validation
 328:      *
 329:      * If null, filter_var() will be used. Use the emailPattern() method
 330:      * to set a custom pattern.'
 331:      *
 332:      * @var string
 333:      */
 334:     protected $_emailPattern = self::EMAIL_PATTERN;
 335: 
 336:     /**
 337:      * Constructor
 338:      *
 339:      * @param array|string|null $config Array of configs, or string to load configs from app.php
 340:      */
 341:     public function __construct($config = null)
 342:     {
 343:         $this->_appCharset = Configure::read('App.encoding');
 344:         if ($this->_appCharset !== null) {
 345:             $this->charset = $this->_appCharset;
 346:         }
 347:         $this->_domain = preg_replace('/\:\d+$/', '', env('HTTP_HOST'));
 348:         if (empty($this->_domain)) {
 349:             $this->_domain = php_uname('n');
 350:         }
 351: 
 352:         $this->viewBuilder()
 353:             ->setClassName('Cake\View\View')
 354:             ->setTemplate('')
 355:             ->setLayout('default')
 356:             ->setHelpers(['Html']);
 357: 
 358:         if ($config === null) {
 359:             $config = static::getConfig('default');
 360:         }
 361:         if ($config) {
 362:             $this->setProfile($config);
 363:         }
 364:         if (empty($this->headerCharset)) {
 365:             $this->headerCharset = $this->charset;
 366:         }
 367:     }
 368: 
 369:     /**
 370:      * Clone ViewBuilder instance when email object is cloned.
 371:      *
 372:      * @return void
 373:      */
 374:     public function __clone()
 375:     {
 376:         $this->_viewBuilder = clone $this->viewBuilder();
 377:     }
 378: 
 379:     /**
 380:      * Sets "from" address.
 381:      *
 382:      * @param string|array $email Null to get, String with email,
 383:      *   Array with email as key, name as value or email as value (without name)
 384:      * @param string|null $name Name
 385:      * @return $this
 386:      * @throws \InvalidArgumentException
 387:      */
 388:     public function setFrom($email, $name = null)
 389:     {
 390:         return $this->_setEmailSingle('_from', $email, $name, 'From requires only 1 email address.');
 391:     }
 392: 
 393:     /**
 394:      * Gets "from" address.
 395:      *
 396:      * @return array
 397:      */
 398:     public function getFrom()
 399:     {
 400:         return $this->_from;
 401:     }
 402: 
 403:     /**
 404:      * From
 405:      *
 406:      * @deprecated 3.4.0 Use setFrom()/getFrom() instead.
 407:      * @param string|array|null $email Null to get, String with email,
 408:      *   Array with email as key, name as value or email as value (without name)
 409:      * @param string|null $name Name
 410:      * @return array|$this
 411:      * @throws \InvalidArgumentException
 412:      */
 413:     public function from($email = null, $name = null)
 414:     {
 415:         deprecationWarning('Email::from() is deprecated. Use Email::setFrom() or Email::getFrom() instead.');
 416:         if ($email === null) {
 417:             return $this->getFrom();
 418:         }
 419: 
 420:         return $this->setFrom($email, $name);
 421:     }
 422: 
 423:     /**
 424:      * Sets "sender" address.
 425:      *
 426:      * @param string|array $email String with email,
 427:      *   Array with email as key, name as value or email as value (without name)
 428:      * @param string|null $name Name
 429:      * @return $this
 430:      * @throws \InvalidArgumentException
 431:      */
 432:     public function setSender($email, $name = null)
 433:     {
 434:         return $this->_setEmailSingle('_sender', $email, $name, 'Sender requires only 1 email address.');
 435:     }
 436: 
 437:     /**
 438:      * Gets "sender" address.
 439:      *
 440:      * @return array
 441:      */
 442:     public function getSender()
 443:     {
 444:         return $this->_sender;
 445:     }
 446: 
 447:     /**
 448:      * Sender
 449:      *
 450:      * @deprecated 3.4.0 Use setSender()/getSender() instead.
 451:      * @param string|array|null $email Null to get, String with email,
 452:      *   Array with email as key, name as value or email as value (without name)
 453:      * @param string|null $name Name
 454:      * @return array|$this
 455:      * @throws \InvalidArgumentException
 456:      */
 457:     public function sender($email = null, $name = null)
 458:     {
 459:         deprecationWarning('Email::sender() is deprecated. Use Email::setSender() or Email::getSender() instead.');
 460: 
 461:         if ($email === null) {
 462:             return $this->getSender();
 463:         }
 464: 
 465:         return $this->setSender($email, $name);
 466:     }
 467: 
 468:     /**
 469:      * Sets "Reply-To" address.
 470:      *
 471:      * @param string|array $email String with email,
 472:      *   Array with email as key, name as value or email as value (without name)
 473:      * @param string|null $name Name
 474:      * @return $this
 475:      * @throws \InvalidArgumentException
 476:      */
 477:     public function setReplyTo($email, $name = null)
 478:     {
 479:         return $this->_setEmailSingle('_replyTo', $email, $name, 'Reply-To requires only 1 email address.');
 480:     }
 481: 
 482:     /**
 483:      * Gets "Reply-To" address.
 484:      *
 485:      * @return array
 486:      */
 487:     public function getReplyTo()
 488:     {
 489:         return $this->_replyTo;
 490:     }
 491: 
 492:     /**
 493:      * Reply-To
 494:      *
 495:      * @deprecated 3.4.0 Use setReplyTo()/getReplyTo() instead.
 496:      * @param string|array|null $email Null to get, String with email,
 497:      *   Array with email as key, name as value or email as value (without name)
 498:      * @param string|null $name Name
 499:      * @return array|$this
 500:      * @throws \InvalidArgumentException
 501:      */
 502:     public function replyTo($email = null, $name = null)
 503:     {
 504:         deprecationWarning('Email::replyTo() is deprecated. Use Email::setReplyTo() or Email::getReplyTo() instead.');
 505: 
 506:         if ($email === null) {
 507:             return $this->getReplyTo();
 508:         }
 509: 
 510:         return $this->setReplyTo($email, $name);
 511:     }
 512: 
 513:     /**
 514:      * Sets Read Receipt (Disposition-Notification-To header).
 515:      *
 516:      * @param string|array $email String with email,
 517:      *   Array with email as key, name as value or email as value (without name)
 518:      * @param string|null $name Name
 519:      * @return $this
 520:      * @throws \InvalidArgumentException
 521:      */
 522:     public function setReadReceipt($email, $name = null)
 523:     {
 524:         return $this->_setEmailSingle('_readReceipt', $email, $name, 'Disposition-Notification-To requires only 1 email address.');
 525:     }
 526: 
 527:     /**
 528:      * Gets Read Receipt (Disposition-Notification-To header).
 529:      *
 530:      * @return array
 531:      */
 532:     public function getReadReceipt()
 533:     {
 534:         return $this->_readReceipt;
 535:     }
 536: 
 537:     /**
 538:      * Read Receipt (Disposition-Notification-To header)
 539:      *
 540:      * @deprecated 3.4.0 Use setReadReceipt()/getReadReceipt() instead.
 541:      * @param string|array|null $email Null to get, String with email,
 542:      *   Array with email as key, name as value or email as value (without name)
 543:      * @param string|null $name Name
 544:      * @return array|$this
 545:      * @throws \InvalidArgumentException
 546:      */
 547:     public function readReceipt($email = null, $name = null)
 548:     {
 549:         deprecationWarning('Email::readReceipt() is deprecated. Use Email::setReadReceipt() or Email::getReadReceipt() instead.');
 550: 
 551:         if ($email === null) {
 552:             return $this->getReadReceipt();
 553:         }
 554: 
 555:         return $this->setReadReceipt($email, $name);
 556:     }
 557: 
 558:     /**
 559:      * Return Path
 560:      *
 561:      * @param string|array $email String with email,
 562:      *   Array with email as key, name as value or email as value (without name)
 563:      * @param string|null $name Name
 564:      * @return $this
 565:      * @throws \InvalidArgumentException
 566:      */
 567:     public function setReturnPath($email, $name = null)
 568:     {
 569:         return $this->_setEmailSingle('_returnPath', $email, $name, 'Return-Path requires only 1 email address.');
 570:     }
 571: 
 572:     /**
 573:      * Gets return path.
 574:      *
 575:      * @return array
 576:      */
 577:     public function getReturnPath()
 578:     {
 579:         return $this->_returnPath;
 580:     }
 581: 
 582:     /**
 583:      * Return Path
 584:      *
 585:      * @deprecated 3.4.0 Use setReturnPath()/getReturnPath() instead.
 586:      * @param string|array|null $email Null to get, String with email,
 587:      *   Array with email as key, name as value or email as value (without name)
 588:      * @param string|null $name Name
 589:      * @return array|$this
 590:      * @throws \InvalidArgumentException
 591:      */
 592:     public function returnPath($email = null, $name = null)
 593:     {
 594:         deprecationWarning('Email::returnPath() is deprecated. Use Email::setReturnPath() or Email::getReturnPath() instead.');
 595:         if ($email === null) {
 596:             return $this->getReturnPath();
 597:         }
 598: 
 599:         return $this->setReturnPath($email, $name);
 600:     }
 601: 
 602:     /**
 603:      * Sets "to" address.
 604:      *
 605:      * @param string|array $email String with email,
 606:      *   Array with email as key, name as value or email as value (without name)
 607:      * @param string|null $name Name
 608:      * @return $this
 609:      */
 610:     public function setTo($email, $name = null)
 611:     {
 612:         return $this->_setEmail('_to', $email, $name);
 613:     }
 614: 
 615:     /**
 616:      * Gets "to" address
 617:      *
 618:      * @return array
 619:      */
 620:     public function getTo()
 621:     {
 622:         return $this->_to;
 623:     }
 624: 
 625:     /**
 626:      * To
 627:      *
 628:      * @deprecated 3.4.0 Use setTo()/getTo() instead.
 629:      * @param string|array|null $email Null to get, String with email,
 630:      *   Array with email as key, name as value or email as value (without name)
 631:      * @param string|null $name Name
 632:      * @return array|$this
 633:      */
 634:     public function to($email = null, $name = null)
 635:     {
 636:         deprecationWarning('Email::to() is deprecated. Use Email::setTo() or Email::getTo() instead.');
 637: 
 638:         if ($email === null) {
 639:             return $this->getTo();
 640:         }
 641: 
 642:         return $this->setTo($email, $name);
 643:     }
 644: 
 645:     /**
 646:      * Add To
 647:      *
 648:      * @param string|array $email Null to get, String with email,
 649:      *   Array with email as key, name as value or email as value (without name)
 650:      * @param string|null $name Name
 651:      * @return $this
 652:      */
 653:     public function addTo($email, $name = null)
 654:     {
 655:         return $this->_addEmail('_to', $email, $name);
 656:     }
 657: 
 658:     /**
 659:      * Sets "cc" address.
 660:      *
 661:      * @param string|array $email String with email,
 662:      *   Array with email as key, name as value or email as value (without name)
 663:      * @param string|null $name Name
 664:      * @return $this
 665:      */
 666:     public function setCc($email, $name = null)
 667:     {
 668:         return $this->_setEmail('_cc', $email, $name);
 669:     }
 670: 
 671:     /**
 672:      * Gets "cc" address.
 673:      *
 674:      * @return array
 675:      */
 676:     public function getCc()
 677:     {
 678:         return $this->_cc;
 679:     }
 680: 
 681:     /**
 682:      * Cc
 683:      *
 684:      * @deprecated 3.4.0 Use setCc()/getCc() instead.
 685:      * @param string|array|null $email Null to get, String with email,
 686:      *   Array with email as key, name as value or email as value (without name)
 687:      * @param string|null $name Name
 688:      * @return array|$this
 689:      */
 690:     public function cc($email = null, $name = null)
 691:     {
 692:         deprecationWarning('Email::cc() is deprecated. Use Email::setCc() or Email::getCc() instead.');
 693: 
 694:         if ($email === null) {
 695:             return $this->getCc();
 696:         }
 697: 
 698:         return $this->setCc($email, $name);
 699:     }
 700: 
 701:     /**
 702:      * Add Cc
 703:      *
 704:      * @param string|array $email Null to get, String with email,
 705:      *   Array with email as key, name as value or email as value (without name)
 706:      * @param string|null $name Name
 707:      * @return $this
 708:      */
 709:     public function addCc($email, $name = null)
 710:     {
 711:         return $this->_addEmail('_cc', $email, $name);
 712:     }
 713: 
 714:     /**
 715:      * Sets "bcc" address.
 716:      *
 717:      * @param string|array $email String with email,
 718:      *   Array with email as key, name as value or email as value (without name)
 719:      * @param string|null $name Name
 720:      * @return $this
 721:      */
 722:     public function setBcc($email, $name = null)
 723:     {
 724:         return $this->_setEmail('_bcc', $email, $name);
 725:     }
 726: 
 727:     /**
 728:      * Gets "bcc" address.
 729:      *
 730:      * @return array
 731:      */
 732:     public function getBcc()
 733:     {
 734:         return $this->_bcc;
 735:     }
 736: 
 737:     /**
 738:      * Bcc
 739:      *
 740:      * @deprecated 3.4.0 Use setBcc()/getBcc() instead.
 741:      * @param string|array|null $email Null to get, String with email,
 742:      *   Array with email as key, name as value or email as value (without name)
 743:      * @param string|null $name Name
 744:      * @return array|$this
 745:      */
 746:     public function bcc($email = null, $name = null)
 747:     {
 748:         deprecationWarning('Email::bcc() is deprecated. Use Email::setBcc() or Email::getBcc() instead.');
 749: 
 750:         if ($email === null) {
 751:             return $this->getBcc();
 752:         }
 753: 
 754:         return $this->setBcc($email, $name);
 755:     }
 756: 
 757:     /**
 758:      * Add Bcc
 759:      *
 760:      * @param string|array $email Null to get, String with email,
 761:      *   Array with email as key, name as value or email as value (without name)
 762:      * @param string|null $name Name
 763:      * @return $this
 764:      */
 765:     public function addBcc($email, $name = null)
 766:     {
 767:         return $this->_addEmail('_bcc', $email, $name);
 768:     }
 769: 
 770:     /**
 771:      * Charset setter.
 772:      *
 773:      * @param string|null $charset Character set.
 774:      * @return $this
 775:      */
 776:     public function setCharset($charset)
 777:     {
 778:         $this->charset = $charset;
 779:         if (!$this->headerCharset) {
 780:             $this->headerCharset = $charset;
 781:         }
 782: 
 783:         return $this;
 784:     }
 785: 
 786:     /**
 787:      * Charset getter.
 788:      *
 789:      * @return string Charset
 790:      */
 791:     public function getCharset()
 792:     {
 793:         return $this->charset;
 794:     }
 795: 
 796:     /**
 797:      * Charset setter/getter
 798:      *
 799:      * @deprecated 3.4.0 Use setCharset()/getCharset() instead.
 800:      * @param string|null $charset Character set.
 801:      * @return string Charset
 802:      */
 803:     public function charset($charset = null)
 804:     {
 805:         deprecationWarning('Email::charset() is deprecated. Use Email::setCharset() or Email::getCharset() instead.');
 806: 
 807:         if ($charset === null) {
 808:             return $this->getCharset();
 809:         }
 810:         $this->setCharset($charset);
 811: 
 812:         return $this->charset;
 813:     }
 814: 
 815:     /**
 816:      * HeaderCharset setter.
 817:      *
 818:      * @param string|null $charset Character set.
 819:      * @return $this
 820:      */
 821:     public function setHeaderCharset($charset)
 822:     {
 823:         $this->headerCharset = $charset;
 824: 
 825:         return $this;
 826:     }
 827: 
 828:     /**
 829:      * HeaderCharset getter.
 830:      *
 831:      * @return string Charset
 832:      */
 833:     public function getHeaderCharset()
 834:     {
 835:         return $this->headerCharset;
 836:     }
 837: 
 838:     /**
 839:      * HeaderCharset setter/getter
 840:      *
 841:      * @deprecated 3.4.0 Use setHeaderCharset()/getHeaderCharset() instead.
 842:      * @param string|null $charset Character set.
 843:      * @return string Charset
 844:      */
 845:     public function headerCharset($charset = null)
 846:     {
 847:         deprecationWarning('Email::headerCharset() is deprecated. Use Email::setHeaderCharset() or Email::getHeaderCharset() instead.');
 848: 
 849:         if ($charset === null) {
 850:             return $this->getHeaderCharset();
 851:         }
 852: 
 853:         $this->setHeaderCharset($charset);
 854: 
 855:         return $this->headerCharset;
 856:     }
 857: 
 858:     /**
 859:      * TransferEncoding setter.
 860:      *
 861:      * @param string|null $encoding Encoding set.
 862:      * @return $this
 863:      */
 864:     public function setTransferEncoding($encoding)
 865:     {
 866:         $encoding = strtolower($encoding);
 867:         if (!in_array($encoding, $this->_transferEncodingAvailable)) {
 868:             throw new InvalidArgumentException(
 869:                 sprintf(
 870:                     'Transfer encoding not available. Can be : %s.',
 871:                     implode(', ', $this->_transferEncodingAvailable)
 872:                 )
 873:             );
 874:         }
 875:         $this->transferEncoding = $encoding;
 876: 
 877:         return $this;
 878:     }
 879: 
 880:     /**
 881:      * TransferEncoding getter.
 882:      *
 883:      * @return string|null Encoding
 884:      */
 885:     public function getTransferEncoding()
 886:     {
 887:         return $this->transferEncoding;
 888:     }
 889: 
 890:     /**
 891:      * EmailPattern setter/getter
 892:      *
 893:      * @param string|null $regex The pattern to use for email address validation,
 894:      *   null to unset the pattern and make use of filter_var() instead.
 895:      * @return $this
 896:      */
 897:     public function setEmailPattern($regex)
 898:     {
 899:         $this->_emailPattern = $regex;
 900: 
 901:         return $this;
 902:     }
 903: 
 904:     /**
 905:      * EmailPattern setter/getter
 906:      *
 907:      * @return string
 908:      */
 909:     public function getEmailPattern()
 910:     {
 911:         return $this->_emailPattern;
 912:     }
 913: 
 914:     /**
 915:      * EmailPattern setter/getter
 916:      *
 917:      * @deprecated 3.4.0 Use setEmailPattern()/getEmailPattern() instead.
 918:      * @param string|bool|null $regex The pattern to use for email address validation,
 919:      *   null to unset the pattern and make use of filter_var() instead, false or
 920:      *   nothing to return the current value
 921:      * @return string|$this
 922:      */
 923:     public function emailPattern($regex = false)
 924:     {
 925:         deprecationWarning('Email::emailPattern() is deprecated. Use Email::setEmailPattern() or Email::getEmailPattern() instead.');
 926: 
 927:         if ($regex === false) {
 928:             return $this->getEmailPattern();
 929:         }
 930: 
 931:         return $this->setEmailPattern($regex);
 932:     }
 933: 
 934:     /**
 935:      * Set email
 936:      *
 937:      * @param string $varName Property name
 938:      * @param string|array $email String with email,
 939:      *   Array with email as key, name as value or email as value (without name)
 940:      * @param string $name Name
 941:      * @return $this
 942:      * @throws \InvalidArgumentException
 943:      */
 944:     protected function _setEmail($varName, $email, $name)
 945:     {
 946:         if (!is_array($email)) {
 947:             $this->_validateEmail($email, $varName);
 948:             if ($name === null) {
 949:                 $name = $email;
 950:             }
 951:             $this->{$varName} = [$email => $name];
 952: 
 953:             return $this;
 954:         }
 955:         $list = [];
 956:         foreach ($email as $key => $value) {
 957:             if (is_int($key)) {
 958:                 $key = $value;
 959:             }
 960:             $this->_validateEmail($key, $varName);
 961:             $list[$key] = $value;
 962:         }
 963:         $this->{$varName} = $list;
 964: 
 965:         return $this;
 966:     }
 967: 
 968:     /**
 969:      * Validate email address
 970:      *
 971:      * @param string $email Email address to validate
 972:      * @param string $context Which property was set
 973:      * @return void
 974:      * @throws \InvalidArgumentException If email address does not validate
 975:      */
 976:     protected function _validateEmail($email, $context)
 977:     {
 978:         if ($this->_emailPattern === null) {
 979:             if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
 980:                 return;
 981:             }
 982:         } elseif (preg_match($this->_emailPattern, $email)) {
 983:             return;
 984:         }
 985: 
 986:         $context = ltrim($context, '_');
 987:         if ($email == '') {
 988:             throw new InvalidArgumentException(sprintf('The email set for "%s" is empty.', $context));
 989:         }
 990:         throw new InvalidArgumentException(sprintf('Invalid email set for "%s". You passed "%s".', $context, $email));
 991:     }
 992: 
 993:     /**
 994:      * Set only 1 email
 995:      *
 996:      * @param string $varName Property name
 997:      * @param string|array $email String with email,
 998:      *   Array with email as key, name as value or email as value (without name)
 999:      * @param string $name Name
1000:      * @param string $throwMessage Exception message
1001:      * @return $this
1002:      * @throws \InvalidArgumentException
1003:      */
1004:     protected function _setEmailSingle($varName, $email, $name, $throwMessage)
1005:     {
1006:         if ($email === []) {
1007:             $this->{$varName} = $email;
1008: 
1009:             return $this;
1010:         }
1011: 
1012:         $current = $this->{$varName};
1013:         $this->_setEmail($varName, $email, $name);
1014:         if (count($this->{$varName}) !== 1) {
1015:             $this->{$varName} = $current;
1016:             throw new InvalidArgumentException($throwMessage);
1017:         }
1018: 
1019:         return $this;
1020:     }
1021: 
1022:     /**
1023:      * Add email
1024:      *
1025:      * @param string $varName Property name
1026:      * @param string|array $email String with email,
1027:      *   Array with email as key, name as value or email as value (without name)
1028:      * @param string $name Name
1029:      * @return $this
1030:      * @throws \InvalidArgumentException
1031:      */
1032:     protected function _addEmail($varName, $email, $name)
1033:     {
1034:         if (!is_array($email)) {
1035:             $this->_validateEmail($email, $varName);
1036:             if ($name === null) {
1037:                 $name = $email;
1038:             }
1039:             $this->{$varName}[$email] = $name;
1040: 
1041:             return $this;
1042:         }
1043:         $list = [];
1044:         foreach ($email as $key => $value) {
1045:             if (is_int($key)) {
1046:                 $key = $value;
1047:             }
1048:             $this->_validateEmail($key, $varName);
1049:             $list[$key] = $value;
1050:         }
1051:         $this->{$varName} = array_merge($this->{$varName}, $list);
1052: 
1053:         return $this;
1054:     }
1055: 
1056:     /**
1057:      * Sets subject.
1058:      *
1059:      * @param string $subject Subject string.
1060:      * @return $this
1061:      */
1062:     public function setSubject($subject)
1063:     {
1064:         $this->_subject = $this->_encode((string)$subject);
1065: 
1066:         return $this;
1067:     }
1068: 
1069:     /**
1070:      * Gets subject.
1071:      *
1072:      * @return string
1073:      */
1074:     public function getSubject()
1075:     {
1076:         return $this->_subject;
1077:     }
1078: 
1079:     /**
1080:      * Get/Set Subject.
1081:      *
1082:      * @deprecated 3.4.0 Use setSubject()/getSubject() instead.
1083:      * @param string|null $subject Subject string.
1084:      * @return string|$this
1085:      */
1086:     public function subject($subject = null)
1087:     {
1088:         deprecationWarning('Email::subject() is deprecated. Use Email::setSubject() or Email::getSubject() instead.');
1089: 
1090:         if ($subject === null) {
1091:             return $this->getSubject();
1092:         }
1093: 
1094:         return $this->setSubject($subject);
1095:     }
1096: 
1097:     /**
1098:      * Get original subject without encoding
1099:      *
1100:      * @return string Original subject
1101:      */
1102:     public function getOriginalSubject()
1103:     {
1104:         return $this->_decode($this->_subject);
1105:     }
1106: 
1107:     /**
1108:      * Sets headers for the message
1109:      *
1110:      * @param array $headers Associative array containing headers to be set.
1111:      * @return $this
1112:      */
1113:     public function setHeaders(array $headers)
1114:     {
1115:         $this->_headers = $headers;
1116: 
1117:         return $this;
1118:     }
1119: 
1120:     /**
1121:      * Add header for the message
1122:      *
1123:      * @param array $headers Headers to set.
1124:      * @return $this
1125:      */
1126:     public function addHeaders(array $headers)
1127:     {
1128:         $this->_headers = Hash::merge($this->_headers, $headers);
1129: 
1130:         return $this;
1131:     }
1132: 
1133:     /**
1134:      * Get list of headers
1135:      *
1136:      * ### Includes:
1137:      *
1138:      * - `from`
1139:      * - `replyTo`
1140:      * - `readReceipt`
1141:      * - `returnPath`
1142:      * - `to`
1143:      * - `cc`
1144:      * - `bcc`
1145:      * - `subject`
1146:      *
1147:      * @param array $include List of headers.
1148:      * @return array
1149:      */
1150:     public function getHeaders(array $include = [])
1151:     {
1152:         if ($include == array_values($include)) {
1153:             $include = array_fill_keys($include, true);
1154:         }
1155:         $defaults = array_fill_keys(
1156:             [
1157:                 'from', 'sender', 'replyTo', 'readReceipt', 'returnPath',
1158:                 'to', 'cc', 'bcc', 'subject'],
1159:             false
1160:         );
1161:         $include += $defaults;
1162: 
1163:         $headers = [];
1164:         $relation = [
1165:             'from' => 'From',
1166:             'replyTo' => 'Reply-To',
1167:             'readReceipt' => 'Disposition-Notification-To',
1168:             'returnPath' => 'Return-Path'
1169:         ];
1170:         foreach ($relation as $var => $header) {
1171:             if ($include[$var]) {
1172:                 $var = '_' . $var;
1173:                 $headers[$header] = current($this->_formatAddress($this->{$var}));
1174:             }
1175:         }
1176:         if ($include['sender']) {
1177:             if (key($this->_sender) === key($this->_from)) {
1178:                 $headers['Sender'] = '';
1179:             } else {
1180:                 $headers['Sender'] = current($this->_formatAddress($this->_sender));
1181:             }
1182:         }
1183: 
1184:         foreach (['to', 'cc', 'bcc'] as $var) {
1185:             if ($include[$var]) {
1186:                 $classVar = '_' . $var;
1187:                 $headers[ucfirst($var)] = implode(', ', $this->_formatAddress($this->{$classVar}));
1188:             }
1189:         }
1190: 
1191:         $headers += $this->_headers;
1192:         if (!isset($headers['Date'])) {
1193:             $headers['Date'] = date(DATE_RFC2822);
1194:         }
1195:         if ($this->_messageId !== false) {
1196:             if ($this->_messageId === true) {
1197:                 $this->_messageId = '<' . str_replace('-', '', Text::uuid()) . '@' . $this->_domain . '>';
1198:             }
1199: 
1200:             $headers['Message-ID'] = $this->_messageId;
1201:         }
1202: 
1203:         if ($this->_priority) {
1204:             $headers['X-Priority'] = $this->_priority;
1205:         }
1206: 
1207:         if ($include['subject']) {
1208:             $headers['Subject'] = $this->_subject;
1209:         }
1210: 
1211:         $headers['MIME-Version'] = '1.0';
1212:         if ($this->_attachments) {
1213:             $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"';
1214:         } elseif ($this->_emailFormat === 'both') {
1215:             $headers['Content-Type'] = 'multipart/alternative; boundary="' . $this->_boundary . '"';
1216:         } elseif ($this->_emailFormat === 'text') {
1217:             $headers['Content-Type'] = 'text/plain; charset=' . $this->_getContentTypeCharset();
1218:         } elseif ($this->_emailFormat === 'html') {
1219:             $headers['Content-Type'] = 'text/html; charset=' . $this->_getContentTypeCharset();
1220:         }
1221:         $headers['Content-Transfer-Encoding'] = $this->_getContentTransferEncoding();
1222: 
1223:         return $headers;
1224:     }
1225: 
1226:     /**
1227:      * Format addresses
1228:      *
1229:      * If the address contains non alphanumeric/whitespace characters, it will
1230:      * be quoted as characters like `:` and `,` are known to cause issues
1231:      * in address header fields.
1232:      *
1233:      * @param array $address Addresses to format.
1234:      * @return array
1235:      */
1236:     protected function _formatAddress($address)
1237:     {
1238:         $return = [];
1239:         foreach ($address as $email => $alias) {
1240:             if ($email === $alias) {
1241:                 $return[] = $email;
1242:             } else {
1243:                 $encoded = $this->_encode($alias);
1244:                 if ($encoded === $alias && preg_match('/[^a-z0-9 ]/i', $encoded)) {
1245:                     $encoded = '"' . str_replace('"', '\"', $encoded) . '"';
1246:                 }
1247:                 $return[] = sprintf('%s <%s>', $encoded, $email);
1248:             }
1249:         }
1250: 
1251:         return $return;
1252:     }
1253: 
1254:     /**
1255:      * Sets template.
1256:      *
1257:      * @param string|null $template Template name or null to not use.
1258:      * @return $this
1259:      * @deprecated 3.7.0 Use $email->viewBuilder()->setTemplate() instead.
1260:      */
1261:     public function setTemplate($template)
1262:     {
1263:         deprecationWarning(
1264:             'Email::setTemplate() is deprecated. Use $email->viewBuilder()->setTemplate() instead.'
1265:         );
1266: 
1267:         $this->viewBuilder()->setTemplate($template ?: '');
1268: 
1269:         return $this;
1270:     }
1271: 
1272:     /**
1273:      * Gets template.
1274:      *
1275:      * @return string
1276:      * @deprecated 3.7.0 Use $email->viewBuilder()->getTemplate() instead.
1277:      */
1278:     public function getTemplate()
1279:     {
1280:         deprecationWarning(
1281:             'Email::getTemplate() is deprecated. Use $email->viewBuilder()->getTemplate() instead.'
1282:         );
1283: 
1284:         return $this->viewBuilder()->getTemplate();
1285:     }
1286: 
1287:     /**
1288:      * Sets layout.
1289:      *
1290:      * @param string|null $layout Layout name or null to not use
1291:      * @return $this
1292:      * @deprecated 3.7.0 Use $email->viewBuilder()->setLayout() instead.
1293:      */
1294:     public function setLayout($layout)
1295:     {
1296:         deprecationWarning(
1297:             'Email::setLayout() is deprecated. Use $email->viewBuilder()->setLayout() instead.'
1298:         );
1299: 
1300:         $this->viewBuilder()->setLayout($layout ?: false);
1301: 
1302:         return $this;
1303:     }
1304: 
1305:     /**
1306:      * Gets layout.
1307:      *
1308:      * @deprecated 3.7.0 Use $email->viewBuilder()->getLayout() instead.
1309:      * @return string
1310:      */
1311:     public function getLayout()
1312:     {
1313:         deprecationWarning(
1314:             'Email::getLayout() is deprecated. Use $email->viewBuilder()->getLayout() instead.'
1315:         );
1316: 
1317:         return $this->viewBuilder()->getLayout();
1318:     }
1319: 
1320:     /**
1321:      * Template and layout
1322:      *
1323:      * @deprecated 3.4.0 Use setTemplate()/getTemplate() and setLayout()/getLayout() instead.
1324:      * @param bool|string $template Template name or null to not use
1325:      * @param bool|string $layout Layout name or null to not use
1326:      * @return array|$this
1327:      */
1328:     public function template($template = false, $layout = false)
1329:     {
1330:         deprecationWarning(
1331:             'Email::template() is deprecated. ' .
1332:             'Use $email->viewBuilder()->getTemplate()/setTemplate() ' .
1333:             'and $email->viewBuilder()->getLayout()/setLayout() instead.'
1334:         );
1335: 
1336:         if ($template === false) {
1337:             return [
1338:                 'template' => $this->viewBuilder()->getTemplate(),
1339:                 'layout' => $this->viewBuilder()->getLayout()
1340:             ];
1341:         }
1342:         $this->viewBuilder()->setTemplate($template);
1343:         if ($layout !== false) {
1344:             $this->viewBuilder()->setLayout($layout);
1345:         }
1346: 
1347:         return $this;
1348:     }
1349: 
1350:     /**
1351:      * Sets view class for render.
1352:      *
1353:      * @param string $viewClass View class name.
1354:      * @return $this
1355:      */
1356:     public function setViewRenderer($viewClass)
1357:     {
1358:         $this->viewBuilder()->setClassName($viewClass);
1359: 
1360:         return $this;
1361:     }
1362: 
1363:     /**
1364:      * Gets view class for render.
1365:      *
1366:      * @return string
1367:      */
1368:     public function getViewRenderer()
1369:     {
1370:         return $this->viewBuilder()->getClassName();
1371:     }
1372: 
1373:     /**
1374:      * View class for render
1375:      *
1376:      * @deprecated 3.4.0 Use setViewRenderer()/getViewRenderer() instead.
1377:      * @param string|null $viewClass View class name.
1378:      * @return string|$this
1379:      */
1380:     public function viewRender($viewClass = null)
1381:     {
1382:         deprecationWarning('Email::viewRender() is deprecated. Use Email::setViewRenderer() or Email::getViewRenderer() instead.');
1383: 
1384:         if ($viewClass === null) {
1385:             return $this->getViewRenderer();
1386:         }
1387:         $this->setViewRenderer($viewClass);
1388: 
1389:         return $this;
1390:     }
1391: 
1392:     /**
1393:      * Sets variables to be set on render.
1394:      *
1395:      * @param array $viewVars Variables to set for view.
1396:      * @return $this
1397:      */
1398:     public function setViewVars($viewVars)
1399:     {
1400:         $this->set((array)$viewVars);
1401: 
1402:         return $this;
1403:     }
1404: 
1405:     /**
1406:      * Gets variables to be set on render.
1407:      *
1408:      * @return array
1409:      */
1410:     public function getViewVars()
1411:     {
1412:         return $this->viewVars;
1413:     }
1414: 
1415:     /**
1416:      * Variables to be set on render
1417:      *
1418:      * @deprecated 3.4.0 Use setViewVars()/getViewVars() instead.
1419:      * @param array|null $viewVars Variables to set for view.
1420:      * @return array|$this
1421:      */
1422:     public function viewVars($viewVars = null)
1423:     {
1424:         deprecationWarning('Email::viewVars() is deprecated. Use Email::setViewVars() or Email::getViewVars() instead.');
1425: 
1426:         if ($viewVars === null) {
1427:             return $this->getViewVars();
1428:         }
1429: 
1430:         return $this->setViewVars($viewVars);
1431:     }
1432: 
1433:     /**
1434:      * Sets theme to use when rendering.
1435:      *
1436:      * @param string $theme Theme name.
1437:      * @return $this
1438:      * @deprecated 3.7.0 Use $email->viewBuilder()->setTheme() instead.
1439:      */
1440:     public function setTheme($theme)
1441:     {
1442:         deprecationWarning(
1443:             'Email::setTheme() is deprecated. Use $email->viewBuilder()->setTheme() instead.'
1444:         );
1445: 
1446:         $this->viewBuilder()->setTheme($theme);
1447: 
1448:         return $this;
1449:     }
1450: 
1451:     /**
1452:      * Gets theme to use when rendering.
1453:      *
1454:      * @return string
1455:      * @deprecated 3.7.0 Use $email->viewBuilder()->getTheme() instead.
1456:      */
1457:     public function getTheme()
1458:     {
1459:         deprecationWarning(
1460:             'Email::getTheme() is deprecated. Use $email->viewBuilder()->getTheme() instead.'
1461:         );
1462: 
1463:         return $this->viewBuilder()->getTheme();
1464:     }
1465: 
1466:     /**
1467:      * Theme to use when rendering
1468:      *
1469:      * @deprecated 3.4.0 Use setTheme()/getTheme() instead.
1470:      * @param string|null $theme Theme name.
1471:      * @return string|$this
1472:      */
1473:     public function theme($theme = null)
1474:     {
1475:         deprecationWarning(
1476:             'Email::theme() is deprecated. Use $email->viewBuilder()->getTheme()/setTheme() instead.'
1477:         );
1478: 
1479:         if ($theme === null) {
1480:             return $this->viewBuilder()->getTheme();
1481:         }
1482: 
1483:         $this->viewBuilder()->setTheme($theme);
1484: 
1485:         return $this;
1486:     }
1487: 
1488:     /**
1489:      * Sets helpers to be used when rendering.
1490:      *
1491:      * @param array $helpers Helpers list.
1492:      * @return $this
1493:      * @deprecated 3.7.0 Use $email->viewBuilder()->setHelpers() instead.
1494:      */
1495:     public function setHelpers(array $helpers)
1496:     {
1497:         deprecationWarning(
1498:             'Email::setHelpers() is deprecated. Use $email->viewBuilder()->setHelpers() instead.'
1499:         );
1500: 
1501:         $this->viewBuilder()->setHelpers($helpers, false);
1502: 
1503:         return $this;
1504:     }
1505: 
1506:     /**
1507:      * Gets helpers to be used when rendering.
1508:      *
1509:      * @return array
1510:      * @deprecated 3.7.0 Use $email->viewBuilder()->getHelpers() instead.
1511:      */
1512:     public function getHelpers()
1513:     {
1514:         deprecationWarning(
1515:             'Email::getHelpers() is deprecated. Use $email->viewBuilder()->getHelpers() instead.'
1516:         );
1517: 
1518:         return $this->viewBuilder()->getHelpers();
1519:     }
1520: 
1521:     /**
1522:      * Helpers to be used in render
1523:      *
1524:      * @deprecated 3.4.0 Use setHelpers()/getHelpers() instead.
1525:      * @param array|null $helpers Helpers list.
1526:      * @return array|$this
1527:      */
1528:     public function helpers($helpers = null)
1529:     {
1530:         deprecationWarning(
1531:             'Email::helpers() is deprecated. Use $email->viewBuilder()->getHelpers()/setHelpers() instead.'
1532:         );
1533: 
1534:         if ($helpers === null) {
1535:             return $this->viewBuilder()->getHelpers();
1536:         }
1537: 
1538:         $this->viewBuilder()->setHelpers((array)$helpers);
1539: 
1540:         return $this;
1541:     }
1542: 
1543:     /**
1544:      * Sets email format.
1545:      *
1546:      * @param string $format Formatting string.
1547:      * @return $this
1548:      * @throws \InvalidArgumentException
1549:      */
1550:     public function setEmailFormat($format)
1551:     {
1552:         if (!in_array($format, $this->_emailFormatAvailable)) {
1553:             throw new InvalidArgumentException('Format not available.');
1554:         }
1555:         $this->_emailFormat = $format;
1556: 
1557:         return $this;
1558:     }
1559: 
1560:     /**
1561:      * Gets email format.
1562:      *
1563:      * @return string
1564:      */
1565:     public function getEmailFormat()
1566:     {
1567:         return $this->_emailFormat;
1568:     }
1569: 
1570:     /**
1571:      * Email format
1572:      *
1573:      * @deprecated 3.4.0 Use setEmailFormat()/getEmailFormat() instead.
1574:      * @param string|null $format Formatting string.
1575:      * @return string|$this
1576:      * @throws \InvalidArgumentException
1577:      */
1578:     public function emailFormat($format = null)
1579:     {
1580:         deprecationWarning('Email::emailFormat() is deprecated. Use Email::setEmailFormat() or Email::getEmailFormat() instead.');
1581: 
1582:         if ($format === null) {
1583:             return $this->getEmailFormat();
1584:         }
1585: 
1586:         return $this->setEmailFormat($format);
1587:     }
1588: 
1589:     /**
1590:      * Sets the transport.
1591:      *
1592:      * When setting the transport you can either use the name
1593:      * of a configured transport or supply a constructed transport.
1594:      *
1595:      * @param string|\Cake\Mailer\AbstractTransport $name Either the name of a configured
1596:      *   transport, or a transport instance.
1597:      * @return $this
1598:      * @throws \LogicException When the chosen transport lacks a send method.
1599:      * @throws \InvalidArgumentException When $name is neither a string nor an object.
1600:      */
1601:     public function setTransport($name)
1602:     {
1603:         if (is_string($name)) {
1604:             $transport = TransportFactory::get($name);
1605:         } elseif (is_object($name)) {
1606:             $transport = $name;
1607:         } else {
1608:             throw new InvalidArgumentException(
1609:                 sprintf('The value passed for the "$name" argument must be either a string, or an object, %s given.', gettype($name))
1610:             );
1611:         }
1612:         if (!method_exists($transport, 'send')) {
1613:             throw new LogicException(sprintf('The "%s" do not have send method.', get_class($transport)));
1614:         }
1615: 
1616:         $this->_transport = $transport;
1617: 
1618:         return $this;
1619:     }
1620: 
1621:     /**
1622:      * Gets the transport.
1623:      *
1624:      * @return \Cake\Mailer\AbstractTransport
1625:      */
1626:     public function getTransport()
1627:     {
1628:         return $this->_transport;
1629:     }
1630: 
1631:     /**
1632:      * Get/set the transport.
1633:      *
1634:      * When setting the transport you can either use the name
1635:      * of a configured transport or supply a constructed transport.
1636:      *
1637:      * @deprecated 3.4.0 Use setTransport()/getTransport() instead.
1638:      * @param string|\Cake\Mailer\AbstractTransport|null $name Either the name of a configured
1639:      *   transport, or a transport instance.
1640:      * @return \Cake\Mailer\AbstractTransport|$this
1641:      * @throws \LogicException When the chosen transport lacks a send method.
1642:      * @throws \InvalidArgumentException When $name is neither a string nor an object.
1643:      */
1644:     public function transport($name = null)
1645:     {
1646:         deprecationWarning('Email::transport() is deprecated. Use Email::setTransport() or Email::getTransport() instead.');
1647: 
1648:         if ($name === null) {
1649:             return $this->getTransport();
1650:         }
1651: 
1652:         return $this->setTransport($name);
1653:     }
1654: 
1655:     /**
1656:      * Sets message ID.
1657:      *
1658:      * @param bool|string $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID.
1659:      * @return $this
1660:      * @throws \InvalidArgumentException
1661:      */
1662:     public function setMessageId($message)
1663:     {
1664:         if (is_bool($message)) {
1665:             $this->_messageId = $message;
1666:         } else {
1667:             if (!preg_match('/^\<.+@.+\>$/', $message)) {
1668:                 throw new InvalidArgumentException('Invalid format to Message-ID. The text should be something like "<uuid@server.com>"');
1669:             }
1670:             $this->_messageId = $message;
1671:         }
1672: 
1673:         return $this;
1674:     }
1675: 
1676:     /**
1677:      * Gets message ID.
1678:      *
1679:      * @return bool|string
1680:      */
1681:     public function getMessageId()
1682:     {
1683:         return $this->_messageId;
1684:     }
1685: 
1686:     /**
1687:      * Message-ID
1688:      *
1689:      * @deprecated 3.4.0 Use setMessageId()/getMessageId() instead.
1690:      * @param bool|string|null $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID
1691:      * @return bool|string|$this
1692:      * @throws \InvalidArgumentException
1693:      */
1694:     public function messageId($message = null)
1695:     {
1696:         deprecationWarning('Email::messageId() is deprecated. Use Email::setMessageId() or Email::getMessageId() instead.');
1697: 
1698:         if ($message === null) {
1699:             return $this->getMessageId();
1700:         }
1701: 
1702:         return $this->setMessageId($message);
1703:     }
1704: 
1705:     /**
1706:      * Sets domain.
1707:      *
1708:      * Domain as top level (the part after @).
1709:      *
1710:      * @param string $domain Manually set the domain for CLI mailing.
1711:      * @return $this
1712:      */
1713:     public function setDomain($domain)
1714:     {
1715:         $this->_domain = $domain;
1716: 
1717:         return $this;
1718:     }
1719: 
1720:     /**
1721:      * Gets domain.
1722:      *
1723:      * @return string
1724:      */
1725:     public function getDomain()
1726:     {
1727:         return $this->_domain;
1728:     }
1729: 
1730:     /**
1731:      * Domain as top level (the part after @)
1732:      *
1733:      * @deprecated 3.4.0 Use setDomain()/getDomain() instead.
1734:      * @param string|null $domain Manually set the domain for CLI mailing
1735:      * @return string|$this
1736:      */
1737:     public function domain($domain = null)
1738:     {
1739:         deprecationWarning('Email::domain() is deprecated. Use Email::setDomain() or Email::getDomain() instead.');
1740: 
1741:         if ($domain === null) {
1742:             return $this->getDomain();
1743:         }
1744: 
1745:         return $this->setDomain($domain);
1746:     }
1747: 
1748:     /**
1749:      * Add attachments to the email message
1750:      *
1751:      * Attachments can be defined in a few forms depending on how much control you need:
1752:      *
1753:      * Attach a single file:
1754:      *
1755:      * ```
1756:      * $email->setAttachments('path/to/file');
1757:      * ```
1758:      *
1759:      * Attach a file with a different filename:
1760:      *
1761:      * ```
1762:      * $email->setAttachments(['custom_name.txt' => 'path/to/file.txt']);
1763:      * ```
1764:      *
1765:      * Attach a file and specify additional properties:
1766:      *
1767:      * ```
1768:      * $email->setAttachments(['custom_name.png' => [
1769:      *      'file' => 'path/to/file',
1770:      *      'mimetype' => 'image/png',
1771:      *      'contentId' => 'abc123',
1772:      *      'contentDisposition' => false
1773:      *    ]
1774:      * ]);
1775:      * ```
1776:      *
1777:      * Attach a file from string and specify additional properties:
1778:      *
1779:      * ```
1780:      * $email->setAttachments(['custom_name.png' => [
1781:      *      'data' => file_get_contents('path/to/file'),
1782:      *      'mimetype' => 'image/png'
1783:      *    ]
1784:      * ]);
1785:      * ```
1786:      *
1787:      * The `contentId` key allows you to specify an inline attachment. In your email text, you
1788:      * can use `<img src="cid:abc123" />` to display the image inline.
1789:      *
1790:      * The `contentDisposition` key allows you to disable the `Content-Disposition` header, this can improve
1791:      * attachment compatibility with outlook email clients.
1792:      *
1793:      * @param string|array $attachments String with the filename or array with filenames
1794:      * @return $this
1795:      * @throws \InvalidArgumentException
1796:      */
1797:     public function setAttachments($attachments)
1798:     {
1799:         $attach = [];
1800:         foreach ((array)$attachments as $name => $fileInfo) {
1801:             if (!is_array($fileInfo)) {
1802:                 $fileInfo = ['file' => $fileInfo];
1803:             }
1804:             if (!isset($fileInfo['file'])) {
1805:                 if (!isset($fileInfo['data'])) {
1806:                     throw new InvalidArgumentException('No file or data specified.');
1807:                 }
1808:                 if (is_int($name)) {
1809:                     throw new InvalidArgumentException('No filename specified.');
1810:                 }
1811:                 $fileInfo['data'] = chunk_split(base64_encode($fileInfo['data']), 76, "\r\n");
1812:             } else {
1813:                 $fileName = $fileInfo['file'];
1814:                 $fileInfo['file'] = realpath($fileInfo['file']);
1815:                 if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) {
1816:                     throw new InvalidArgumentException(sprintf('File not found: "%s"', $fileName));
1817:                 }
1818:                 if (is_int($name)) {
1819:                     $name = basename($fileInfo['file']);
1820:                 }
1821:             }
1822:             if (!isset($fileInfo['mimetype']) && isset($fileInfo['file']) && function_exists('mime_content_type')) {
1823:                 $fileInfo['mimetype'] = mime_content_type($fileInfo['file']);
1824:             }
1825:             if (!isset($fileInfo['mimetype'])) {
1826:                 $fileInfo['mimetype'] = 'application/octet-stream';
1827:             }
1828:             $attach[$name] = $fileInfo;
1829:         }
1830:         $this->_attachments = $attach;
1831: 
1832:         return $this;
1833:     }
1834: 
1835:     /**
1836:      * Gets attachments to the email message.
1837:      *
1838:      * @return array Array of attachments.
1839:      */
1840:     public function getAttachments()
1841:     {
1842:         return $this->_attachments;
1843:     }
1844: 
1845:     /**
1846:      * Add attachments to the email message
1847:      *
1848:      * Attachments can be defined in a few forms depending on how much control you need:
1849:      *
1850:      * Attach a single file:
1851:      *
1852:      * ```
1853:      * $email->setAttachments('path/to/file');
1854:      * ```
1855:      *
1856:      * Attach a file with a different filename:
1857:      *
1858:      * ```
1859:      * $email->setAttachments(['custom_name.txt' => 'path/to/file.txt']);
1860:      * ```
1861:      *
1862:      * Attach a file and specify additional properties:
1863:      *
1864:      * ```
1865:      * $email->setAttachments(['custom_name.png' => [
1866:      *      'file' => 'path/to/file',
1867:      *      'mimetype' => 'image/png',
1868:      *      'contentId' => 'abc123',
1869:      *      'contentDisposition' => false
1870:      *    ]
1871:      * ]);
1872:      * ```
1873:      *
1874:      * Attach a file from string and specify additional properties:
1875:      *
1876:      * ```
1877:      * $email->setAttachments(['custom_name.png' => [
1878:      *      'data' => file_get_contents('path/to/file'),
1879:      *      'mimetype' => 'image/png'
1880:      *    ]
1881:      * ]);
1882:      * ```
1883:      *
1884:      * The `contentId` key allows you to specify an inline attachment. In your email text, you
1885:      * can use `<img src="cid:abc123" />` to display the image inline.
1886:      *
1887:      * The `contentDisposition` key allows you to disable the `Content-Disposition` header, this can improve
1888:      * attachment compatibility with outlook email clients.
1889:      *
1890:      * @deprecated 3.4.0 Use setAttachments()/getAttachments() instead.
1891:      * @param string|array|null $attachments String with the filename or array with filenames
1892:      * @return array|$this Either the array of attachments when getting or $this when setting.
1893:      * @throws \InvalidArgumentException
1894:      */
1895:     public function attachments($attachments = null)
1896:     {
1897:         deprecationWarning('Email::attachments() is deprecated. Use Email::setAttachments() or Email::getAttachments() instead.');
1898: 
1899:         if ($attachments === null) {
1900:             return $this->getAttachments();
1901:         }
1902: 
1903:         return $this->setAttachments($attachments);
1904:     }
1905: 
1906:     /**
1907:      * Add attachments
1908:      *
1909:      * @param string|array $attachments String with the filename or array with filenames
1910:      * @return $this
1911:      * @throws \InvalidArgumentException
1912:      * @see \Cake\Mailer\Email::attachments()
1913:      */
1914:     public function addAttachments($attachments)
1915:     {
1916:         $current = $this->_attachments;
1917:         $this->setAttachments($attachments);
1918:         $this->_attachments = array_merge($current, $this->_attachments);
1919: 
1920:         return $this;
1921:     }
1922: 
1923:     /**
1924:      * Get generated message (used by transport classes)
1925:      *
1926:      * @param string|null $type Use MESSAGE_* constants or null to return the full message as array
1927:      * @return string|array String if type is given, array if type is null
1928:      */
1929:     public function message($type = null)
1930:     {
1931:         switch ($type) {
1932:             case static::MESSAGE_HTML:
1933:                 return $this->_htmlMessage;
1934:             case static::MESSAGE_TEXT:
1935:                 return $this->_textMessage;
1936:         }
1937: 
1938:         return $this->_message;
1939:     }
1940: 
1941:     /**
1942:      * Sets priority.
1943:      *
1944:      * @param int|null $priority 1 (highest) to 5 (lowest)
1945:      * @return $this
1946:      */
1947:     public function setPriority($priority)
1948:     {
1949:         $this->_priority = $priority;
1950: 
1951:         return $this;
1952:     }
1953: 
1954:     /**
1955:      * Gets priority.
1956:      *
1957:      * @return int
1958:      */
1959:     public function getPriority()
1960:     {
1961:         return $this->_priority;
1962:     }
1963: 
1964:     /**
1965:      * Sets transport configuration.
1966:      *
1967:      * Use this method to define transports to use in delivery profiles.
1968:      * Once defined you cannot edit the configurations, and must use
1969:      * Email::dropTransport() to flush the configuration first.
1970:      *
1971:      * When using an array of configuration data a new transport
1972:      * will be constructed for each message sent. When using a Closure, the
1973:      * closure will be evaluated for each message.
1974:      *
1975:      * The `className` is used to define the class to use for a transport.
1976:      * It can either be a short name, or a fully qualified class name
1977:      *
1978:      * @param string|array $key The configuration name to write. Or
1979:      *   an array of multiple transports to set.
1980:      * @param array|\Cake\Mailer\AbstractTransport|null $config Either an array of configuration
1981:      *   data, or a transport instance. Null when using key as array.
1982:      * @return void
1983:      * @deprecated 3.7.0 Use TransportFactory::setConfig() instead.
1984:      */
1985:     public static function setConfigTransport($key, $config = null)
1986:     {
1987:         deprecationWarning('Email::setConfigTransport() is deprecated. Use TransportFactory::setConfig() instead.');
1988: 
1989:         TransportFactory::setConfig($key, $config);
1990:     }
1991: 
1992:     /**
1993:      * Gets current transport configuration.
1994:      *
1995:      * @param string $key The configuration name to read.
1996:      * @return array|null Transport config.
1997:      * @deprecated 3.7.0 Use TransportFactory::getConfig() instead.
1998:      */
1999:     public static function getConfigTransport($key)
2000:     {
2001:         deprecationWarning('Email::getConfigTransport() is deprecated. Use TransportFactory::getConfig() instead.');
2002: 
2003:         return TransportFactory::getConfig($key);
2004:     }
2005: 
2006:     /**
2007:      * Add or read transport configuration.
2008:      *
2009:      * Use this method to define transports to use in delivery profiles.
2010:      * Once defined you cannot edit the configurations, and must use
2011:      * Email::dropTransport() to flush the configuration first.
2012:      *
2013:      * When using an array of configuration data a new transport
2014:      * will be constructed for each message sent. When using a Closure, the
2015:      * closure will be evaluated for each message.
2016:      *
2017:      * The `className` is used to define the class to use for a transport.
2018:      * It can either be a short name, or a fully qualified classname
2019:      *
2020:      * @deprecated 3.4.0 Use TransportFactory::setConfig()/getConfig() instead.
2021:      * @param string|array $key The configuration name to read/write. Or
2022:      *   an array of multiple transports to set.
2023:      * @param array|\Cake\Mailer\AbstractTransport|null $config Either an array of configuration
2024:      *   data, or a transport instance.
2025:      * @return array|null Either null when setting or an array of data when reading.
2026:      * @throws \BadMethodCallException When modifying an existing configuration.
2027:      */
2028:     public static function configTransport($key, $config = null)
2029:     {
2030:         deprecationWarning('Email::configTransport() is deprecated. Use TransportFactory::setConfig() or TransportFactory::getConfig() instead.');
2031: 
2032:         if ($config === null && is_string($key)) {
2033:             return TransportFactory::getConfig($key);
2034:         }
2035:         if ($config === null && is_array($key)) {
2036:             TransportFactory::setConfig($key);
2037: 
2038:             return null;
2039:         }
2040: 
2041:         TransportFactory::setConfig($key, $config);
2042:     }
2043: 
2044:     /**
2045:      * Returns an array containing the named transport configurations
2046:      *
2047:      * @return array Array of configurations.
2048:      * @deprecated 3.7.0 Use TransportFactory::configured() instead.
2049:      */
2050:     public static function configuredTransport()
2051:     {
2052:         deprecationWarning('Email::configuredTransport() is deprecated. Use TransportFactory::configured().');
2053: 
2054:         return TransportFactory::configured();
2055:     }
2056: 
2057:     /**
2058:      * Delete transport configuration.
2059:      *
2060:      * @param string $key The transport name to remove.
2061:      * @return void
2062:      * @deprecated 3.7.0 Use TransportFactory::drop() instead.
2063:      */
2064:     public static function dropTransport($key)
2065:     {
2066:         deprecationWarning('Email::dropTransport() is deprecated. Use TransportFactory::drop().');
2067: 
2068:         TransportFactory::drop($key);
2069:     }
2070: 
2071:     /**
2072:      * Sets the configuration profile to use for this instance.
2073:      *
2074:      * @param string|array $config String with configuration name, or
2075:      *    an array with config.
2076:      * @return $this
2077:      */
2078:     public function setProfile($config)
2079:     {
2080:         if (!is_array($config)) {
2081:             $config = (string)$config;
2082:         }
2083:         $this->_applyConfig($config);
2084: 
2085:         return $this;
2086:     }
2087: 
2088:     /**
2089:      * Gets the configuration profile to use for this instance.
2090:      *
2091:      * @return string|array
2092:      */
2093:     public function getProfile()
2094:     {
2095:         return $this->_profile;
2096:     }
2097: 
2098:     /**
2099:      * Get/Set the configuration profile to use for this instance.
2100:      *
2101:      * @deprecated 3.4.0 Use setProfile()/getProfile() instead.
2102:      * @param array|string|null $config String with configuration name, or
2103:      *    an array with config or null to return current config.
2104:      * @return string|array|$this
2105:      */
2106:     public function profile($config = null)
2107:     {
2108:         deprecationWarning('Email::profile() is deprecated. Use Email::setProfile() or Email::getProfile() instead.');
2109: 
2110:         if ($config === null) {
2111:             return $this->getProfile();
2112:         }
2113: 
2114:         return $this->setProfile($config);
2115:     }
2116: 
2117:     /**
2118:      * Send an email using the specified content, template and layout
2119:      *
2120:      * @param string|array|null $content String with message or array with messages
2121:      * @return array
2122:      * @throws \BadMethodCallException
2123:      */
2124:     public function send($content = null)
2125:     {
2126:         if (empty($this->_from)) {
2127:             throw new BadMethodCallException('From is not specified.');
2128:         }
2129:         if (empty($this->_to) && empty($this->_cc) && empty($this->_bcc)) {
2130:             throw new BadMethodCallException('You need specify one destination on to, cc or bcc.');
2131:         }
2132: 
2133:         if (is_array($content)) {
2134:             $content = implode("\n", $content) . "\n";
2135:         }
2136: 
2137:         $this->_message = $this->_render($this->_wrap($content));
2138: 
2139:         $transport = $this->getTransport();
2140:         if (!$transport) {
2141:             $msg = 'Cannot send email, transport was not defined. Did you call transport() or define ' .
2142:                 ' a transport in the set profile?';
2143:             throw new BadMethodCallException($msg);
2144:         }
2145:         $contents = $transport->send($this);
2146:         $this->_logDelivery($contents);
2147: 
2148:         return $contents;
2149:     }
2150: 
2151:     /**
2152:      * Log the email message delivery.
2153:      *
2154:      * @param array $contents The content with 'headers' and 'message' keys.
2155:      * @return void
2156:      */
2157:     protected function _logDelivery($contents)
2158:     {
2159:         if (empty($this->_profile['log'])) {
2160:             return;
2161:         }
2162:         $config = [
2163:             'level' => 'debug',
2164:             'scope' => 'email'
2165:         ];
2166:         if ($this->_profile['log'] !== true) {
2167:             if (!is_array($this->_profile['log'])) {
2168:                 $this->_profile['log'] = ['level' => $this->_profile['log']];
2169:             }
2170:             $config = $this->_profile['log'] + $config;
2171:         }
2172:         Log::write(
2173:             $config['level'],
2174:             PHP_EOL . $this->flatten($contents['headers']) . PHP_EOL . PHP_EOL . $this->flatten($contents['message']),
2175:             $config['scope']
2176:         );
2177:     }
2178: 
2179:     /**
2180:      * Converts given value to string
2181:      *
2182:      * @param string|array $value The value to convert
2183:      * @return string
2184:      */
2185:     protected function flatten($value)
2186:     {
2187:         return is_array($value) ? implode(';', $value) : (string)$value;
2188:     }
2189: 
2190:     /**
2191:      * Static method to fast create an instance of \Cake\Mailer\Email
2192:      *
2193:      * @param string|array|null $to Address to send (see Cake\Mailer\Email::to()). If null, will try to use 'to' from transport config
2194:      * @param string|null $subject String of subject or null to use 'subject' from transport config
2195:      * @param string|array|null $message String with message or array with variables to be used in render
2196:      * @param string|array $config String to use Email delivery profile from app.php or array with configs
2197:      * @param bool $send Send the email or just return the instance pre-configured
2198:      * @return static Instance of Cake\Mailer\Email
2199:      * @throws \InvalidArgumentException
2200:      */
2201:     public static function deliver($to = null, $subject = null, $message = null, $config = 'default', $send = true)
2202:     {
2203:         $class = __CLASS__;
2204: 
2205:         if (is_array($config) && !isset($config['transport'])) {
2206:             $config['transport'] = 'default';
2207:         }
2208:         /* @var \Cake\Mailer\Email $instance */
2209:         $instance = new $class($config);
2210:         if ($to !== null) {
2211:             $instance->setTo($to);
2212:         }
2213:         if ($subject !== null) {
2214:             $instance->setSubject($subject);
2215:         }
2216:         if (is_array($message)) {
2217:             $instance->setViewVars($message);
2218:             $message = null;
2219:         } elseif ($message === null && array_key_exists('message', $config = $instance->getProfile())) {
2220:             $message = $config['message'];
2221:         }
2222: 
2223:         if ($send === true) {
2224:             $instance->send($message);
2225:         }
2226: 
2227:         return $instance;
2228:     }
2229: 
2230:     /**
2231:      * Apply the config to an instance
2232:      *
2233:      * @param string|array $config Configuration options.
2234:      * @return void
2235:      * @throws \InvalidArgumentException When using a configuration that doesn't exist.
2236:      */
2237:     protected function _applyConfig($config)
2238:     {
2239:         if (is_string($config)) {
2240:             $name = $config;
2241:             $config = static::getConfig($name);
2242:             if (empty($config)) {
2243:                 throw new InvalidArgumentException(sprintf('Unknown email configuration "%s".', $name));
2244:             }
2245:             unset($name);
2246:         }
2247: 
2248:         $this->_profile = array_merge($this->_profile, $config);
2249: 
2250:         $simpleMethods = [
2251:             'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath',
2252:             'cc', 'bcc', 'messageId', 'domain', 'subject', 'attachments',
2253:             'transport', 'emailFormat', 'emailPattern', 'charset', 'headerCharset'
2254:         ];
2255:         foreach ($simpleMethods as $method) {
2256:             if (isset($config[$method])) {
2257:                 $this->{'set' . ucfirst($method)}($config[$method]);
2258:             }
2259:         }
2260: 
2261:         if (empty($this->headerCharset)) {
2262:             $this->headerCharset = $this->charset;
2263:         }
2264:         if (isset($config['headers'])) {
2265:             $this->setHeaders($config['headers']);
2266:         }
2267: 
2268:         $viewBuilderMethods = [
2269:             'template', 'layout', 'theme'
2270:         ];
2271:         foreach ($viewBuilderMethods as $method) {
2272:             if (array_key_exists($method, $config)) {
2273:                 $this->viewBuilder()->{'set' . ucfirst($method)}($config[$method]);
2274:             }
2275:         }
2276: 
2277:         if (array_key_exists('helpers', $config)) {
2278:             $this->viewBuilder()->setHelpers($config['helpers'], false);
2279:         }
2280:         if (array_key_exists('viewRender', $config)) {
2281:             $this->viewBuilder()->setClassName($config['viewRender']);
2282:         }
2283:         if (array_key_exists('viewVars', $config)) {
2284:             $this->set($config['viewVars']);
2285:         }
2286:     }
2287: 
2288:     /**
2289:      * Reset all the internal variables to be able to send out a new email.
2290:      *
2291:      * @return $this
2292:      */
2293:     public function reset()
2294:     {
2295:         $this->_to = [];
2296:         $this->_from = [];
2297:         $this->_sender = [];
2298:         $this->_replyTo = [];
2299:         $this->_readReceipt = [];
2300:         $this->_returnPath = [];
2301:         $this->_cc = [];
2302:         $this->_bcc = [];
2303:         $this->_messageId = true;
2304:         $this->_subject = '';
2305:         $this->_headers = [];
2306:         $this->_textMessage = '';
2307:         $this->_htmlMessage = '';
2308:         $this->_message = [];
2309:         $this->_emailFormat = 'text';
2310:         $this->_transport = null;
2311:         $this->_priority = null;
2312:         $this->charset = 'utf-8';
2313:         $this->headerCharset = null;
2314:         $this->transferEncoding = null;
2315:         $this->_attachments = [];
2316:         $this->_profile = [];
2317:         $this->_emailPattern = self::EMAIL_PATTERN;
2318: 
2319:         $this->viewBuilder()->setLayout('default');
2320:         $this->viewBuilder()->setTemplate('');
2321:         $this->viewBuilder()->setClassName('Cake\View\View');
2322:         $this->viewVars = [];
2323:         $this->viewBuilder()->setTheme(false);
2324:         $this->viewBuilder()->setHelpers(['Html'], false);
2325: 
2326:         return $this;
2327:     }
2328: 
2329:     /**
2330:      * Encode the specified string using the current charset
2331:      *
2332:      * @param string $text String to encode
2333:      * @return string Encoded string
2334:      */
2335:     protected function _encode($text)
2336:     {
2337:         $restore = mb_internal_encoding();
2338:         mb_internal_encoding($this->_appCharset);
2339:         if (empty($this->headerCharset)) {
2340:             $this->headerCharset = $this->charset;
2341:         }
2342:         $return = mb_encode_mimeheader($text, $this->headerCharset, 'B');
2343:         mb_internal_encoding($restore);
2344: 
2345:         return $return;
2346:     }
2347: 
2348:     /**
2349:      * Decode the specified string
2350:      *
2351:      * @param string $text String to decode
2352:      * @return string Decoded string
2353:      */
2354:     protected function _decode($text)
2355:     {
2356:         $restore = mb_internal_encoding();
2357:         mb_internal_encoding($this->_appCharset);
2358:         $return = mb_decode_mimeheader($text);
2359:         mb_internal_encoding($restore);
2360: 
2361:         return $return;
2362:     }
2363: 
2364:     /**
2365:      * Translates a string for one charset to another if the App.encoding value
2366:      * differs and the mb_convert_encoding function exists
2367:      *
2368:      * @param string $text The text to be converted
2369:      * @param string $charset the target encoding
2370:      * @return string
2371:      */
2372:     protected function _encodeString($text, $charset)
2373:     {
2374:         if ($this->_appCharset === $charset) {
2375:             return $text;
2376:         }
2377: 
2378:         return mb_convert_encoding($text, $charset, $this->_appCharset);
2379:     }
2380: 
2381:     /**
2382:      * Wrap the message to follow the RFC 2822 - 2.1.1
2383:      *
2384:      * @param string $message Message to wrap
2385:      * @param int $wrapLength The line length
2386:      * @return array Wrapped message
2387:      */
2388:     protected function _wrap($message, $wrapLength = Email::LINE_LENGTH_MUST)
2389:     {
2390:         if (strlen($message) === 0) {
2391:             return [''];
2392:         }
2393:         $message = str_replace(["\r\n", "\r"], "\n", $message);
2394:         $lines = explode("\n", $message);
2395:         $formatted = [];
2396:         $cut = ($wrapLength == Email::LINE_LENGTH_MUST);
2397: 
2398:         foreach ($lines as $line) {
2399:             if (empty($line) && $line !== '0') {
2400:                 $formatted[] = '';
2401:                 continue;
2402:             }
2403:             if (strlen($line) < $wrapLength) {
2404:                 $formatted[] = $line;
2405:                 continue;
2406:             }
2407:             if (!preg_match('/<[a-z]+.*>/i', $line)) {
2408:                 $formatted = array_merge(
2409:                     $formatted,
2410:                     explode("\n", Text::wordWrap($line, $wrapLength, "\n", $cut))
2411:                 );
2412:                 continue;
2413:             }
2414: 
2415:             $tagOpen = false;
2416:             $tmpLine = $tag = '';
2417:             $tmpLineLength = 0;
2418:             for ($i = 0, $count = strlen($line); $i < $count; $i++) {
2419:                 $char = $line[$i];
2420:                 if ($tagOpen) {
2421:                     $tag .= $char;
2422:                     if ($char === '>') {
2423:                         $tagLength = strlen($tag);
2424:                         if ($tagLength + $tmpLineLength < $wrapLength) {
2425:                             $tmpLine .= $tag;
2426:                             $tmpLineLength += $tagLength;
2427:                         } else {
2428:                             if ($tmpLineLength > 0) {
2429:                                 $formatted = array_merge(
2430:                                     $formatted,
2431:                                     explode("\n", Text::wordWrap(trim($tmpLine), $wrapLength, "\n", $cut))
2432:                                 );
2433:                                 $tmpLine = '';
2434:                                 $tmpLineLength = 0;
2435:                             }
2436:                             if ($tagLength > $wrapLength) {
2437:                                 $formatted[] = $tag;
2438:                             } else {
2439:                                 $tmpLine = $tag;
2440:                                 $tmpLineLength = $tagLength;
2441:                             }
2442:                         }
2443:                         $tag = '';
2444:                         $tagOpen = false;
2445:                     }
2446:                     continue;
2447:                 }
2448:                 if ($char === '<') {
2449:                     $tagOpen = true;
2450:                     $tag = '<';
2451:                     continue;
2452:                 }
2453:                 if ($char === ' ' && $tmpLineLength >= $wrapLength) {
2454:                     $formatted[] = $tmpLine;
2455:                     $tmpLineLength = 0;
2456:                     continue;
2457:                 }
2458:                 $tmpLine .= $char;
2459:                 $tmpLineLength++;
2460:                 if ($tmpLineLength === $wrapLength) {
2461:                     $nextChar = $line[$i + 1];
2462:                     if ($nextChar === ' ' || $nextChar === '<') {
2463:                         $formatted[] = trim($tmpLine);
2464:                         $tmpLine = '';
2465:                         $tmpLineLength = 0;
2466:                         if ($nextChar === ' ') {
2467:                             $i++;
2468:                         }
2469:                     } else {
2470:                         $lastSpace = strrpos($tmpLine, ' ');
2471:                         if ($lastSpace === false) {
2472:                             continue;
2473:                         }
2474:                         $formatted[] = trim(substr($tmpLine, 0, $lastSpace));
2475:                         $tmpLine = substr($tmpLine, $lastSpace + 1);
2476: 
2477:                         $tmpLineLength = strlen($tmpLine);
2478:                     }
2479:                 }
2480:             }
2481:             if (!empty($tmpLine)) {
2482:                 $formatted[] = $tmpLine;
2483:             }
2484:         }
2485:         $formatted[] = '';
2486: 
2487:         return $formatted;
2488:     }
2489: 
2490:     /**
2491:      * Create unique boundary identifier
2492:      *
2493:      * @return void
2494:      */
2495:     protected function _createBoundary()
2496:     {
2497:         if ($this->_attachments || $this->_emailFormat === 'both') {
2498:             $this->_boundary = md5(Security::randomBytes(16));
2499:         }
2500:     }
2501: 
2502:     /**
2503:      * Attach non-embedded files by adding file contents inside boundaries.
2504:      *
2505:      * @param string|null $boundary Boundary to use. If null, will default to $this->_boundary
2506:      * @return array An array of lines to add to the message
2507:      */
2508:     protected function _attachFiles($boundary = null)
2509:     {
2510:         if ($boundary === null) {
2511:             $boundary = $this->_boundary;
2512:         }
2513: 
2514:         $msg = [];
2515:         foreach ($this->_attachments as $filename => $fileInfo) {
2516:             if (!empty($fileInfo['contentId'])) {
2517:                 continue;
2518:             }
2519:             $data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
2520:             $hasDisposition = (
2521:                 !isset($fileInfo['contentDisposition']) ||
2522:                 $fileInfo['contentDisposition']
2523:             );
2524:             $part = new FormDataPart(false, $data, false);
2525: 
2526:             if ($hasDisposition) {
2527:                 $part->disposition('attachment');
2528:                 $part->filename($filename);
2529:             }
2530:             $part->transferEncoding('base64');
2531:             $part->type($fileInfo['mimetype']);
2532: 
2533:             $msg[] = '--' . $boundary;
2534:             $msg[] = (string)$part;
2535:             $msg[] = '';
2536:         }
2537: 
2538:         return $msg;
2539:     }
2540: 
2541:     /**
2542:      * Read the file contents and return a base64 version of the file contents.
2543:      *
2544:      * @param string $path The absolute path to the file to read.
2545:      * @return string File contents in base64 encoding
2546:      */
2547:     protected function _readFile($path)
2548:     {
2549:         $File = new File($path);
2550: 
2551:         return chunk_split(base64_encode($File->read()));
2552:     }
2553: 
2554:     /**
2555:      * Attach inline/embedded files to the message.
2556:      *
2557:      * @param string|null $boundary Boundary to use. If null, will default to $this->_boundary
2558:      * @return array An array of lines to add to the message
2559:      */
2560:     protected function _attachInlineFiles($boundary = null)
2561:     {
2562:         if ($boundary === null) {
2563:             $boundary = $this->_boundary;
2564:         }
2565: 
2566:         $msg = [];
2567:         foreach ($this->_attachments as $filename => $fileInfo) {
2568:             if (empty($fileInfo['contentId'])) {
2569:                 continue;
2570:             }
2571:             $data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
2572: 
2573:             $msg[] = '--' . $boundary;
2574:             $part = new FormDataPart(false, $data, 'inline');
2575:             $part->type($fileInfo['mimetype']);
2576:             $part->transferEncoding('base64');
2577:             $part->contentId($fileInfo['contentId']);
2578:             $part->filename($filename);
2579:             $msg[] = (string)$part;
2580:             $msg[] = '';
2581:         }
2582: 
2583:         return $msg;
2584:     }
2585: 
2586:     /**
2587:      * Render the body of the email.
2588:      *
2589:      * @param array $content Content to render
2590:      * @return array Email body ready to be sent
2591:      */
2592:     protected function _render($content)
2593:     {
2594:         $this->_textMessage = $this->_htmlMessage = '';
2595: 
2596:         $content = implode("\n", $content);
2597:         $rendered = $this->_renderTemplates($content);
2598: 
2599:         $this->_createBoundary();
2600:         $msg = [];
2601: 
2602:         $contentIds = array_filter((array)Hash::extract($this->_attachments, '{s}.contentId'));
2603:         $hasInlineAttachments = count($contentIds) > 0;
2604:         $hasAttachments = !empty($this->_attachments);
2605:         $hasMultipleTypes = count($rendered) > 1;
2606:         $multiPart = ($hasAttachments || $hasMultipleTypes);
2607: 
2608:         $boundary = $relBoundary = $textBoundary = $this->_boundary;
2609: 
2610:         if ($hasInlineAttachments) {
2611:             $msg[] = '--' . $boundary;
2612:             $msg[] = 'Content-Type: multipart/related; boundary="rel-' . $boundary . '"';
2613:             $msg[] = '';
2614:             $relBoundary = $textBoundary = 'rel-' . $boundary;
2615:         }
2616: 
2617:         if ($hasMultipleTypes && $hasAttachments) {
2618:             $msg[] = '--' . $relBoundary;
2619:             $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"';
2620:             $msg[] = '';
2621:             $textBoundary = 'alt-' . $boundary;
2622:         }
2623: 
2624:         if (isset($rendered['text'])) {
2625:             if ($multiPart) {
2626:                 $msg[] = '--' . $textBoundary;
2627:                 $msg[] = 'Content-Type: text/plain; charset=' . $this->_getContentTypeCharset();
2628:                 $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
2629:                 $msg[] = '';
2630:             }
2631:             $this->_textMessage = $rendered['text'];
2632:             $content = explode("\n", $this->_textMessage);
2633:             $msg = array_merge($msg, $content);
2634:             $msg[] = '';
2635:         }
2636: 
2637:         if (isset($rendered['html'])) {
2638:             if ($multiPart) {
2639:                 $msg[] = '--' . $textBoundary;
2640:                 $msg[] = 'Content-Type: text/html; charset=' . $this->_getContentTypeCharset();
2641:                 $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
2642:                 $msg[] = '';
2643:             }
2644:             $this->_htmlMessage = $rendered['html'];
2645:             $content = explode("\n", $this->_htmlMessage);
2646:             $msg = array_merge($msg, $content);
2647:             $msg[] = '';
2648:         }
2649: 
2650:         if ($textBoundary !== $relBoundary) {
2651:             $msg[] = '--' . $textBoundary . '--';
2652:             $msg[] = '';
2653:         }
2654: 
2655:         if ($hasInlineAttachments) {
2656:             $attachments = $this->_attachInlineFiles($relBoundary);
2657:             $msg = array_merge($msg, $attachments);
2658:             $msg[] = '';
2659:             $msg[] = '--' . $relBoundary . '--';
2660:             $msg[] = '';
2661:         }
2662: 
2663:         if ($hasAttachments) {
2664:             $attachments = $this->_attachFiles($boundary);
2665:             $msg = array_merge($msg, $attachments);
2666:         }
2667:         if ($hasAttachments || $hasMultipleTypes) {
2668:             $msg[] = '';
2669:             $msg[] = '--' . $boundary . '--';
2670:             $msg[] = '';
2671:         }
2672: 
2673:         return $msg;
2674:     }
2675: 
2676:     /**
2677:      * Gets the text body types that are in this email message
2678:      *
2679:      * @return array Array of types. Valid types are 'text' and 'html'
2680:      */
2681:     protected function _getTypes()
2682:     {
2683:         $types = [$this->_emailFormat];
2684:         if ($this->_emailFormat === 'both') {
2685:             $types = ['html', 'text'];
2686:         }
2687: 
2688:         return $types;
2689:     }
2690: 
2691:     /**
2692:      * Build and set all the view properties needed to render the templated emails.
2693:      * If there is no template set, the $content will be returned in a hash
2694:      * of the text content types for the email.
2695:      *
2696:      * @param string $content The content passed in from send() in most cases.
2697:      * @return array The rendered content with html and text keys.
2698:      */
2699:     protected function _renderTemplates($content)
2700:     {
2701:         $types = $this->_getTypes();
2702:         $rendered = [];
2703:         $template = $this->viewBuilder()->getTemplate();
2704:         if (empty($template)) {
2705:             foreach ($types as $type) {
2706:                 $rendered[$type] = $this->_encodeString($content, $this->charset);
2707:             }
2708: 
2709:             return $rendered;
2710:         }
2711: 
2712:         $View = $this->createView();
2713: 
2714:         list($templatePlugin) = pluginSplit($View->getTemplate());
2715:         list($layoutPlugin) = pluginSplit($View->getLayout());
2716:         if ($templatePlugin) {
2717:             $View->setPlugin($templatePlugin);
2718:         } elseif ($layoutPlugin) {
2719:             $View->setPlugin($layoutPlugin);
2720:         }
2721: 
2722:         if ($View->get('content') === null) {
2723:             $View->set('content', $content);
2724:         }
2725: 
2726:         foreach ($types as $type) {
2727:             $View->hasRendered = false;
2728:             $View->setTemplatePath('Email' . DIRECTORY_SEPARATOR . $type);
2729:             $View->setLayoutPath('Email' . DIRECTORY_SEPARATOR . $type);
2730: 
2731:             $render = $View->render();
2732:             $render = str_replace(["\r\n", "\r"], "\n", $render);
2733:             $rendered[$type] = $this->_encodeString($render, $this->charset);
2734:         }
2735: 
2736:         foreach ($rendered as $type => $content) {
2737:             $rendered[$type] = $this->_wrap($content);
2738:             $rendered[$type] = implode("\n", $rendered[$type]);
2739:             $rendered[$type] = rtrim($rendered[$type], "\n");
2740:         }
2741: 
2742:         return $rendered;
2743:     }
2744: 
2745:     /**
2746:      * Return the Content-Transfer Encoding value based
2747:      * on the set transferEncoding or set charset.
2748:      *
2749:      * @return string
2750:      */
2751:     protected function _getContentTransferEncoding()
2752:     {
2753:         if ($this->transferEncoding) {
2754:             return $this->transferEncoding;
2755:         }
2756: 
2757:         $charset = strtoupper($this->charset);
2758:         if (in_array($charset, $this->_charset8bit)) {
2759:             return '8bit';
2760:         }
2761: 
2762:         return '7bit';
2763:     }
2764: 
2765:     /**
2766:      * Return charset value for Content-Type.
2767:      *
2768:      * Checks fallback/compatibility types which include workarounds
2769:      * for legacy japanese character sets.
2770:      *
2771:      * @return string
2772:      */
2773:     protected function _getContentTypeCharset()
2774:     {
2775:         $charset = strtoupper($this->charset);
2776:         if (array_key_exists($charset, $this->_contentTypeCharset)) {
2777:             return strtoupper($this->_contentTypeCharset[$charset]);
2778:         }
2779: 
2780:         return strtoupper($this->charset);
2781:     }
2782: 
2783:     /**
2784:      * Serializes the email object to a value that can be natively serialized and re-used
2785:      * to clone this email instance.
2786:      *
2787:      * It has certain limitations for viewVars that are good to know:
2788:      *
2789:      *    - ORM\Query executed and stored as resultset
2790:      *    - SimpleXMLElements stored as associative array
2791:      *    - Exceptions stored as strings
2792:      *    - Resources, \Closure and \PDO are not supported.
2793:      *
2794:      * @return array Serializable array of configuration properties.
2795:      * @throws \Exception When a view var object can not be properly serialized.
2796:      */
2797:     public function jsonSerialize()
2798:     {
2799:         $properties = [
2800:             '_to', '_from', '_sender', '_replyTo', '_cc', '_bcc', '_subject',
2801:             '_returnPath', '_readReceipt', '_emailFormat', '_emailPattern', '_domain',
2802:             '_attachments', '_messageId', '_headers', '_appCharset', 'viewVars', 'charset', 'headerCharset'
2803:         ];
2804: 
2805:         $array = ['viewConfig' => $this->viewBuilder()->jsonSerialize()];
2806: 
2807:         foreach ($properties as $property) {
2808:             $array[$property] = $this->{$property};
2809:         }
2810: 
2811:         array_walk($array['_attachments'], function (&$item, $key) {
2812:             if (!empty($item['file'])) {
2813:                 $item['data'] = $this->_readFile($item['file']);
2814:                 unset($item['file']);
2815:             }
2816:         });
2817: 
2818:         array_walk_recursive($array['viewVars'], [$this, '_checkViewVars']);
2819: 
2820:         return array_filter($array, function ($i) {
2821:             return !is_array($i) && strlen($i) || !empty($i);
2822:         });
2823:     }
2824: 
2825:     /**
2826:      * Iterates through hash to clean up and normalize.
2827:      *
2828:      * @param mixed $item Reference to the view var value.
2829:      * @param string $key View var key.
2830:      * @return void
2831:      */
2832:     protected function _checkViewVars(&$item, $key)
2833:     {
2834:         if ($item instanceof Exception) {
2835:             $item = (string)$item;
2836:         }
2837: 
2838:         if (is_resource($item) ||
2839:             $item instanceof Closure ||
2840:             $item instanceof PDO
2841:         ) {
2842:             throw new RuntimeException(sprintf(
2843:                 'Failed serializing the `%s` %s in the `%s` view var',
2844:                 is_resource($item) ? get_resource_type($item) : get_class($item),
2845:                 is_resource($item) ? 'resource' : 'object',
2846:                 $key
2847:             ));
2848:         }
2849:     }
2850: 
2851:     /**
2852:      * Configures an email instance object from serialized config.
2853:      *
2854:      * @param array $config Email configuration array.
2855:      * @return $this Configured email instance.
2856:      */
2857:     public function createFromArray($config)
2858:     {
2859:         if (isset($config['viewConfig'])) {
2860:             $this->viewBuilder()->createFromArray($config['viewConfig']);
2861:             unset($config['viewConfig']);
2862:         }
2863: 
2864:         foreach ($config as $property => $value) {
2865:             $this->{$property} = $value;
2866:         }
2867: 
2868:         return $this;
2869:     }
2870: 
2871:     /**
2872:      * Serializes the Email object.
2873:      *
2874:      * @return string
2875:      */
2876:     public function serialize()
2877:     {
2878:         $array = $this->jsonSerialize();
2879:         array_walk_recursive($array, function (&$item, $key) {
2880:             if ($item instanceof SimpleXMLElement) {
2881:                 $item = json_decode(json_encode((array)$item), true);
2882:             }
2883:         });
2884: 
2885:         return serialize($array);
2886:     }
2887: 
2888:     /**
2889:      * Unserializes the Email object.
2890:      *
2891:      * @param string $data Serialized string.
2892:      * @return static Configured email instance.
2893:      */
2894:     public function unserialize($data)
2895:     {
2896:         return $this->createFromArray(unserialize($data));
2897:     }
2898: }
2899: 
Follow @CakePHP
#IRC
OpenHub
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Logos & Trademarks
  • Community
  • Team
  • Issues (Github)
  • YouTube Channel
  • Get Involved
  • Bakery
  • Featured Resources
  • Newsletter
  • Certification
  • My CakePHP
  • CakeFest
  • Facebook
  • Twitter
  • Help & Support
  • Forum
  • Stack Overflow
  • IRC
  • Slack
  • Paid Support

Generated using CakePHP API Docs