Mail.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. <?php
  2. /**
  3. *
  4. * Cube Framework $Id$ ogzubZwehP/Dbar/vhFenwBJAAkAUl0ok54nV45kx1k=
  5. *
  6. * @link http://codecu.be/framework
  7. * @copyright Copyright (c) 2016 CodeCube SRL
  8. * @license http://codecu.be/framework/license Commercial License
  9. *
  10. * @version 1.8
  11. */
  12. namespace Cube;
  13. use Cube\Mail\Transport\AbstractTransport,
  14. Cube\Controller\Front,
  15. Cube\Translate\Adapter\AbstractAdapter as TranslateAdapter;
  16. /**
  17. * unified mailer class
  18. *
  19. * Class Mail
  20. *
  21. * @package Cube
  22. */
  23. class Mail
  24. {
  25. /**
  26. * carriage return
  27. */
  28. const CRLF = "\r\n";
  29. /**
  30. * new line character
  31. */
  32. const NL = "\n";
  33. /**
  34. *
  35. * mail charset
  36. *
  37. * @var string
  38. */
  39. protected $_charset = 'utf-8';
  40. /**
  41. *
  42. * content type, accepted: text/plain, text/html
  43. *
  44. * @var string
  45. */
  46. protected $_contentType = 'text/plain';
  47. /**
  48. *
  49. * content encoding
  50. *
  51. * @var string
  52. */
  53. protected $_encoding = '7bit';
  54. /**
  55. *
  56. * mailer to use
  57. *
  58. * @var \Cube\Mail\Transport\AbstractTransport
  59. */
  60. protected $_transport;
  61. /**
  62. *
  63. * to field ('address', 'name')
  64. *
  65. * @var array
  66. */
  67. protected $_to = array();
  68. /**
  69. *
  70. * from field ('address', 'name')
  71. *
  72. * @var array
  73. */
  74. protected $_from = array();
  75. /**
  76. *
  77. * reply to field ('address', 'name')
  78. *
  79. * @var array
  80. */
  81. protected $_replyTo = array();
  82. /**
  83. *
  84. * cc field ('address', 'name')
  85. *
  86. * @var array
  87. */
  88. protected $_cc = array();
  89. /**
  90. *
  91. * bcc field ('address', 'name')
  92. *
  93. * @var array
  94. */
  95. protected $_bcc = array();
  96. /**
  97. *
  98. * email subject
  99. *
  100. * @var string
  101. */
  102. protected $_subject;
  103. /**
  104. *
  105. * text email body
  106. *
  107. * @var string
  108. */
  109. protected $_bodyText;
  110. /**
  111. *
  112. * html email body
  113. *
  114. * @var string
  115. */
  116. protected $_bodyHtml;
  117. /**
  118. *
  119. * email headers
  120. *
  121. * @var array
  122. */
  123. protected $_headers = array('X-Mailer' => 'Cube Framework');
  124. /**
  125. * date header
  126. *
  127. * @var string
  128. */
  129. protected $_date = null;
  130. /**
  131. * Message-ID header
  132. *
  133. * @var string
  134. */
  135. protected $_messageId = null;
  136. /**
  137. *
  138. * view object
  139. *
  140. * @var \Cube\View
  141. */
  142. protected $_view;
  143. /**
  144. *
  145. * translate adapter
  146. *
  147. * @var \Cube\Translate\Adapter\AbstractAdapter
  148. */
  149. protected $_translate;
  150. /**
  151. *
  152. * class constructor
  153. *
  154. * @param string $charset
  155. */
  156. public function __construct($charset = null)
  157. {
  158. if ($charset !== null) {
  159. $this->_charset = $charset;
  160. }
  161. date_default_timezone_set(
  162. @date_default_timezone_get());
  163. $this->setDate()
  164. ->setMessageId();
  165. }
  166. /**
  167. *
  168. * set charset
  169. *
  170. * @return string
  171. */
  172. public function getCharset()
  173. {
  174. return $this->_charset;
  175. }
  176. /**
  177. *
  178. * get charset
  179. *
  180. * @param string $charset
  181. *
  182. * @return $this
  183. */
  184. public function setCharset($charset)
  185. {
  186. $this->_charset = $charset;
  187. return $this;
  188. }
  189. /**
  190. *
  191. * get content type
  192. *
  193. * @return string
  194. */
  195. public function getContentType()
  196. {
  197. return $this->_contentType;
  198. }
  199. /**
  200. *
  201. * set content type
  202. *
  203. * @param string $contentType
  204. *
  205. * @return $this
  206. */
  207. public function setContentType($contentType)
  208. {
  209. $this->_contentType = $contentType;
  210. return $this;
  211. }
  212. /**
  213. *
  214. * get 'To' field
  215. *
  216. * @return array
  217. */
  218. public function getTo()
  219. {
  220. return $this->_to;
  221. }
  222. /**
  223. *
  224. * set 'To' field (clear first)
  225. *
  226. * @param string $address 'To' email address
  227. * @param string $name 'To' name (optional)
  228. *
  229. * @return $this
  230. */
  231. public function setTo($address, $name = null)
  232. {
  233. $this->clearTo()
  234. ->addTo($address, $name);
  235. return $this;
  236. }
  237. /**
  238. *
  239. * add new address in the 'To' field
  240. *
  241. * @param string $address 'To' email address
  242. * @param string $name 'To' name (optional)
  243. *
  244. * @return $this
  245. */
  246. public function addTo($address, $name = null)
  247. {
  248. $this->_to[] = array(
  249. 'address' => $address,
  250. 'name' => $name,
  251. );
  252. return $this;
  253. }
  254. /**
  255. *
  256. * get 'From' field
  257. *
  258. * @return array
  259. */
  260. public function getFrom()
  261. {
  262. return $this->_from;
  263. }
  264. /**
  265. *
  266. * set 'From' field
  267. *
  268. * @param string $address 'To' email address
  269. * @param string $name 'To' name (optional)
  270. *
  271. * @return $this
  272. */
  273. public function setFrom($address, $name = null)
  274. {
  275. $this->_from = array(
  276. 'address' => $address,
  277. 'name' => $name,
  278. );
  279. return $this;
  280. }
  281. /**
  282. *
  283. * get 'Cc' field
  284. *
  285. * @return array
  286. */
  287. public function getCc()
  288. {
  289. return $this->_cc;
  290. }
  291. /**
  292. *
  293. * set 'Cc' field
  294. *
  295. * @param string $address 'To' email address
  296. * @param string $name 'To' name (optional)
  297. *
  298. * @return $this
  299. */
  300. public function setCc($address, $name = null)
  301. {
  302. $this->_cc[] = array(
  303. 'address' => $address,
  304. 'name' => $name,
  305. );
  306. return $this;
  307. }
  308. /**
  309. *
  310. * get 'Bcc' field
  311. *
  312. * @return array
  313. */
  314. public function getBcc()
  315. {
  316. return $this->_bcc;
  317. }
  318. /**
  319. *
  320. * set 'Bcc' field
  321. *
  322. * @param string $address 'To' email address
  323. * @param string $name 'To' name (optional)
  324. *
  325. * @return $this
  326. */
  327. public function setBcc($address, $name = null)
  328. {
  329. $this->_bcc[] = array(
  330. 'address' => $address,
  331. 'name' => $name,
  332. );
  333. return $this;
  334. }
  335. /**
  336. *
  337. * get 'ReplyTo' field
  338. *
  339. * @return array
  340. */
  341. public function getReplyTo()
  342. {
  343. return $this->_replyTo;
  344. }
  345. /**
  346. *
  347. * set 'ReplyTo' field
  348. *
  349. * @param string $address 'To' email address
  350. * @param string $name 'To' name (optional)
  351. *
  352. * @return $this
  353. */
  354. public function setReplyTo($address, $name = null)
  355. {
  356. $this->_replyTo = array(
  357. 'address' => $address,
  358. 'name' => $name,
  359. );
  360. return $this;
  361. }
  362. /**
  363. *
  364. * clear all "To" and headers fields
  365. *
  366. * @return $this
  367. */
  368. public function clearTo()
  369. {
  370. $this->_to = array();
  371. $this->_cc = array();
  372. $this->_bcc = array();
  373. $this->_replyTo = array();
  374. $this->_headers = array('X-Mailer' => 'Cube Framework');
  375. return $this;
  376. }
  377. /**
  378. *
  379. * get subject field
  380. *
  381. * @return string
  382. */
  383. public function getSubject()
  384. {
  385. $translate = $this->getTranslate();
  386. if (null !== $translate) {
  387. $subject = $translate->_($this->_subject);
  388. }
  389. else {
  390. $subject = $this->_subject;
  391. }
  392. return $this->_filterString($subject);
  393. }
  394. /**
  395. *
  396. * set message subject field
  397. *
  398. * @param string $subject
  399. *
  400. * @return $this
  401. */
  402. public function setSubject($subject)
  403. {
  404. $this->_subject = (string)$subject;
  405. return $this;
  406. }
  407. /**
  408. *
  409. * get body text (for plain messages)
  410. *
  411. * @return string
  412. */
  413. public function getBodyText()
  414. {
  415. return $this->_bodyText;
  416. }
  417. /**
  418. *
  419. * set body text (for plain messages)
  420. *
  421. * @param string $bodyText
  422. *
  423. * @return $this
  424. */
  425. public function setBodyText($bodyText)
  426. {
  427. $this->setContentType('text/plain');
  428. $this->_bodyText = (string)$bodyText;
  429. return $this;
  430. }
  431. /**
  432. *
  433. * get body content (for html messages)
  434. *
  435. * @return string
  436. */
  437. public function getBodyHtml()
  438. {
  439. return $this->_bodyHtml;
  440. }
  441. /**
  442. *
  443. * set body content (for html messages)
  444. *
  445. * @param string $bodyHtml
  446. *
  447. * @return $this
  448. */
  449. public function setBodyHtml($bodyHtml)
  450. {
  451. $this->setContentType('text/html');
  452. $this->_bodyHtml = (string)$bodyHtml;
  453. return $this;
  454. }
  455. /**
  456. *
  457. * get mail body
  458. *
  459. * @return string
  460. * @throws \RuntimeException
  461. */
  462. public function getBody()
  463. {
  464. if ($this->_contentType == 'text/plain') {
  465. return $this->_bodyText;
  466. }
  467. else if ($this->_contentType == 'text/html') {
  468. return $this->_bodyHtml;
  469. }
  470. else {
  471. throw new \RuntimeException(sprintf("The content type must be of type
  472. 'text/plain' or 'text/html', '%s' given", $this->_contentType));
  473. }
  474. }
  475. /**
  476. *
  477. * add multiple headers to the message
  478. *
  479. * @param array $headers
  480. *
  481. * @return $this
  482. */
  483. public function addHeaders(array $headers)
  484. {
  485. foreach ($headers as $key => $value) {
  486. $this->addHeader($key, $value);
  487. }
  488. return $this;
  489. }
  490. /**
  491. *
  492. * add a single header to the message
  493. *
  494. * @param string $key
  495. * @param string $value
  496. *
  497. * @return $this
  498. */
  499. public function addHeader($key, $value)
  500. {
  501. $this->_headers[(string)$key] = (string)$value;
  502. return $this;
  503. }
  504. /**
  505. *
  506. * get message date
  507. *
  508. * @return string
  509. */
  510. public function getDate()
  511. {
  512. return $this->_date;
  513. }
  514. /**
  515. *
  516. * set message date
  517. *
  518. * @param string $date
  519. *
  520. * @return $this
  521. */
  522. public function setDate($date = null)
  523. {
  524. if ($date === null) {
  525. $date = date('D, j M Y H:i:s O');
  526. }
  527. $this->_date = $date;
  528. return $this;
  529. }
  530. /**
  531. *
  532. * get message id
  533. *
  534. * @return string
  535. */
  536. public function getMessageId()
  537. {
  538. return $this->_messageId;
  539. }
  540. /**
  541. *
  542. * set message id
  543. *
  544. * @param string $messageId
  545. *
  546. * @return $this
  547. */
  548. public function setMessageId($messageId = null)
  549. {
  550. if ($messageId === null) {
  551. $uniqId = md5(uniqid(time()));
  552. $serverName = array_key_exists('SERVER_NAME', $_SERVER) ? $_SERVER['SERVER_NAME'] : 'localhost';
  553. $messageId = sprintf("<%s@%s>", $uniqId, $serverName);
  554. }
  555. $this->_messageId = $messageId;
  556. return $this;
  557. }
  558. /**
  559. *
  560. * get active mailer
  561. *
  562. * @return \Cube\Mail\Transport\AbstractTransport
  563. */
  564. public function getTransport()
  565. {
  566. if (!$this->_transport instanceof AbstractTransport) {
  567. $this->setTransport('mail');
  568. }
  569. return $this->_transport;
  570. }
  571. /**
  572. *
  573. * set active mailer
  574. *
  575. * @param string|\Cube\Mail\Transport\AbstractTransport $transport
  576. *
  577. * @throws \InvalidArgumentException
  578. * @return $this
  579. */
  580. public function setTransport($transport)
  581. {
  582. if ($transport instanceof AbstractTransport) {
  583. $this->_transport = $transport;
  584. }
  585. else {
  586. if (!class_exists($transport)) {
  587. if (!in_array($transport, array('mail', 'sendmail', 'smtp'))) {
  588. throw new \InvalidArgumentException(
  589. sprintf("The mail transport must be
  590. one of 'mail', 'sendmail', 'smtp' or a class that extends
  591. \Cube\Mail\Transport\AbstractTransport.", $transport));
  592. }
  593. $transport = '\\Cube\\Mail\\Transport\\' . ucfirst($transport);
  594. }
  595. $this->_transport = new $transport();
  596. }
  597. return $this;
  598. }
  599. /**
  600. *
  601. * get the view object
  602. *
  603. * @return \Cube\View
  604. */
  605. public function getView()
  606. {
  607. if ($this->_view === null) {
  608. $this->setView();
  609. }
  610. return $this->_view;
  611. }
  612. /**
  613. * set the view object
  614. *
  615. * @param \Cube\View $view
  616. *
  617. * @return $this
  618. */
  619. public function setView(View $view = null)
  620. {
  621. if (!$view instanceof View) {
  622. $view = new View();
  623. }
  624. $this->_view = $view;
  625. return $this;
  626. }
  627. /**
  628. *
  629. * set translate adapter
  630. *
  631. * @param \Cube\Translate\Adapter\AbstractAdapter $translate
  632. *
  633. * @return $this
  634. */
  635. public function setTranslate(TranslateAdapter $translate)
  636. {
  637. $this->_translate = $translate;
  638. return $this;
  639. }
  640. /**
  641. *
  642. * get translate adapter
  643. *
  644. * @return \Cube\Translate\Adapter\AbstractAdapter
  645. */
  646. public function getTranslate()
  647. {
  648. if (!$this->_translate instanceof TranslateAdapter) {
  649. $translate = Front::getInstance()->getBootstrap()->getResource('translate');
  650. if ($translate instanceof Translate) {
  651. $this->setTranslate(
  652. $translate->getAdapter());
  653. }
  654. }
  655. return $this->_translate;
  656. }
  657. /**
  658. *
  659. * format an address field
  660. *
  661. * @param array $data array of data
  662. *
  663. * @return string formatted address
  664. */
  665. protected function _formatAddress($data)
  666. {
  667. $address = array();
  668. if (array_key_exists('address', $data)) {
  669. $data = array($data);
  670. }
  671. foreach ((array)$data as $field) {
  672. if (isset($field['name'])) {
  673. $address[] = $field['name'] . ' <' . $field['address'] . '>';
  674. }
  675. else {
  676. $address[] = $field['address'];
  677. }
  678. }
  679. return implode('; ', $address);
  680. }
  681. /**
  682. *
  683. * filter a string of new line characters and for ascii tags
  684. *
  685. * @param string $string
  686. *
  687. * @return string
  688. */
  689. protected function _filterString($string)
  690. {
  691. return str_ireplace(
  692. array("\r", "\n", '&amp;', '&#039;', '&quot;', '&lt;', '&gt;', '&nbsp;'),
  693. array('', '', '&', "'", '"', '<', '>', ' '), $string);
  694. }
  695. /**
  696. *
  697. * create mail header
  698. *
  699. * @return string
  700. * @throws \RuntimeException
  701. */
  702. public function createHeader()
  703. {
  704. $headers = array();
  705. $this->addHeader('Date', $this->getDate());
  706. if (!isset($this->_from['address'])) {
  707. throw new \RuntimeException("The 'From' email field must be set.");
  708. }
  709. $this->addHeader('Return-Path', '<' . $this->_from['address'] . '>');
  710. if (!$this->_transport instanceof Mail\Transport\Mail) {
  711. if (count($this->getTo()) > 0) {
  712. $this->addHeader('To', $this->_formatAddress($this->_to));
  713. }
  714. else if (count($this->_cc) == 0) {
  715. $this->addHeader('To', "Undisclosed Recipients");
  716. }
  717. }
  718. $from[] = $this->_from;
  719. $this->addHeader('From', $this->_formatAddress($from));
  720. if (count($this->_cc) > 0) {
  721. $this->addHeader('Cc', $this->_formatAddress($this->_cc));
  722. }
  723. if (count($this->_bcc) > 0) {
  724. $this->addHeader('Bcc', $this->_formatAddress($this->_bcc));
  725. }
  726. if (count($this->_replyTo) > 0) {
  727. $this->addHeader('Reply-To', $this->_formatAddress($this->_replyTo));
  728. }
  729. if (!$this->_transport instanceof Mail\Transport\Mail) {
  730. $this->addHeader('Subject', $this->getSubject());
  731. }
  732. $this->addHeader('Message-ID', $this->getMessageId())
  733. ->addHeader('MIME-Version', '1.0');
  734. $this->addHeader('Content-Transfer-Encoding', $this->_encoding)
  735. ->addHeader('Content-Type', sprintf('%s; charset="%s"', $this->_contentType, $this->_charset));
  736. foreach ((array)$this->_headers as $key => $value) {
  737. $headers[] = $key . ': ' . $value;
  738. }
  739. return implode(self::NL, $headers);
  740. }
  741. /**
  742. *
  743. * send mail
  744. *
  745. * @return bool
  746. */
  747. public function send()
  748. {
  749. return $this->getTransport()
  750. ->setMail($this)
  751. ->send();
  752. }
  753. }