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

Classes

  • Message
  • MimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

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