Overview

Namespaces

  • Jyxo
    • Beholder
      • TestCase
    • Gettext
      • Parser
    • Input
      • Chain
      • Filter
      • Validator
    • Mail
      • Email
        • Attachment
      • Parser
      • Sender
    • Rpc
      • Json
      • Xml
    • Shell
    • Spl
    • Svn
    • Time
    • Webdav
  • PHP

Classes

  • Email
  • Encoding
  • Parser
  • Sender
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * Jyxo PHP Library
  5:  *
  6:  * LICENSE
  7:  *
  8:  * This source file is subject to the new BSD license that is bundled
  9:  * with this package in the file license.txt.
 10:  * It is also available through the world-wide-web at this URL:
 11:  * https://github.com/jyxo/php/blob/master/license.txt
 12:  */
 13: 
 14: namespace Jyxo\Mail;
 15: 
 16: /**
 17:  * Class for sending emails.
 18:  * Based on PhpMailer class (C) Copyright 2001-2003  Brent R. Matzelle
 19:  *
 20:  * @category Jyxo
 21:  * @package Jyxo\Mail
 22:  * @subpackage Sender
 23:  * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
 24:  * @license https://github.com/jyxo/php/blob/master/license.txt
 25:  * @author Jaroslav HanslĂ­k
 26:  */
 27: class Sender
 28: {
 29:     /**
 30:      * Send using the internal mail() function.
 31:      *
 32:      * @var string
 33:      */
 34:     const MODE_MAIL = 'mail';
 35: 
 36:     /**
 37:      * Send using a SMTP server.
 38:      *
 39:      * @var string
 40:      */
 41:     const MODE_SMTP = 'smtp';
 42: 
 43:     /**
 44:      * No sending.
 45:      * Useful if we actually don't want to send the message but just generate it.
 46:      *
 47:      * @var string
 48:      */
 49:     const MODE_NONE = 'none';
 50: 
 51:     /**
 52:      * Maximum line length.
 53:      *
 54:      * @var integer
 55:      */
 56:     const LINE_LENGTH = 74;
 57: 
 58:     /**
 59:      * Line ending.
 60:      *
 61:      * @var string
 62:      */
 63:     const LINE_END = "\n";
 64: 
 65:     /**
 66:      * Simple mail type.
 67:      *
 68:      * @var string
 69:      */
 70:     const TYPE_SIMPLE = 'simple';
 71: 
 72:     /**
 73:      * Email with a HTML and plaintext part.
 74:      *
 75:      * @var string
 76:      */
 77:     const TYPE_ALTERNATIVE = 'alternative';
 78: 
 79:     /**
 80:      * Email with a HTML and plaintext part and attachments.
 81:      *
 82:      * @var string
 83:      */
 84:     const TYPE_ALTERNATIVE_ATTACHMENTS = 'alternative_attachments';
 85: 
 86:     /**
 87:      * Email with attachments.
 88:      *
 89:      * @var string
 90:      */
 91:     const TYPE_ATTACHMENTS = 'attachments';
 92: 
 93:     /**
 94:      * Charset.
 95:      *
 96:      * @var string
 97:      */
 98:     private $charset = 'iso-8859-2';
 99: 
100:     /**
101:      * Hostname.
102:      *
103:      * @var string
104:      */
105:     private $hostname = '';
106: 
107:     /**
108:      * X-Mailer header value.
109:      *
110:      * @var string
111:      */
112:     private $xmailer = '';
113: 
114:     /**
115:      * Mail encoding (8bit, 7bit, binary, base64, quoted-printable).
116:      *
117:      * @var string
118:      */
119:     private $encoding = Encoding::QUOTED_PRINTABLE;
120: 
121:     /**
122:      * Email instance to be sent.
123:      *
124:      * @var \Jyxo\Mail\Email
125:      */
126:     private $email = null;
127: 
128:     /**
129:      * SMTP server.
130:      *
131:      * @var string
132:      */
133:     private $smtpHost = 'localhost';
134: 
135:     /**
136:      * SMTP port.
137:      *
138:      * @var integer
139:      */
140:     private $smtpPort = 25;
141: 
142:     /**
143:      * SMTP HELO value.
144:      *
145:      * @var string
146:      */
147:     private $smtpHelo = '';
148: 
149:     /**
150:      * SMTP username.
151:      *
152:      * @var string
153:      */
154:     private $smtpUser = '';
155: 
156:     /**
157:      * SMTP password.
158:      *
159:      * @var string
160:      */
161:     private $smtpPsw = '';
162: 
163:     /**
164:      * SMTP connection timeout.
165:      *
166:      * @var string
167:      */
168:     private $smtpTimeout = 5;
169: 
170:     /**
171:      * Sending result.
172:      *
173:      * @var \Jyxo\Mail\Sender\Result
174:      */
175:     private $result = null;
176: 
177:     /**
178:      * Generated boundaries of mail parts.
179:      *
180:      * @var array
181:      */
182:     private $boundary = array();
183: 
184:     /**
185:      * Sending mode.
186:      *
187:      * @var integer
188:      */
189:     private $mode = self::MODE_MAIL;
190: 
191:     /**
192:      * Email type.
193:      *
194:      * @var string
195:      */
196:     private $type = self::TYPE_SIMPLE;
197: 
198:     /**
199:      * Generated email headers.
200:      *
201:      * @var array
202:      */
203:     private $createdHeader = array();
204: 
205:     /**
206:      * Generated email body.
207:      *
208:      * @var string
209:      */
210:     private $createdBody = '';
211: 
212:     /**
213:      * Returns charset.
214:      *
215:      * @return string
216:      */
217:     public function getCharset()
218:     {
219:         return $this->charset;
220:     }
221: 
222:     /**
223:      * Sets charset.
224:      *
225:      * @param string $charset Final charset
226:      * @return \Jyxo\Mail\Sender
227:      */
228:     public function setCharset($charset)
229:     {
230:         $this->charset = (string) $charset;
231: 
232:         return $this;
233:     }
234: 
235:     /**
236:      * Returns hostname.
237:      *
238:      * @return string
239:      */
240:     public function getHostname()
241:     {
242:         if (empty($this->hostname)) {
243:             $this->hostname = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
244:         }
245: 
246:         return $this->hostname;
247:     }
248: 
249:     /**
250:      * Sets hostname.
251:      *
252:      * @param string $hostname Hostname
253:      * @return \Jyxo\Mail\Sender
254:      */
255:     public function setHostname($hostname)
256:     {
257:         $this->hostname = (string) $hostname;
258: 
259:         return $this;
260:     }
261: 
262:     /**
263:      * Returns X-Mailer header value.
264:      *
265:      * @return string
266:      */
267:     public function getXmailer()
268:     {
269:         return $this->xmailer;
270:     }
271: 
272:     /**
273:      * Sets X-Mailer header value.
274:      *
275:      * @param string $xmailer X-Mailer header value
276:      * @return \Jyxo\Mail\Sender
277:      */
278:     public function setXmailer($xmailer)
279:     {
280:         $this->xmailer = (string) $xmailer;
281: 
282:         return $this;
283:     }
284: 
285:     /**
286:      * Returns encoding.
287:      *
288:      * @return string
289:      */
290:     public function getEncoding()
291:     {
292:         return $this->encoding;
293:     }
294: 
295:     /**
296:      * Sets encoding.
297:      *
298:      * @param string $encoding Encoding
299:      * @return \Jyxo\Mail\Sender
300:      * @throws \InvalidArgumentException If an incompatible encoding was provided
301:      */
302:     public function setEncoding($encoding)
303:     {
304:         if (!Encoding::isCompatible($encoding)) {
305:             throw new \InvalidArgumentException(sprintf('Incompatible encoding %s.', $encoding));
306:         }
307: 
308:         $this->encoding = (string) $encoding;
309: 
310:         return $this;
311:     }
312: 
313:     /**
314:      * Returns the email to be sent.
315:      *
316:      * @return \Jyxo\Mail\Email
317:      */
318:     public function getEmail()
319:     {
320:         return $this->email;
321:     }
322: 
323:     /**
324:      * Sets the email to be sent.
325:      *
326:      * @param \Jyxo\Mail\Email $email Email instance
327:      * @return \Jyxo\Mail\Sender
328:      */
329:     public function setEmail(\Jyxo\Mail\Email $email)
330:     {
331:         $this->email = $email;
332: 
333:         return $this;
334:     }
335: 
336:     /**
337:      * Sets SMTP parameters.
338:      *
339:      * @param string $host Hostname
340:      * @param integer $port Port
341:      * @param string $helo HELO value
342:      * @param string $user Username
343:      * @param string $password Password
344:      * @param integer $timeout Connection timeout
345:      * @return \Jyxo\Mail\Sender
346:      */
347:     public function setSmtp($host, $port = 25, $helo = '', $user = '', $password = '', $timeout = 5)
348:     {
349:         $this->smtpHost = (string) $host;
350:         $this->smtpPort = (int) $port;
351:         $this->smtpHelo = !empty($helo) ? (string) $helo : $this->getHostname();
352:         $this->smtpUser = (string) $user;
353:         $this->smtpPsw = (string) $password;
354:         $this->smtpTimeout = (int) $timeout;
355: 
356:         return $this;
357:     }
358: 
359:     /**
360:      * Sends an email using the given mode.
361:      *
362:      * @param string $mode Sending mode
363:      * @return \Jyxo\Mail\Sender\Result
364:      * @throws \InvalidArgumentException If an unknown mode was requested
365:      * @throws \Jyxo\Mail\Sender\Exception On error
366:      * @throws \Jyxo\Mail\Sender\CreateException If a required setting is missing
367:      */
368:     public function send($mode)
369:     {
370:         // Sending modes
371:         static $modes = array(
372:             self::MODE_SMTP => true,
373:             self::MODE_MAIL => true,
374:             self::MODE_NONE => true
375:         );
376:         if (!isset($modes[$mode])) {
377:             throw new \InvalidArgumentException(sprintf('Unknown sending mode %s.', $mode));
378:         }
379:         $this->mode = (string) $mode;
380: 
381:         // Check of required parameters
382:         if (null === $this->email->from) {
383:             throw new Sender\CreateException('No sender was set.');
384:         }
385:         if ((count($this->email->to) + count($this->email->cc) + count($this->email->bcc)) < 1) {
386:             throw new Sender\CreateException('No recipient was set.');
387:         }
388: 
389:         // Creates a result
390:         $this->result = new Sender\Result();
391: 
392:         // Creates an email
393:         $this->create();
394:         $body = trim($this->createdBody);
395:         if (empty($body)) {
396:             throw new Sender\CreateException('No body was created.');
397:         }
398: 
399:         // Choose the appropriate sending method
400:         switch ($this->mode) {
401:             case self::MODE_SMTP:
402:                 $this->sendBySmtp();
403:                 break;
404:             case self::MODE_MAIL:
405:                 $this->sendByMail();
406:                 break;
407:             case self::MODE_NONE:
408:                 // Break missing intentionally
409:             default:
410:                 // No sending
411:                 break;
412:         }
413: 
414:         // Save the generated source code to the result
415:         $this->result->source = $this->getHeader() . $this->createdBody;
416: 
417:         // Flush of created email
418:         $this->createdHeader = array();
419:         $this->createdBody = '';
420: 
421:         return $this->result;
422:     }
423: 
424:     /**
425:      * Sends an email using the mail() function.
426:      *
427:      * @throws \Jyxo\Mail\Sender\Exception On error
428:      */
429:     private function sendByMail()
430:     {
431:         $recipients = '';
432:         $iterator = new \ArrayIterator($this->email->to);
433:         while ($iterator->valid()) {
434:             $recipients .= $this->formatAddress($iterator->current());
435: 
436:             $iterator->next();
437:             if ($iterator->valid()) {
438:                 $recipients .= ', ';
439:             }
440:         }
441: 
442:         $subject = $this->changeCharset($this->clearHeaderValue($this->email->subject));
443: 
444:         if (!mail($recipients, $this->encodeHeader($subject), $this->createdBody, $this->getHeader(array('To', 'Subject')))) {
445:             throw new Sender\Exception('Could not send the message.');
446:         }
447:     }
448: 
449:     /**
450:      * Sends an email using a SMTP server.
451:      *
452:      * @throws \Jyxo\Mail\Sender\Exception On error
453:      */
454:     private function sendBySmtp()
455:     {
456:         if (!class_exists('\Jyxo\Mail\Sender\Smtp')) {
457:             throw new Sender\Exception('Could not sent the message. Required class \Jyxo\Mail\Sender\Smtp is missing.');
458:         }
459: 
460:         try {
461:             $smtp = new Sender\Smtp($this->smtpHost, $this->smtpPort, $this->smtpHelo, $this->smtpTimeout);
462:             $smtp->connect();
463:             if (!empty($this->smtpUser)) {
464:                 $smtp->auth($this->smtpUser, $this->smtpPsw);
465:             }
466: 
467:             // Sender
468:             $smtp->from($this->email->from->email);
469: 
470:             // Recipients
471:             $unknownRecipients = array();
472:             foreach (array_merge($this->email->to, $this->email->cc, $this->email->bcc) as $recipient) {
473:                 try {
474:                     $smtp->recipient($recipient->email);
475:                 } catch (\Jyxo\Mail\Sender\SmtpException $e) {
476:                     $unknownRecipients[] = $recipient->email;
477:                 }
478:             }
479:             if (!empty($unknownRecipients)) {
480:                 throw new Sender\RecipientUnknownException('Unknown email recipients.', 0, $unknownRecipients);
481:             }
482: 
483:             // Data
484:             $smtp->data($this->getHeader(), $this->createdBody);
485:             $smtp->disconnect();
486:         } catch (\Jyxo\Mail\Sender\RecipientUnknownException $e) {
487:             $smtp->disconnect();
488:             throw $e;
489:         } catch (\Jyxo\Mail\Sender\SmtpException $e) {
490:             $smtp->disconnect();
491:             throw new Sender\Exception('Cannot send email: ' . $e->getMessage());
492:         }
493:     }
494: 
495:     /**
496:      * Creates an email.
497:      */
498:     private function create()
499:     {
500:         $uniqueId = md5(uniqid(time()));
501:         $hostname = $this->clearHeaderValue($this->getHostname());
502: 
503:         // Unique email Id
504:         $this->result->messageId = $uniqueId . '@' . $hostname;
505: 
506:         // Sending time
507:         $this->result->datetime = \Jyxo\Time\Time::now();
508: 
509:         // Parts boundaries
510:         $this->boundary = array(
511:             1 => '====b1' . $uniqueId . '====' . $hostname . '====',
512:             2 => '====b2' . $uniqueId . '====' . $hostname . '===='
513:         );
514: 
515:         // Determine the message type
516:         if (!empty($this->email->attachments)) {
517:             // Are there any attachments?
518:             if (!empty($this->email->body->alternative)) {
519:                 // There is an alternative content
520:                 $this->type = self::TYPE_ALTERNATIVE_ATTACHMENTS;
521:             } else {
522:                 // No alternative content
523:                 $this->type = self::TYPE_ATTACHMENTS;
524:             }
525:         } else {
526:             // No attachments
527:             if (!empty($this->email->body->alternative)) {
528:                 // There is an alternative content
529:                 $this->type = self::TYPE_ALTERNATIVE;
530:             } else {
531:                 // No alternative content
532:                 $this->type = self::TYPE_SIMPLE;
533:             }
534:         }
535: 
536:         // Creates header and body
537:         $this->createHeader();
538:         $this->createBody();
539:     }
540: 
541:     /**
542:      * Creates header.
543:      */
544:     private function createHeader()
545:     {
546:         $this->addHeaderLine('Date', $this->result->datetime->email);
547:         $this->addHeaderLine('Return-Path', '<' . $this->clearHeaderValue($this->email->from->email) . '>');
548: 
549:         $this->addHeaderLine('From', $this->formatAddress($this->email->from));
550:         $this->addHeaderLine('Subject', $this->encodeHeader($this->changeCharset($this->clearHeaderValue($this->email->subject))));
551: 
552:         if (!empty($this->email->to)) {
553:             $this->addHeaderLine('To', $this->formatAddressList($this->email->to));
554:         } elseif (empty($this->email->cc)) {
555:             // Only blind carbon copy recipients
556:             $this->addHeaderLine('To', 'undisclosed-recipients:;');
557:         }
558: 
559:         if (!empty($this->email->cc)) {
560:             $this->addHeaderLine('Cc', $this->formatAddressList($this->email->cc));
561:         }
562:         if (!empty($this->email->bcc)) {
563:             $this->addHeaderLine('Bcc', $this->formatAddressList($this->email->bcc));
564:         }
565:         if (!empty($this->email->replyTo)) {
566:             $this->addHeaderLine('Reply-To', $this->formatAddressList($this->email->replyTo));
567:         }
568: 
569:         if (!empty($this->email->confirmReadingTo)) {
570:             $this->addHeaderLine('Disposition-Notification-To', $this->formatAddress($this->email->confirmReadingTo));
571:         }
572: 
573:         if (!empty($this->email->priority)) {
574:             $this->addHeaderLine('X-Priority', $this->email->priority);
575:         }
576: 
577:         $this->addHeaderLine('Message-ID', '<' . $this->result->messageId . '>');
578: 
579:         if (!empty($this->email->inReplyTo)) {
580:             $this->addHeaderLine('In-Reply-To', '<' . $this->clearHeaderValue($this->email->inReplyTo) . '>');
581:         }
582:         if (!empty($this->email->references)) {
583:             $references = $this->email->references;
584:             foreach ($references as $key => $reference) {
585:                 $references[$key] = $this->clearHeaderValue($reference);
586:             }
587:             $this->addHeaderLine('References', '<' . implode('> <', $references) . '>');
588:         }
589: 
590:         if (!empty($this->xmailer)) {
591:             $this->addHeaderLine('X-Mailer', $this->changeCharset($this->clearHeaderValue($this->xmailer)));
592:         }
593: 
594:         $this->addHeaderLine('MIME-Version', '1.0');
595: 
596:         // Custom headers
597:         foreach ($this->email->headers as $header) {
598:             $this->addHeaderLine($this->changeCharset($this->clearHeaderValue($header->name)), $this->encodeHeader($this->clearHeaderValue($header->value)));
599:         }
600: 
601:         switch ($this->type) {
602:             case self::TYPE_ATTACHMENTS:
603:                 // Break missing intentionally
604:             case self::TYPE_ALTERNATIVE_ATTACHMENTS:
605:                 $subtype = $this->email->hasInlineAttachments() ? 'related' : 'mixed';
606:                 $this->addHeaderLine('Content-Type', 'multipart/' . $subtype . ';' . self::LINE_END . ' boundary="' . $this->boundary[1] . '"');
607:                 break;
608:             case self::TYPE_ALTERNATIVE:
609:                 $this->addHeaderLine('Content-Type', 'multipart/alternative;' . self::LINE_END . ' boundary="' . $this->boundary[1] . '"');
610:                 break;
611:             case self::TYPE_SIMPLE:
612:                 // Break missing intentionally
613:             default:
614:                 $contentType = $this->email->body->isHtml() ? 'text/html' : 'text/plain';
615: 
616:                 $this->addHeaderLine('Content-Type', $contentType . '; charset="' . $this->clearHeaderValue($this->charset) . '"');
617:                 $this->addHeaderLine('Content-Transfer-Encoding', $this->encoding);
618:                 break;
619:         }
620:     }
621: 
622:     /**
623:      * Creates body.
624:      */
625:     private function createBody()
626:     {
627:         switch ($this->type) {
628:             case self::TYPE_ATTACHMENTS:
629:                 $contentType = $this->email->body->isHtml() ? 'text/html' : 'text/plain';
630: 
631:                 $this->createdBody .= $this->getBoundaryStart($this->boundary[1], $contentType, $this->charset, $this->encoding) . self::LINE_END;
632:                 $this->createdBody .= $this->encodeString($this->changeCharset($this->email->body->main), $this->encoding) . self::LINE_END;
633: 
634:                 $this->createdBody .= $this->attachAll();
635:                 break;
636:             case self::TYPE_ALTERNATIVE_ATTACHMENTS:
637:                 $this->createdBody .= '--' . $this->boundary[1] . self::LINE_END;
638:                 $this->createdBody .= 'Content-Type: multipart/alternative;' . self::LINE_END . ' boundary="' . $this->boundary[2] . '"' . self::LINE_END . self::LINE_END;
639:                 $this->createdBody .= $this->getBoundaryStart($this->boundary[2], 'text/plain', $this->charset, $this->encoding) . self::LINE_END;
640:                 $this->createdBody .= $this->encodeString($this->changeCharset($this->email->body->alternative), $this->encoding) . self::LINE_END;
641:                 $this->createdBody .= $this->getBoundaryStart($this->boundary[2], 'text/html', $this->charset, $this->encoding) . self::LINE_END;
642:                 $this->createdBody .= $this->encodeString($this->changeCharset($this->email->body->main), $this->encoding) . self::LINE_END;
643:                 $this->createdBody .= $this->getBoundaryEnd($this->boundary[2]) . self::LINE_END;
644: 
645:                 $this->createdBody .= $this->attachAll();
646:                 break;
647:             case self::TYPE_ALTERNATIVE:
648:                 $this->createdBody .= $this->getBoundaryStart($this->boundary[1], 'text/plain', $this->charset, $this->encoding) . self::LINE_END;
649:                 $this->createdBody .= $this->encodeString($this->changeCharset($this->email->body->alternative), $this->encoding) . self::LINE_END;
650:                 $this->createdBody .= $this->getBoundaryStart($this->boundary[1], 'text/html', $this->charset, $this->encoding) . self::LINE_END;
651:                 $this->createdBody .= $this->encodeString($this->changeCharset($this->email->body->main), $this->encoding) . self::LINE_END;
652:                 $this->createdBody .= $this->getBoundaryEnd($this->boundary[1]);
653:                 break;
654:             case self::TYPE_SIMPLE:
655:                 // Break missing intentionally
656:             default:
657:                 $this->createdBody = $this->encodeString($this->changeCharset($this->email->body->main), $this->encoding);
658:                 break;
659:         }
660:     }
661: 
662:     /**
663:      * Adds all attachments to the email.
664:      *
665:      * @return string
666:      */
667:     private function attachAll()
668:     {
669:         $mime = array();
670: 
671:         foreach ($this->email->attachments as $attachment) {
672:             $encoding = !empty($attachment->encoding) ? $attachment->encoding : Encoding::BASE64;
673:             $name = $this->changeCharset($this->clearHeaderValue($attachment->name));
674: 
675:             $mime[] = '--' . $this->boundary[1] . self::LINE_END;
676:             $mime[] = 'Content-Type: ' . $this->clearHeaderValue($attachment->mimeType) . ';' . self::LINE_END;
677:             $mime[] = ' name="' .  $this->encodeHeader($name) . '"' . self::LINE_END;
678:             $mime[] = 'Content-Transfer-Encoding: ' . $encoding . self::LINE_END;
679: 
680:             if ($attachment->isInline()) {
681:                 $mime[] = 'Content-ID: <' . $this->clearHeaderValue($attachment->cid) . '>' . self::LINE_END;
682:             }
683: 
684:             $mime[] = 'Content-Disposition: ' . $attachment->disposition . ';' . self::LINE_END;
685:             $mime[] = ' filename="' . $this->encodeHeader($name) . '"' . self::LINE_END . self::LINE_END;
686: 
687:             // Just fix line endings in case of encoded attachments, encode otherwise
688:             $mime[] = !empty($attachment->encoding)
689:                 ? \Jyxo\String::fixLineEnding($attachment->content, self::LINE_END)
690:                 : $this->encodeString($attachment->content, $encoding);
691:             $mime[] = self::LINE_END . self::LINE_END;
692:         }
693: 
694:         $mime[] = '--' . $this->boundary[1] . '--' . self::LINE_END;
695: 
696:         return implode('', $mime);
697:     }
698: 
699:     /**
700:      * Returns headers except given lines.
701:      * Various sending methods need some headers removed, because they add them on their own.
702:      *
703:      * @param array $except Headers to be removed
704:      * @return string
705:      */
706:     private function getHeader(array $except = array())
707:     {
708:         $header = '';
709:         foreach ($this->createdHeader as $headerLine) {
710:             if (!in_array($headerLine['name'], $except)) {
711:                 $header .= $headerLine['name'] . ': ' . $headerLine['value'] . self::LINE_END;
712:             }
713:         }
714: 
715:         return $header . self::LINE_END;
716:     }
717: 
718:     /**
719:      * Formats an email address.
720:      *
721:      * @param \Jyxo\Mail\Email\Address $address Address
722:      * @return string
723:      */
724:     private function formatAddress(\Jyxo\Mail\Email\Address $address)
725:     {
726:         $name = $this->changeCharset($this->clearHeaderValue($address->name));
727:         $email = $this->clearHeaderValue($address->email);
728: 
729:         // No name is set
730:         if ((empty($name)) || ($name === $email)) {
731:             return $email;
732:         }
733: 
734:         if (preg_match('~[\200-\377]~', $name)) {
735:             // High ascii
736:             $name = $this->encodeHeader($name);
737:         } elseif (preg_match('~[^\w\s!#\$%&\'*+/=?^_`{|}\~-]~', $name)) {
738:             // Dangerous characters
739:             $name = '"' . $name . '"';
740:         }
741: 
742:         return $name . ' <' . $email . '>';
743:     }
744: 
745:     /**
746:      * Formats a list of addresses.
747:      *
748:      * @param array $addressList Array of addresses
749:      * @return string
750:      */
751:     private function formatAddressList(array $addressList)
752:     {
753:         $formated = '';
754:         while ($address = current($addressList)) {
755:             $formated .= $this->formatAddress($address);
756: 
757:             if (false !== next($addressList)) {
758:                 $formated .= ', ';
759:             }
760:         }
761:         return $formated;
762:     }
763: 
764:     /**
765:      * Adds a header line.
766:      *
767:      * @param string $name Header name
768:      * @param string $value Header value
769:      */
770:     private function addHeaderLine($name, $value)
771:     {
772:         $this->createdHeader[] = array(
773:             'name' => $name,
774:             'value' => trim($value)
775:         );
776:     }
777: 
778:     /**
779:      * Encodes headers.
780:      *
781:      * @param string $string Header definition
782:      * @return string
783:      */
784:     private function encodeHeader($string)
785:     {
786:         // There might be dangerous characters in the string
787:         $count = preg_match_all('~[^\040-\176]~', $string, $matches);
788:         if (0 === $count) {
789:             return $string;
790:         }
791: 
792:         // 7 is =? + ? + Q/B + ? + ?=
793:         $maxlen = 75 - 7 - strlen($this->charset);
794: 
795:         // Uses encoding with shorter output
796:         /*
797:         if (mb_strlen($string, $this->charset) / 3 < $count) {
798:             $encoding = 'B';
799:             $maxlen -= $maxlen % 4;
800:             $encoded = $this->encodeString($string, \Jyxo\Mail\Encoding::BASE64, $maxlen);
801:         } else {
802:             $encoding = 'Q';
803:             $encoded = $this->encodeString($string, \Jyxo\Mail\Encoding::QUOTED_PRINTABLE);
804:             $encoded = str_replace(array('?', ' '), array('=3F', '=20'), $encoded);
805:         }
806:         */
807: 
808:         // We have to use base64 always, because Thunderbird has problems with quoted-printable
809:         $encoding = 'B';
810:         $maxlen -= $maxlen % 4;
811:         $encoded = $this->encodeString($string, Encoding::BASE64, $maxlen);
812: 
813:         // Splitting to multiple lines
814:         $encoded = trim(preg_replace('~^(.*)$~m', ' =?' . $this->clearHeaderValue($this->charset) . '?' . $encoding . '?\1?=', $encoded));
815: 
816:         return $encoded;
817:     }
818: 
819:     /**
820:      * Encodes a string using the given encoding.
821:      *
822:      * @param string $string Input string
823:      * @param string $encoding Encoding
824:      * @param integer $lineLength Line length
825:      * @return string
826:      */
827:     private function encodeString($string, $encoding, $lineLength = self::LINE_LENGTH)
828:     {
829:         return Encoding::encode($string, $encoding, $lineLength, self::LINE_END);
830:     }
831: 
832:     /**
833:      * Returns a beginning of an email part.
834:      *
835:      * @param string $boundary Boundary
836:      * @param string $contentType Mime-type
837:      * @param string $charset Charset
838:      * @param string $encoding Encoding
839:      * @return string
840:      */
841:     private function getBoundaryStart($boundary, $contentType, $charset, $encoding)
842:     {
843:         $start = '--' . $boundary . self::LINE_END;
844:         $start .= 'Content-Type: ' . $contentType . '; charset="' . $this->clearHeaderValue($charset) . '"' . self::LINE_END;
845:         $start .= 'Content-Transfer-Encoding: ' . $encoding . self::LINE_END;
846: 
847:         return $start;
848:     }
849: 
850:     /**
851:      * Returns an end of an email part.
852:      *
853:      * @param string $boundary Boundary
854:      * @return string
855:      */
856:     private function getBoundaryEnd($boundary)
857:     {
858:         return self::LINE_END . '--' . $boundary . '--' . self::LINE_END;
859:     }
860: 
861:     /**
862:      * Clears headers from line endings.
863:      *
864:      * @param string $string Headers definition
865:      * @return string
866:      */
867:     private function clearHeaderValue($string)
868:     {
869:         return strtr(trim($string), "\r\n\t", '   ');
870:     }
871: 
872:     /**
873:      * Converts a string from UTF-8 into the email encoding.
874:      *
875:      * @param string $string Input string
876:      * @return string
877:      */
878:     private function changeCharset($string)
879:     {
880:         if ('utf-8' !== $this->charset) {
881:             // Triggers a notice on an invalid character
882:             $string = @iconv('utf-8', $this->charset . '//TRANSLIT', $string);
883:         }
884: 
885:         return $string;
886:     }
887: }
888: 
Jyxo PHP Library API documentation generated by ApiGen 2.3.0