Компонент Mime

Дата обновления перевода 2023-08-21

Компонент Mime

Компонент Mime позволяет взаимодействовать с сообщениями MIME, используемыми для отправки электронных писем, и предоставляет утилиты, связанные с типами MIME.

Установка

1
$ composer require symfony/mime

Note

Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно подключить файл vendor/autoload.php в вашем коде для включения механизма автозагрузки классов, предоставляемых Composer. Детальнее читайте в этой статье.

Вступление

MIME (Многоцелевые расширения интерне-почты) - это стандарт Интернета, который расширяет первоначальный базовый формат электронных писем для поддержки функций вроде:

  • Заголовков и содержания текста, использующих не ASCII символы;
  • Тела сообщения с несколькими частями (например, HTML и простое текстовое содержание);
  • Нетекстовых вложений: аудио, видео, изображений, и т.д.

Целиком стандарт MIME сложен и огромен, но Symfony отсекает все эти сложности, чтобы предоставить два способа создания MIME-сообщений:

  • API высокого уровня, основанного на классе Email для быстрого создания сообщений писем со всеми распространенными функциями;
  • API низкого уровня, основанного на классе Message для абсолютного контроля над каждый частью сообщения письма.

Использование

Используйте класс Email и их образующие цепи методы, чтобы составить сообщение электронного письма полностью:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Mime\Email;

$email = (new Email())
    ->from('fabien@symfony.com')
    ->to('foo@example.com')
    ->cc('bar@example.com')
    ->bcc('baz@example.com')
    ->replyTo('fabien@symfony.com')
    ->priority(Email::PRIORITY_HIGH)
    ->subject('Important Notification')
    ->text('Lorem ipsum...')
    ->html('<h1>Lorem ipsum</h1> <p>...</p>')
;

Единственной целью этого компонента является создание сообщений писем. Используйте компонент Mailer для того, чтобы отправлять их.

Интеграция с Twig

Компонент Mime поставляется с отличной интеграцией с Twig, позволяющей вам создавать сообщения из шаблонов Twig, встраивать изображения, CSS и другое. Детальней о том, как использовать эти функции, можно найти в документации Mailer: Twig: HTML & CSS .

Но если вы используете компонент Mime без фреймворка Symfony, вам нужно будет разобраться с некоторыми деталями настройки.

Настройка Twig

Для интеграции с Twig, используйте класс BodyRenderer для отображения шаблона и обновления содержания сообщениям письма с результатами:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...
use Symfony\Bridge\Twig\Mime\BodyRenderer;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;

// при использовании компонента Mime внутри полностекового приложения Symfony, вам
// не нужно проходить эту настройку Twig. Вам только нужно внедрить сервис 'twig'
$loader = new FilesystemLoader(__DIR__.'/templates');
$twig = new Environment($loader);

$renderer = new BodyRenderer($twig);
// это обновляет объект $email с результатами отображения
// шаблона, определенного ранее в заданном контексте
$renderer->render($email);

Встраивание CSS-стилей (и других расширений)

Чтобы использовать фильтр inline_css , для начала установите расширение Twig:

1
$ composer require twig/cssinliner-extra

Теперь, подключите расширение:

1
2
3
4
5
6
// ...
use Twig\Extra\CssInliner\CssInlinerExtension;

$loader = new FilesystemLoader(__DIR__.'/templates');
$twig = new Environment($loader);
$twig->addExtension(new CssInlinerExtension());

Такой же процесс должен быть использован для подключения других расширений, вроде MarkdownExtension и InkyExtension .

Cоздание неформатированных сообщений электронных писем

Это полезно для продвинутых приложений, которым нужен абсолютны контроль над каждой частью письма. Это не рекомендуется для приложений с обычными требованиями к электронными письмам, потому как добавляет сложностей и не дает преимуществ.

Перед тем, как продолжать, важно посмотреть на структуру нижнего уровня сообщения письма. Рассмотрите сообщение, которое включает в себя некоторое содержание, как текстовое, так и HTML, одно изображение PNG, встроенное в это содержание, и файл PDF, вложенный в него. Стандарт MIME позволяет структурировать это сообщение разными способами, но следующее древо работает для большинства почтовых клиентов:

1
2
3
4
5
6
7
multipart/mixed
├── multipart/related
│   ├── multipart/alternative
│   │   ├── text/plain
│   │   └── text/html
│   └── image/png
└── application/pdf

Цели каждой части сообщения MIME:

  • multipart/alternative: используется, когда две или более части являются альтернативами одного и того же (или очень похожего) содержания. Предпочитаемый формат должен быть добавлен последним.
  • multipart/mixed: используется для отправки разных типов содержания в одном сообщении письма, вроде как при вложении файлов.
  • multipart/related: используется для обозначения того, что каждая часть сообщения является компонентом совокупного целого. Наиболее распространенное применение - отображение изображений, встроенных в содержание сообщения.

При использовании класса нижнего уровня Message, создайте сообщение письма. Не забывайте обо всем, написанном выше, определяя разные части письма вручную:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Message;
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
use Symfony\Component\Mime\Part\TextPart;

$headers = (new Headers())
    ->addMailboxListHeader('From', ['fabien@symfony.com'])
    ->addMailboxListHeader('To', ['foo@example.com'])
    ->addTextHeader('Subject', 'Important Notification')
;

$textContent = new TextPart('Lorem ipsum...');
$htmlContent = new TextPart('<h1>Lorem ipsum</h1> <p>...</p>', null, 'html');
$body = new AlternativePart($textContent, $htmlContent);

$email = new Message($headers, $body);

Встраивание изображений и вложение файлов возможно путем создания соответствующих мультичастей письма:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
use Symfony\Component\Mime\Part\Multipart\RelatedPart;

// ...
$embeddedImage = new DataPart(fopen('/path/to/images/logo.png', 'r'), null, 'image/png');
$imageCid = $embeddedImage->getContentId();

$attachedFile = new DataPart(fopen('/path/to/documents/terms-of-use.pdf', 'r'), null, 'application/pdf');

$textContent = new TextPart('Lorem ipsum...');
$htmlContent = new TextPart(sprintf(
    '<img src="cid:%s"/> <h1>Lorem ipsum</h1> <p>...</p>', $imageCid
), null, 'html');
$bodyContent = new AlternativePart($textContent, $htmlContent);
$body = new RelatedPart($bodyContent, $embeddedImage);

$messageParts = new MixedPart($body, $attachedFile);

$email = new Message($headers, $messageParts);

Сериализация сообщений писем

Сообщения писем, созданные с классом Email или Message, могут быть сериализованы, так как они являются простыми объектами данных:

1
2
3
4
5
6
$email = (new Email())
    ->from('fabien@symfony.com')
    // ...
;

$serializedEmail = serialize($email);

Распространенное использование - хранение сериализованных сообщений писем, включение их в сообщение, отправленное с помощью компонента Messenger и их позднейшее воссоздание при отправлении. Используйте класс RawMessage, чтобы воссоздавать сообщения писем из их сериализованного содержания:

1
2
3
4
5
6
7
use Symfony\Component\Mime\RawMessage;

// ...
$serializedEmail = serialize($email);

// позже, воссоздайте изначально сообщение, чтобы действительно отправить его
$message = new RawMessage(unserialize($serializedEmail));

Утилиты типов MIME

Хотя MIME был создан в оснвонмо для создания писем, типы содержания (так же известные как MIME-типы и "медиа-типы"), определенные стандартами MIME, также важны для протоколов коммуникации вне писем, например, для HTTP. Поэтому этот компонент также предоставляет утилиты для работы с MIME-типами.

Класс MimeTypes преобразуется между MIME-типами и расширениями имен файлов:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Mime\MimeTypes;

$mimeTypes = new MimeTypes();
$exts = $mimeTypes->getExtensions('application/javascript');
// $exts = ['js', 'jsm', 'mjs']
$exts = $mimeTypes->getExtensions('image/jpeg');
// $exts = ['jpeg', 'jpg', 'jpe']

$types = $mimeTypes->getMimeTypes('js');
// $types = ['application/javascript', 'application/x-javascript', 'text/javascript']
$types = $mimeTypes->getMimeTypes('apk');
// $types = ['application/vnd.android.package-archive']

Эти методы возвращают массивы с одним или более элементов. Расположение элемента отображает его приоритет, поэтому первое возвращенное расширение является предпочитаемым.

Угадывание MIME-типа

Другая полезная утилита позволяет угадывать MIME-тип любого заданного файла:

1
2
3
4
5
6
use Symfony\Component\Mime\MimeTypes;

$mimeTypes = new MimeTypes();
$mimeType = $mimeTypes->guessMimeType('/some/path/to/image.gif');
// Угадывание не основывается на имени файла, поэтому $mimeType будет 'image/gif'
// только, если заданный файл действительно является изображением GIF

Угадывание MIME-типа - времязатратный процесс, требующий исследования части содержания файла. Symfony применяет множество механизмов угадывания, один из которых основывается на PHP-расширении fileinfo. Рекомендуется установить это расширение, чтобы улучшить производительность угадывания.

Добавление угадывателя MIME-типа

Вы можете добавлять собственного угадывателя MIME-типа, создавая класс, реалищующий MimeTypeGuesserInterface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
namespace App;

use Symfony\Component\Mime\MimeTypeGuesserInterface;

class SomeMimeTypeGuesser implements MimeTypeGuesserInterface
{
    public function isGuesserSupported(): bool
    {
        // вернуть true, если угадыватель поддерживается (может зависеть от OS, к примеру)
        return true;
    }

    public function guessMimeType(string $path): ?string
    {
        // исследовать содержание файла, хранящегося в $path, чтобы угадать его
        // тип и вернуть валидный MIME-тип ... или null, если тип неизвестен

        return '...';
    }
}

Угадыватели MIME-типа должны быть зарегистрированы как сервисы и тегированы тегом mime.mime_type_guesser. Если вы используете конфигурацию services.yaml по умолчанию , это уже сделано за вас, благодаря автоконфигурации .