Open Monograph Press  3.3.0
Email.php
1 <?php
2 
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11 
12 namespace Symfony\Component\Mime;
13 
21 
25 class Email extends Message
26 {
27  const PRIORITY_HIGHEST = 1;
28  const PRIORITY_HIGH = 2;
29  const PRIORITY_NORMAL = 3;
30  const PRIORITY_LOW = 4;
31  const PRIORITY_LOWEST = 5;
32 
33  private const PRIORITY_MAP = [
34  self::PRIORITY_HIGHEST => 'Highest',
35  self::PRIORITY_HIGH => 'High',
36  self::PRIORITY_NORMAL => 'Normal',
37  self::PRIORITY_LOW => 'Low',
38  self::PRIORITY_LOWEST => 'Lowest',
39  ];
40 
41  private $text;
42  private $textCharset;
43  private $html;
44  private $htmlCharset;
45  private $attachments = [];
46 
50  public function subject(string $subject)
51  {
52  return $this->setHeaderBody('Text', 'Subject', $subject);
53  }
54 
55  public function getSubject(): ?string
56  {
57  return $this->getHeaders()->getHeaderBody('Subject');
58  }
59 
63  public function date(\DateTimeInterface $dateTime)
64  {
65  return $this->setHeaderBody('Date', 'Date', $dateTime);
66  }
67 
68  public function getDate(): ?\DateTimeImmutable
69  {
70  return $this->getHeaders()->getHeaderBody('Date');
71  }
72 
78  public function returnPath($address)
79  {
80  return $this->setHeaderBody('Path', 'Return-Path', Address::create($address));
81  }
82 
83  public function getReturnPath(): ?Address
84  {
85  return $this->getHeaders()->getHeaderBody('Return-Path');
86  }
87 
93  public function sender($address)
94  {
95  return $this->setHeaderBody('Mailbox', 'Sender', Address::create($address));
96  }
97 
98  public function getSender(): ?Address
99  {
100  return $this->getHeaders()->getHeaderBody('Sender');
101  }
102 
108  public function addFrom(...$addresses)
109  {
110  return $this->addListAddressHeaderBody('From', $addresses);
111  }
112 
118  public function from(...$addresses)
119  {
120  return $this->setListAddressHeaderBody('From', $addresses);
121  }
122 
126  public function getFrom(): array
127  {
128  return $this->getHeaders()->getHeaderBody('From') ?: [];
129  }
130 
136  public function addReplyTo(...$addresses)
137  {
138  return $this->addListAddressHeaderBody('Reply-To', $addresses);
139  }
140 
146  public function replyTo(...$addresses)
147  {
148  return $this->setListAddressHeaderBody('Reply-To', $addresses);
149  }
150 
154  public function getReplyTo(): array
155  {
156  return $this->getHeaders()->getHeaderBody('Reply-To') ?: [];
157  }
158 
164  public function addTo(...$addresses)
165  {
166  return $this->addListAddressHeaderBody('To', $addresses);
167  }
168 
174  public function to(...$addresses)
175  {
176  return $this->setListAddressHeaderBody('To', $addresses);
177  }
178 
182  public function getTo(): array
183  {
184  return $this->getHeaders()->getHeaderBody('To') ?: [];
185  }
186 
192  public function addCc(...$addresses)
193  {
194  return $this->addListAddressHeaderBody('Cc', $addresses);
195  }
196 
202  public function cc(...$addresses)
203  {
204  return $this->setListAddressHeaderBody('Cc', $addresses);
205  }
206 
210  public function getCc(): array
211  {
212  return $this->getHeaders()->getHeaderBody('Cc') ?: [];
213  }
214 
220  public function addBcc(...$addresses)
221  {
222  return $this->addListAddressHeaderBody('Bcc', $addresses);
223  }
224 
230  public function bcc(...$addresses)
231  {
232  return $this->setListAddressHeaderBody('Bcc', $addresses);
233  }
234 
238  public function getBcc(): array
239  {
240  return $this->getHeaders()->getHeaderBody('Bcc') ?: [];
241  }
242 
250  public function priority(int $priority)
251  {
252  if ($priority > 5) {
253  $priority = 5;
254  } elseif ($priority < 1) {
255  $priority = 1;
256  }
257 
258  return $this->setHeaderBody('Text', 'X-Priority', sprintf('%d (%s)', $priority, self::PRIORITY_MAP[$priority]));
259  }
260 
267  public function getPriority(): int
268  {
269  list($priority) = sscanf($this->getHeaders()->getHeaderBody('X-Priority'), '%[1-5]');
270 
271  return $priority ?? 3;
272  }
273 
279  public function text($body, string $charset = 'utf-8')
280  {
281  $this->text = $body;
282  $this->textCharset = $charset;
283 
284  return $this;
285  }
286 
290  public function getTextBody()
291  {
292  return $this->text;
293  }
294 
295  public function getTextCharset(): ?string
296  {
297  return $this->textCharset;
298  }
299 
305  public function html($body, string $charset = 'utf-8')
306  {
307  $this->html = $body;
308  $this->htmlCharset = $charset;
309 
310  return $this;
311  }
312 
316  public function getHtmlBody()
317  {
318  return $this->html;
319  }
320 
321  public function getHtmlCharset(): ?string
322  {
323  return $this->htmlCharset;
324  }
325 
331  public function attach($body, string $name = null, string $contentType = null)
332  {
333  $this->attachments[] = ['body' => $body, 'name' => $name, 'content-type' => $contentType, 'inline' => false];
334 
335  return $this;
336  }
337 
341  public function attachFromPath(string $path, string $name = null, string $contentType = null)
342  {
343  $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => false];
344 
345  return $this;
346  }
347 
353  public function embed($body, string $name = null, string $contentType = null)
354  {
355  $this->attachments[] = ['body' => $body, 'name' => $name, 'content-type' => $contentType, 'inline' => true];
356 
357  return $this;
358  }
359 
363  public function embedFromPath(string $path, string $name = null, string $contentType = null)
364  {
365  $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => true];
366 
367  return $this;
368  }
369 
373  public function attachPart(DataPart $part)
374  {
375  $this->attachments[] = ['part' => $part];
376 
377  return $this;
378  }
379 
383  public function getAttachments(): array
384  {
385  $parts = [];
386  foreach ($this->attachments as $attachment) {
387  $parts[] = $this->createDataPart($attachment);
388  }
389 
390  return $parts;
391  }
392 
393  public function getBody(): AbstractPart
394  {
395  if (null !== $body = parent::getBody()) {
396  return $body;
397  }
398 
399  return $this->generateBody();
400  }
401 
402  public function ensureValidity()
403  {
404  if (null === $this->text && null === $this->html && !$this->attachments) {
405  throw new LogicException('A message must have a text or an HTML part or attachments.');
406  }
407 
408  parent::ensureValidity();
409  }
410 
431  private function generateBody(): AbstractPart
432  {
433  $this->ensureValidity();
434 
435  [$htmlPart, $attachmentParts, $inlineParts] = $this->prepareParts();
436 
437  $part = null === $this->text ? null : new TextPart($this->text, $this->textCharset);
438  if (null !== $htmlPart) {
439  if (null !== $part) {
440  $part = new AlternativePart($part, $htmlPart);
441  } else {
442  $part = $htmlPart;
443  }
444  }
445 
446  if ($inlineParts) {
447  $part = new RelatedPart($part, ...$inlineParts);
448  }
449 
450  if ($attachmentParts) {
451  if ($part) {
452  $part = new MixedPart($part, ...$attachmentParts);
453  } else {
454  $part = new MixedPart(...$attachmentParts);
455  }
456  }
457 
458  return $part;
459  }
460 
461  private function prepareParts(): ?array
462  {
463  $names = [];
464  $htmlPart = null;
465  $html = $this->html;
466  if (null !== $this->html) {
467  if (\is_resource($html)) {
468  if (stream_get_meta_data($html)['seekable'] ?? false) {
469  rewind($html);
470  }
471 
472  $html = stream_get_contents($html);
473  }
474  $htmlPart = new TextPart($html, $this->htmlCharset, 'html');
475  preg_match_all('(<img\s+[^>]*src\s*=\s*(?:([\'"])cid:([^"]+)\\1|cid:([^>\s]+)))i', $html, $names);
476  $names = array_filter(array_unique(array_merge($names[2], $names[3])));
477  }
478 
479  $attachmentParts = $inlineParts = [];
480  foreach ($this->attachments as $attachment) {
481  foreach ($names as $name) {
482  if (isset($attachment['part'])) {
483  continue;
484  }
485  if ($name !== $attachment['name']) {
486  continue;
487  }
488  if (isset($inlineParts[$name])) {
489  continue 2;
490  }
491  $attachment['inline'] = true;
492  $inlineParts[$name] = $part = $this->createDataPart($attachment);
493  $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html);
494  continue 2;
495  }
496  $attachmentParts[] = $this->createDataPart($attachment);
497  }
498  if (null !== $htmlPart) {
499  $htmlPart = new TextPart($html, $this->htmlCharset, 'html');
500  }
501 
502  return [$htmlPart, $attachmentParts, array_values($inlineParts)];
503  }
504 
505  private function createDataPart(array $attachment): DataPart
506  {
507  if (isset($attachment['part'])) {
508  return $attachment['part'];
509  }
510 
511  if (isset($attachment['body'])) {
512  $part = new DataPart($attachment['body'], $attachment['name'] ?? null, $attachment['content-type'] ?? null);
513  } else {
514  $part = DataPart::fromPath($attachment['path'] ?? '', $attachment['name'] ?? null, $attachment['content-type'] ?? null);
515  }
516  if ($attachment['inline']) {
517  $part->asInline();
518  }
519 
520  return $part;
521  }
522 
526  private function setHeaderBody(string $type, string $name, $body)
527  {
528  $this->getHeaders()->setHeaderBody($type, $name, $body);
529 
530  return $this;
531  }
532 
533  private function addListAddressHeaderBody(string $name, array $addresses)
534  {
535  if (!$header = $this->getHeaders()->get($name)) {
536  return $this->setListAddressHeaderBody($name, $addresses);
537  }
538  $header->addAddresses(Address::createArray($addresses));
539 
540  return $this;
541  }
542 
543  private function setListAddressHeaderBody(string $name, array $addresses)
544  {
545  $addresses = Address::createArray($addresses);
546  $headers = $this->getHeaders();
547  if ($header = $headers->get($name)) {
548  $header->setAddresses($addresses);
549  } else {
550  $headers->addMailboxListHeader($name, $addresses);
551  }
552 
553  return $this;
554  }
555 
559  public function __serialize(): array
560  {
561  if (\is_resource($this->text)) {
562  if (stream_get_meta_data($this->text)['seekable'] ?? false) {
563  rewind($this->text);
564  }
565 
566  $this->text = stream_get_contents($this->text);
567  }
568 
569  if (\is_resource($this->html)) {
570  if (stream_get_meta_data($this->html)['seekable'] ?? false) {
571  rewind($this->html);
572  }
573 
574  $this->html = stream_get_contents($this->html);
575  }
576 
577  foreach ($this->attachments as $i => $attachment) {
578  if (isset($attachment['body']) && \is_resource($attachment['body'])) {
579  if (stream_get_meta_data($attachment['body'])['seekable'] ?? false) {
580  rewind($attachment['body']);
581  }
582 
583  $this->attachments[$i]['body'] = stream_get_contents($attachment['body']);
584  }
585  }
586 
587  return [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, parent::__serialize()];
588  }
589 
593  public function __unserialize(array $data): void
594  {
595  [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, $parentData] = $data;
596 
597  parent::__unserialize($parentData);
598  }
599 }
Symfony\Component\Mime\Part\Multipart\MixedPart
Definition: MixedPart.php:19
Symfony\Component\Mime\Email\addCc
addCc(... $addresses)
Definition: Email.php:192
Symfony\Component\Mime\Email\date
date(\DateTimeInterface $dateTime)
Definition: Email.php:63
Symfony\Component\Mime\Email\getSender
getSender()
Definition: Email.php:98
Symfony\Component\Mime\Address\create
static create($address)
Definition: Address.php:86
Symfony\Component\Mime\Email\embedFromPath
embedFromPath(string $path, string $name=null, string $contentType=null)
Definition: Email.php:363
Symfony\Component\Mime\Email\returnPath
returnPath($address)
Definition: Email.php:78
Symfony\Component\Mime\Email\getAttachments
getAttachments()
Definition: Email.php:383
Symfony\Component\Mime\Email\PRIORITY_HIGHEST
const PRIORITY_HIGHEST
Definition: Email.php:27
Symfony\Component\Mime\Email\html
html($body, string $charset='utf-8')
Definition: Email.php:305
Symfony\Component\Mime\Email
Definition: Email.php:25
Symfony\Component\Mime\Email\replyTo
replyTo(... $addresses)
Definition: Email.php:146
Symfony\Component\Mime\Email\getTextBody
getTextBody()
Definition: Email.php:290
Symfony\Component\Mime
Definition: Address.php:12
Symfony\Component\Mime\Part\DataPart\fromPath
static fromPath(string $path, string $name=null, string $contentType=null)
Definition: DataPart.php:47
Symfony\Component\Mime\Email\bcc
bcc(... $addresses)
Definition: Email.php:230
Symfony\Component\Mime\Email\getReturnPath
getReturnPath()
Definition: Email.php:83
Symfony\Component\Mime\Email\getHtmlCharset
getHtmlCharset()
Definition: Email.php:321
Symfony\Component\Mime\Part\DataPart
Definition: DataPart.php:21
Symfony\Component\Mime\Email\getReplyTo
getReplyTo()
Definition: Email.php:154
Symfony\Component\Mime\Email\attach
attach($body, string $name=null, string $contentType=null)
Definition: Email.php:331
Symfony\Component\Mime\Email\getBody
getBody()
Definition: Email.php:393
Http\Message\Decorator\rewind
rewind()
Definition: StreamDecorator.php:89
Symfony\Component\Mime\Part\AbstractPart
Definition: AbstractPart.php:19
Symfony\Component\Mime\Address
Definition: Address.php:24
Symfony\Component\Mime\Email\getCc
getCc()
Definition: Email.php:210
Symfony\Component\Mime\Email\addFrom
addFrom(... $addresses)
Definition: Email.php:108
Symfony\Component\Mime\Message
Definition: Message.php:22
Symfony\Component\Mime\Email\attachFromPath
attachFromPath(string $path, string $name=null, string $contentType=null)
Definition: Email.php:341
Symfony\Component\Mime\Email\__unserialize
__unserialize(array $data)
Definition: Email.php:593
Symfony\Component\Mime\Email\getTextCharset
getTextCharset()
Definition: Email.php:295
Symfony\Component\Mime\Email\embed
embed($body, string $name=null, string $contentType=null)
Definition: Email.php:353
Symfony\Component\Mime\Email\PRIORITY_LOW
const PRIORITY_LOW
Definition: Email.php:30
Symfony\Component\Mime\Email\getHtmlBody
getHtmlBody()
Definition: Email.php:316
Symfony\Component\Mime\Part\TextPart
Definition: TextPart.php:24
Symfony\Component\Mime\Email\attachPart
attachPart(DataPart $part)
Definition: Email.php:373
Symfony\Component\Mime\Email\subject
subject(string $subject)
Definition: Email.php:50
Symfony\Component\Mime\Email\text
text($body, string $charset='utf-8')
Definition: Email.php:279
Symfony\Component\Mime\Email\getPriority
getPriority()
Definition: Email.php:267
Symfony\Component\Mime\Email\cc
cc(... $addresses)
Definition: Email.php:202
Symfony\Component\Mime\Part\Multipart\RelatedPart
Definition: RelatedPart.php:20
Symfony\Component\Mime\Email\getTo
getTo()
Definition: Email.php:182
Symfony\Component\Mime\Email\to
to(... $addresses)
Definition: Email.php:174
Symfony\Component\Mime\Email\sender
sender($address)
Definition: Email.php:93
Symfony\Component\Mime\Email\addBcc
addBcc(... $addresses)
Definition: Email.php:220
Symfony\Component\Mime\Exception\LogicException
Definition: LogicException.php:17
Symfony\Component\Mime\Email\PRIORITY_LOWEST
const PRIORITY_LOWEST
Definition: Email.php:31
Symfony\Component\Mime\Email\getDate
getDate()
Definition: Email.php:68
Symfony\Component\Mime\Email\__serialize
__serialize()
Definition: Email.php:559
Symfony\Component\Mime\Address\createArray
static createArray(array $addresses)
Definition: Address.php:103
Symfony\Component\Mime\Email\PRIORITY_NORMAL
const PRIORITY_NORMAL
Definition: Email.php:29
Symfony\Component\Mime\Email\addReplyTo
addReplyTo(... $addresses)
Definition: Email.php:136
Symfony\Component\Mime\Email\getBcc
getBcc()
Definition: Email.php:238
Symfony\Component\Mime\Email\priority
priority(int $priority)
Definition: Email.php:250
Symfony\Component\Mime\Email\getSubject
getSubject()
Definition: Email.php:55
Symfony\Component\Mime\Email\PRIORITY_HIGH
const PRIORITY_HIGH
Definition: Email.php:28
Symfony\Component\Mime\Email\from
from(... $addresses)
Definition: Email.php:118
Symfony\Component\Mime\Email\getFrom
getFrom()
Definition: Email.php:126
Symfony\Component\Mime\Email\addTo
addTo(... $addresses)
Definition: Email.php:164
Symfony\Component\Mime\Message\getHeaders
getHeaders()
Definition: Message.php:67
Symfony\Component\Mime\Part\Multipart\AlternativePart
Definition: AlternativePart.php:19
Symfony\Component\Mime\Email\ensureValidity
ensureValidity()
Definition: Email.php:402