Namespaces

  • 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

  • Message
  • MimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

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