PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/shopaholic/lib/Nette/Mail/Mail.php

http://github.com/jakubkulhan/shopaholic
PHP | 458 lines | 209 code | 94 blank | 155 comment | 17 complexity | 982ed2b878ad16da256bf1c20a4f36bf MD5 | raw file
Possible License(s): WTFPL
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license" that is bundled
  8. * with this package in the file license.txt.
  9. *
  10. * For more information please see http://nettephp.com
  11. *
  12. * @copyright Copyright (c) 2004, 2009 David Grudl
  13. * @license http://nettephp.com/license Nette license
  14. * @link http://nettephp.com
  15. * @category Nette
  16. * @package Nette\Mail
  17. * @version $Id: Mail.php 368 2009-06-25 14:26:59Z david@grudl.com $
  18. */
  19. require_once dirname(__FILE__) . '/../Mail/MailMimePart.php';
  20. /**
  21. * Mail provides functionality to compose and send both text and MIME-compliant multipart e-mail messages.
  22. *
  23. * @author David Grudl
  24. * @copyright Copyright (c) 2004, 2009 David Grudl
  25. * @package Nette\Mail
  26. *
  27. * @property string $from
  28. * @property string $subject
  29. * @property string $returnPath
  30. * @property int $priority
  31. * @property string $htmlBody
  32. */
  33. class Mail extends MailMimePart
  34. {
  35. /**#@+ Priority */
  36. const HIGH = 1;
  37. const NORMAL = 3;
  38. const LOW = 5;
  39. /**#@-*/
  40. /** @var IMailer */
  41. public static $defaultMailer = 'Nette\Mail\SendmailMailer';
  42. /** @var array */
  43. public static $defaultHeaders = array(
  44. 'MIME-Version' => '1.0',
  45. 'X-Mailer' => 'Nette Framework',
  46. );
  47. /** @var string */
  48. private $charset = 'UTF-8';
  49. /** @var array */
  50. private $attachments = array();
  51. /** @var array */
  52. private $inlines = array();
  53. /** @var mixed */
  54. private $html;
  55. /** @var string */
  56. private $basePath;
  57. public function __construct()
  58. {
  59. foreach (self::$defaultHeaders as $name => $value) {
  60. $this->setHeader($name, $value);
  61. }
  62. $this->setHeader('Date', date('r'));
  63. }
  64. /**
  65. * Sets the sender of the message.
  66. * @param string e-mail or format "John Doe" <doe@example.com>
  67. * @param string
  68. * @return Mail provides a fluent interface
  69. */
  70. public function setFrom($email, $name = NULL)
  71. {
  72. $this->setHeader('From', $this->formatEmail($email, $name));
  73. return $this;
  74. }
  75. /**
  76. * Returns the sender of the message.
  77. * @return array
  78. */
  79. public function getFrom()
  80. {
  81. return $this->getHeader('From');
  82. }
  83. /**
  84. * Adds the reply-to address.
  85. * @param string e-mail or format "John Doe" <doe@example.com>
  86. * @param string
  87. * @return Mail provides a fluent interface
  88. */
  89. public function addReplyTo($email, $name = NULL)
  90. {
  91. $this->setHeader('Reply-To', $this->formatEmail($email, $name), TRUE);
  92. return $this;
  93. }
  94. /**
  95. * Sets the subject of the message.
  96. * @param string
  97. * @return Mail provides a fluent interface
  98. */
  99. public function setSubject($subject)
  100. {
  101. $this->setHeader('Subject', $subject);
  102. return $this;
  103. }
  104. /**
  105. * Returns the subject of the message.
  106. * @return string
  107. */
  108. public function getSubject()
  109. {
  110. return $this->getHeader('Subject');
  111. }
  112. /**
  113. * Adds email recipient.
  114. * @param string e-mail or format "John Doe" <doe@example.com>
  115. * @param string
  116. * @return Mail provides a fluent interface
  117. */
  118. public function addTo($email, $name = NULL) // addRecipient()
  119. {
  120. $this->setHeader('To', $this->formatEmail($email, $name), TRUE);
  121. return $this;
  122. }
  123. /**
  124. * Adds carbon copy email recipient.
  125. * @param string e-mail or format "John Doe" <doe@example.com>
  126. * @param string
  127. * @return Mail provides a fluent interface
  128. */
  129. public function addCc($email, $name = NULL)
  130. {
  131. $this->setHeader('Cc', $this->formatEmail($email, $name), TRUE);
  132. return $this;
  133. }
  134. /**
  135. * Adds blind carbon copy email recipient.
  136. * @param string e-mail or format "John Doe" <doe@example.com>
  137. * @param string
  138. * @return Mail provides a fluent interface
  139. */
  140. public function addBcc($email, $name = NULL)
  141. {
  142. $this->setHeader('Bcc', $this->formatEmail($email, $name), TRUE);
  143. return $this;
  144. }
  145. /**
  146. * Formats recipient e-mail.
  147. * @param string
  148. * @param string
  149. * @return array
  150. */
  151. private function formatEmail($email, $name)
  152. {
  153. if (!$name && preg_match('#^(.+) +<(.*)>$#', $email, $matches)) {
  154. return array($matches[2] => $matches[1]);
  155. } else {
  156. return array($email => $name);
  157. }
  158. }
  159. /**
  160. * Sets the Return-Path header of the message.
  161. * @param string e-mail
  162. * @return Mail provides a fluent interface
  163. */
  164. public function setReturnPath($email)
  165. {
  166. $this->setHeader('Return-Path', $email);
  167. return $this;
  168. }
  169. /**
  170. * Returns the Return-Path header.
  171. * @return string
  172. */
  173. public function getReturnPath()
  174. {
  175. return $this->getHeader('From');
  176. }
  177. /**
  178. * Sets email priority.
  179. * @param int
  180. * @return Mail provides a fluent interface
  181. */
  182. public function setPriority($priority)
  183. {
  184. $this->setHeader('X-Priority', (int) $priority);
  185. return $this;
  186. }
  187. /**
  188. * Returns email priority.
  189. * @return int
  190. */
  191. public function getPriority()
  192. {
  193. return $this->getHeader('X-Priority');
  194. }
  195. /**
  196. * Sets HTML body.
  197. * @param string|Nette\Templates\ITemplate
  198. * @param mixed base-path or FALSE to disable parsing
  199. * @return Mail provides a fluent interface
  200. */
  201. public function setHtmlBody($html, $basePath = NULL)
  202. {
  203. $this->html = $html;
  204. $this->basePath = $basePath;
  205. return $this;
  206. }
  207. /**
  208. * Gets HTML body.
  209. * @return mixed
  210. */
  211. public function getHtmlBody()
  212. {
  213. return $this->html;
  214. }
  215. /**
  216. * Adds embedded file.
  217. * @param string
  218. * @param string
  219. * @param string
  220. * @return MailMimePart
  221. */
  222. public function addEmbeddedFile($file, $contentType = NULL, $encoding = self::ENCODING_BASE64)
  223. {
  224. $part = $this->createFilePart($file, $contentType, $encoding);
  225. $part->setHeader('Content-Disposition', 'inline; filename="' . basename($file) . '"');
  226. $part->setHeader('Content-ID', '<' . md5(uniqid('', TRUE)) . '>');
  227. return $this->inlines[$file] = $part;
  228. }
  229. /**
  230. * Adds attachment.
  231. * @param string
  232. * @param string
  233. * @param string
  234. * @return MailMimePart
  235. */
  236. public function addAttachment($file, $contentType = NULL, $encoding = self::ENCODING_BASE64)
  237. {
  238. $part = $this->createFilePart($file, $contentType, $encoding);
  239. $part->setHeader('Content-Disposition', 'attachment; filename="' . basename($file) . '"');
  240. return $this->attachments[] = $part;
  241. }
  242. /**
  243. * Creates file MIME part.
  244. * @param string
  245. * @param string
  246. * @param string
  247. * @return MailMimePart
  248. */
  249. public function createFilePart($file, $contentType, $encoding)
  250. {
  251. if (!is_file($file)) {
  252. throw new FileNotFoundException("File '$file' not found.");
  253. }
  254. if (!$contentType) {
  255. $info = getimagesize($file);
  256. $contentType = $info ? image_type_to_mime_type($info[2]) : 'application/octet-stream';
  257. }
  258. $part = new MailMimePart;
  259. $part->setContentType($contentType);
  260. $part->setEncoding($encoding);
  261. $part->setBody(file_get_contents($file));
  262. return $part;
  263. }
  264. /********************* building and sending ****************d*g**/
  265. /**
  266. * Sends e-mail.
  267. * @param IMailer
  268. * @return void
  269. */
  270. public function send(IMailer $mailer = NULL)
  271. {
  272. if ($mailer === NULL) {
  273. fixNamespace(self::$defaultMailer);
  274. $mailer = is_object(self::$defaultMailer) ? self::$defaultMailer : new self::$defaultMailer;
  275. }
  276. $mailer->send($this->build());
  277. }
  278. /**
  279. * Builds e-mail.
  280. * @return void
  281. */
  282. protected function build()
  283. {
  284. $mail = clone $this;
  285. $hostname = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost';
  286. $mail->setHeader('Message-ID', '<' . md5(uniqid('', TRUE)) . "@$hostname>");
  287. $mail->buildHtml();
  288. $mail->buildText();
  289. $cursor = $mail;
  290. if ($mail->attachments) {
  291. $tmp = $cursor->setContentType('multipart/mixed');
  292. $cursor = $cursor->addPart();
  293. foreach ($mail->attachments as $value) {
  294. $tmp->addPart($value);
  295. }
  296. }
  297. if ($mail->html != NULL) { // intentionally ==
  298. $tmp = $cursor->setContentType('multipart/alternative');
  299. $cursor = $cursor->addPart();
  300. $alt = $tmp->addPart();
  301. if ($mail->inlines) {
  302. $tmp = $alt->setContentType('multipart/related');
  303. $alt = $alt->addPart();
  304. foreach ($mail->inlines as $name => $value) {
  305. $tmp->addPart($value);
  306. }
  307. }
  308. $alt->setContentType('text/html', $mail->charset)
  309. ->setEncoding(preg_match('#[\x80-\xFF]#', $mail->html) ? self::ENCODING_8BIT : self::ENCODING_7BIT)
  310. ->setBody($mail->html);
  311. }
  312. $text = $mail->getBody();
  313. $mail->setBody(NULL);
  314. $cursor->setContentType('text/plain', $mail->charset)
  315. ->setEncoding(preg_match('#[\x80-\xFF]#', $text) ? self::ENCODING_8BIT : self::ENCODING_7BIT)
  316. ->setBody($text);
  317. return $mail;
  318. }
  319. /**
  320. * Builds HTML content.
  321. * @return void
  322. */
  323. protected function buildHtml()
  324. {
  325. if ($this->html instanceof Template) {
  326. $this->html->mail = $this;
  327. if ($this->basePath === NULL) {
  328. $this->basePath = dirname($this->html->getFile());
  329. }
  330. $this->html = $this->html->__toString(TRUE);
  331. }
  332. if ($this->basePath !== FALSE) {
  333. $cids = array();
  334. preg_match_all('#(src\s*=\s*|background\s*=\s*|url\()(["\'])(?![a-z]+:|[/\\#])(.+?)\\2#i', $this->html, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
  335. foreach (array_reverse($matches) as $m) {
  336. $file = rtrim($this->basePath, '/\\') . '/' . $m[3][0];
  337. $cid = isset($cids[$file]) ? $cids[$file] : $cids[$file] = substr($this->addEmbeddedFile($file)->getHeader("Content-ID"), 1, -1);
  338. $this->html = substr_replace($this->html, "{$m[1][0]}{$m[2][0]}cid:$cid{$m[2][0]}", $m[0][1], strlen($m[0][0]));
  339. }
  340. }
  341. if (!$this->getSubject() && preg_match('#<title>(.+)</title>#i', $this->html, $matches)) {
  342. $this->setSubject(html_entity_decode($matches[1], ENT_QUOTES, $this->charset));
  343. }
  344. }
  345. /**
  346. * Builds text content.
  347. * @return void
  348. */
  349. protected function buildText()
  350. {
  351. $text = $this->getBody();
  352. if ($text instanceof Template) {
  353. $text->mail = $this;
  354. $this->setBody($text->__toString(TRUE));
  355. } elseif ($text == NULL && $this->html != NULL) { // intentionally ==
  356. $text = preg_replace('#<(style|script|head).*</\\1>#Uis', '', $this->html);
  357. $text = preg_replace('#\s+#', ' ', $text);
  358. $text = preg_replace('#<(/?p|/?h\d|li|br|/tr)[ >]#i', "\n$0", $text);
  359. $text = html_entity_decode(strip_tags($text), ENT_QUOTES, $this->charset);
  360. $this->setBody(trim($text));
  361. }
  362. }
  363. }