Namespaces

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