Packages

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • Mail
  • MailMimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

  • SmtpException
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  * @package Nette\Mail
 11:  */
 12: 
 13: 
 14: 
 15: /**
 16:  * Mail provides functionality to compose and send both text and MIME-compliant multipart email messages.
 17:  *
 18:  * @author     David Grudl
 19:  *
 20:  * @property   array $from
 21:  * @property   string $subject
 22:  * @property   string $returnPath
 23:  * @property   int $priority
 24:  * @property   mixed $htmlBody
 25:  * @property   IMailer $mailer
 26:  * @package Nette\Mail
 27:  */
 28: class Mail extends MailMimePart
 29: {
 30:     /** Priority */
 31:     const HIGH = 1,
 32:         NORMAL = 3,
 33:         LOW = 5;
 34: 
 35:     /** @var IMailer */
 36:     public static $defaultMailer = 'SendmailMailer';
 37: 
 38:     /** @var array */
 39:     public static $defaultHeaders = array(
 40:         'MIME-Version' => '1.0',
 41:         'X-Mailer' => 'Nette Framework',
 42:     );
 43: 
 44:     /** @var IMailer */
 45:     private $mailer;
 46: 
 47:     /** @var array */
 48:     private $attachments = array();
 49: 
 50:     /** @var array */
 51:     private $inlines = array();
 52: 
 53:     /** @var mixed */
 54:     private $html;
 55: 
 56:     /** @var string */
 57:     private $basePath;
 58: 
 59: 
 60:     public function __construct()
 61:     {
 62:         foreach (self::$defaultHeaders as $name => $value) {
 63:             $this->setHeader($name, $value);
 64:         }
 65:         $this->setHeader('Date', date('r'));
 66:     }
 67: 
 68: 
 69:     /**
 70:      * Sets the sender of the message.
 71:      * @param  string  email or format "John Doe" <doe@example.com>
 72:      * @param  string
 73:      * @return self
 74:      */
 75:     public function setFrom($email, $name = NULL)
 76:     {
 77:         $this->setHeader('From', $this->formatEmail($email, $name));
 78:         return $this;
 79:     }
 80: 
 81: 
 82:     /**
 83:      * Returns the sender of the message.
 84:      * @return array
 85:      */
 86:     public function getFrom()
 87:     {
 88:         return $this->getHeader('From');
 89:     }
 90: 
 91: 
 92:     /**
 93:      * Adds the reply-to address.
 94:      * @param  string  email or format "John Doe" <doe@example.com>
 95:      * @param  string
 96:      * @return self
 97:      */
 98:     public function addReplyTo($email, $name = NULL)
 99:     {
100:         $this->setHeader('Reply-To', $this->formatEmail($email, $name), TRUE);
101:         return $this;
102:     }
103: 
104: 
105:     /**
106:      * Sets the subject of the message.
107:      * @param  string
108:      * @return self
109:      */
110:     public function setSubject($subject)
111:     {
112:         $this->setHeader('Subject', $subject);
113:         return $this;
114:     }
115: 
116: 
117:     /**
118:      * Returns the subject of the message.
119:      * @return string
120:      */
121:     public function getSubject()
122:     {
123:         return $this->getHeader('Subject');
124:     }
125: 
126: 
127:     /**
128:      * Adds email recipient.
129:      * @param  string  email or format "John Doe" <doe@example.com>
130:      * @param  string
131:      * @return self
132:      */
133:     public function addTo($email, $name = NULL) // addRecipient()
134:     {
135:         $this->setHeader('To', $this->formatEmail($email, $name), TRUE);
136:         return $this;
137:     }
138: 
139: 
140:     /**
141:      * Adds carbon copy email recipient.
142:      * @param  string  email or format "John Doe" <doe@example.com>
143:      * @param  string
144:      * @return self
145:      */
146:     public function addCc($email, $name = NULL)
147:     {
148:         $this->setHeader('Cc', $this->formatEmail($email, $name), TRUE);
149:         return $this;
150:     }
151: 
152: 
153:     /**
154:      * Adds blind carbon copy email recipient.
155:      * @param  string  email or format "John Doe" <doe@example.com>
156:      * @param  string
157:      * @return self
158:      */
159:     public function addBcc($email, $name = NULL)
160:     {
161:         $this->setHeader('Bcc', $this->formatEmail($email, $name), TRUE);
162:         return $this;
163:     }
164: 
165: 
166:     /**
167:      * Formats recipient email.
168:      * @param  string
169:      * @param  string
170:      * @return array
171:      */
172:     private function formatEmail($email, $name)
173:     {
174:         if (!$name && preg_match('#^(.+) +<(.*)>\z#', $email, $matches)) {
175:             return array($matches[2] => $matches[1]);
176:         } else {
177:             return array($email => $name);
178:         }
179:     }
180: 
181: 
182:     /**
183:      * Sets the Return-Path header of the message.
184:      * @param  string  email
185:      * @return self
186:      */
187:     public function setReturnPath($email)
188:     {
189:         $this->setHeader('Return-Path', $email);
190:         return $this;
191:     }
192: 
193: 
194:     /**
195:      * Returns the Return-Path header.
196:      * @return string
197:      */
198:     public function getReturnPath()
199:     {
200:         return $this->getHeader('Return-Path');
201:     }
202: 
203: 
204:     /**
205:      * Sets email priority.
206:      * @param  int
207:      * @return self
208:      */
209:     public function setPriority($priority)
210:     {
211:         $this->setHeader('X-Priority', (int) $priority);
212:         return $this;
213:     }
214: 
215: 
216:     /**
217:      * Returns email priority.
218:      * @return int
219:      */
220:     public function getPriority()
221:     {
222:         return $this->getHeader('X-Priority');
223:     }
224: 
225: 
226:     /**
227:      * Sets HTML body.
228:      * @param  string|ITemplate
229:      * @param  mixed base-path or FALSE to disable parsing
230:      * @return self
231:      */
232:     public function setHtmlBody($html, $basePath = NULL)
233:     {
234:         $this->html = $html;
235:         $this->basePath = $basePath;
236:         return $this;
237:     }
238: 
239: 
240:     /**
241:      * Gets HTML body.
242:      * @return mixed
243:      */
244:     public function getHtmlBody()
245:     {
246:         return $this->html;
247:     }
248: 
249: 
250:     /**
251:      * Adds embedded file.
252:      * @param  string
253:      * @param  string
254:      * @param  string
255:      * @return MailMimePart
256:      */
257:     public function addEmbeddedFile($file, $content = NULL, $contentType = NULL)
258:     {
259:         return $this->inlines[$file] = $this->createAttachment($file, $content, $contentType, 'inline')
260:             ->setHeader('Content-ID', $this->getRandomId());
261:     }
262: 
263: 
264:     /**
265:      * Adds attachment.
266:      * @param  string
267:      * @param  string
268:      * @param  string
269:      * @return MailMimePart
270:      */
271:     public function addAttachment($file, $content = NULL, $contentType = NULL)
272:     {
273:         return $this->attachments[] = $this->createAttachment($file, $content, $contentType, 'attachment');
274:     }
275: 
276: 
277:     /**
278:      * Creates file MIME part.
279:      * @return MailMimePart
280:      */
281:     private function createAttachment($file, $content, $contentType, $disposition)
282:     {
283:         $part = new MailMimePart;
284:         if ($content === NULL) {
285:             $content = @file_get_contents($file); // intentionally @
286:             if ($content === FALSE) {
287:                 throw new FileNotFoundException("Unable to read file '$file'.");
288:             }
289:         } else {
290:             $content = (string) $content;
291:         }
292:         $part->setBody($content);
293:         $part->setContentType($contentType ? $contentType : MimeTypeDetector::fromString($content));
294:         $part->setEncoding(preg_match('#(multipart|message)/#A', $contentType) ? self::ENCODING_8BIT : self::ENCODING_BASE64);
295:         $part->setHeader('Content-Disposition', $disposition . '; filename="' . Strings::fixEncoding(basename($file)) . '"');
296:         return $part;
297:     }
298: 
299: 
300:     /********************* building and sending ****************d*g**/
301: 
302: 
303:     /**
304:      * Sends email.
305:      * @return void
306:      */
307:     public function send()
308:     {
309:         $this->getMailer()->send($this->build());
310:     }
311: 
312: 
313:     /**
314:      * Sets the mailer.
315:      * @return self
316:      */
317:     public function setMailer(IMailer $mailer)
318:     {
319:         $this->mailer = $mailer;
320:         return $this;
321:     }
322: 
323: 
324:     /**
325:      * Returns mailer.
326:      * @return IMailer
327:      */
328:     public function getMailer()
329:     {
330:         if ($this->mailer === NULL) {
331:             $this->mailer = is_object(self::$defaultMailer) ? self::$defaultMailer : new self::$defaultMailer;
332:         }
333:         return $this->mailer;
334:     }
335: 
336: 
337:     /**
338:      * Returns encoded message.
339:      * @return string
340:      */
341:     public function generateMessage()
342:     {
343:         if ($this->getHeader('Message-ID')) {
344:             return parent::generateMessage();
345:         } else {
346:             return $this->build()->generateMessage();
347:         }
348:     }
349: 
350: 
351:     /**
352:      * Builds email. Does not modify itself, but returns a new object.
353:      * @return Mail
354:      */
355:     protected function build()
356:     {
357:         $mail = clone $this;
358:         $mail->setHeader('Message-ID', $this->getRandomId());
359: 
360:         $mail->buildHtml();
361:         $mail->buildText();
362: 
363:         $cursor = $mail;
364:         if ($mail->attachments) {
365:             $tmp = $cursor->setContentType('multipart/mixed');
366:             $cursor = $cursor->addPart();
367:             foreach ($mail->attachments as $value) {
368:                 $tmp->addPart($value);
369:             }
370:         }
371: 
372:         if ($mail->html != NULL) { // intentionally ==
373:             $tmp = $cursor->setContentType('multipart/alternative');
374:             $cursor = $cursor->addPart();
375:             $alt = $tmp->addPart();
376:             if ($mail->inlines) {
377:                 $tmp = $alt->setContentType('multipart/related');
378:                 $alt = $alt->addPart();
379:                 foreach ($mail->inlines as $name => $value) {
380:                     $tmp->addPart($value);
381:                 }
382:             }
383:             $alt->setContentType('text/html', 'UTF-8')
384:                 ->setEncoding(preg_match('#\S{990}#', $mail->html)
385:                     ? self::ENCODING_QUOTED_PRINTABLE
386:                     : (preg_match('#[\x80-\xFF]#', $mail->html) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
387:                 ->setBody($mail->html);
388:         }
389: 
390:         $text = $mail->getBody();
391:         $mail->setBody(NULL);
392:         $cursor->setContentType('text/plain', 'UTF-8')
393:             ->setEncoding(preg_match('#\S{990}#', $text)
394:                 ? self::ENCODING_QUOTED_PRINTABLE
395:                 : (preg_match('#[\x80-\xFF]#', $text) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
396:             ->setBody($text);
397: 
398:         return $mail;
399:     }
400: 
401: 
402:     /**
403:      * Builds HTML content.
404:      * @return void
405:      */
406:     protected function buildHtml()
407:     {
408:         if ($this->html instanceof ITemplate) {
409:             $this->html->mail = $this;
410:             if ($this->basePath === NULL && $this->html instanceof IFileTemplate) {
411:                 $this->basePath = dirname($this->html->getFile());
412:             }
413:             $this->html = $this->html->__toString(TRUE);
414:         }
415: 
416:         if ($this->basePath !== FALSE) {
417:             $cids = array();
418:             $matches = Strings::matchAll(
419:                 $this->html,
420:                 '#(src\s*=\s*|background\s*=\s*|url\()(["\'])(?![a-z]+:|[/\\#])(.+?)\\2#i',
421:                 PREG_OFFSET_CAPTURE
422:             );
423:             foreach (array_reverse($matches) as $m) {
424:                 $file = rtrim($this->basePath, '/\\') . '/' . $m[3][0];
425:                 if (!isset($cids[$file])) {
426:                     $cids[$file] = substr($this->addEmbeddedFile($file)->getHeader("Content-ID"), 1, -1);
427:                 }
428:                 $this->html = substr_replace($this->html,
429:                     "{$m[1][0]}{$m[2][0]}cid:{$cids[$file]}{$m[2][0]}",
430:                     $m[0][1], strlen($m[0][0])
431:                 );
432:             }
433:         }
434: 
435:         if (!$this->getSubject() && $matches = Strings::match($this->html, '#<title>(.+?)</title>#is')) {
436:             $this->setSubject(html_entity_decode($matches[1], ENT_QUOTES, 'UTF-8'));
437:         }
438:     }
439: 
440: 
441:     /**
442:      * Builds text content.
443:      * @return void
444:      */
445:     protected function buildText()
446:     {
447:         $text = $this->getBody();
448:         if ($text instanceof ITemplate) {
449:             $text->mail = $this;
450:             $this->setBody($text->__toString(TRUE));
451: 
452:         } elseif ($text == NULL && $this->html != NULL) { // intentionally ==
453:             $text = Strings::replace($this->html, array(
454:                 '#<(style|script|head).*</\\1>#Uis' => '',
455:                 '#<t[dh][ >]#i' => " $0",
456:                 '#[\r\n]+#' => ' ',
457:                 '#<(/?p|/?h\d|li|br|/tr)[ >/]#i' => "\n$0",
458:             ));
459:             $text = html_entity_decode(strip_tags($text), ENT_QUOTES, 'UTF-8');
460:             $text = Strings::replace($text, '#[ \t]+#', ' ');
461:             $this->setBody(trim($text));
462:         }
463:     }
464: 
465: 
466:     /** @return string */
467:     private function getRandomId()
468:     {
469:         return '<' . Strings::random() . '@' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST']
470:             : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost'))
471:             . '>';
472:     }
473: 
474: }
475: 
Nette Framework 2.0.11 (for PHP 5.2, un-prefixed) API API documentation generated by ApiGen 2.8.0