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.7 Red Velvet API

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