vendor/symfony/twig-bridge/Mime/BodyRenderer.php line 70

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bridge\Twig\Mime;
  11. use League\HTMLToMarkdown\HtmlConverter;
  12. use Symfony\Component\Mime\BodyRendererInterface;
  13. use Symfony\Component\Mime\Exception\InvalidArgumentException;
  14. use Symfony\Component\Mime\Message;
  15. use Twig\Environment;
  16. /**
  17.  * @author Fabien Potencier <fabien@symfony.com>
  18.  */
  19. final class BodyRenderer implements BodyRendererInterface
  20. {
  21.     private $twig;
  22.     private $context;
  23.     private $converter;
  24.     public function __construct(Environment $twig, array $context = [])
  25.     {
  26.         $this->twig $twig;
  27.         $this->context $context;
  28.         if (class_exists(HtmlConverter::class)) {
  29.             $this->converter = new HtmlConverter([
  30.                 'hard_break' => true,
  31.                 'strip_tags' => true,
  32.                 'remove_nodes' => 'head style',
  33.             ]);
  34.         }
  35.     }
  36.     public function render(Message $message): void
  37.     {
  38.         if (!$message instanceof TemplatedEmail) {
  39.             return;
  40.         }
  41.         $messageContext $message->getContext();
  42.         $previousRenderingKey $messageContext[__CLASS__] ?? null;
  43.         unset($messageContext[__CLASS__]);
  44.         $currentRenderingKey $this->getFingerPrint($message);
  45.         if ($previousRenderingKey === $currentRenderingKey) {
  46.             return;
  47.         }
  48.         if (isset($messageContext['email'])) {
  49.             throw new InvalidArgumentException(sprintf('A "%s" context cannot have an "email" entry as this is a reserved variable.'get_debug_type($message)));
  50.         }
  51.         $vars array_merge($this->context$messageContext, [
  52.             'email' => new WrappedTemplatedEmail($this->twig$message),
  53.         ]);
  54.         if ($template $message->getTextTemplate()) {
  55.             $message->text($this->twig->render($template$vars));
  56.         }
  57.         if ($template $message->getHtmlTemplate()) {
  58.             $message->html($this->twig->render($template$vars));
  59.         }
  60.         // if text body is empty, compute one from the HTML body
  61.         if (!$message->getTextBody() && null !== $html $message->getHtmlBody()) {
  62.             $message->text($this->convertHtmlToText(\is_resource($html) ? stream_get_contents($html) : $html));
  63.         }
  64.         $message->context($message->getContext() + [__CLASS__ => $currentRenderingKey]);
  65.     }
  66.     private function getFingerPrint(TemplatedEmail $message): string
  67.     {
  68.         $messageContext $message->getContext();
  69.         unset($messageContext[__CLASS__]);
  70.         $payload = [$messageContext$message->getTextTemplate(), $message->getHtmlTemplate()];
  71.         try {
  72.             $serialized serialize($payload);
  73.         } catch (\Exception $e) {
  74.             // Serialization of 'Closure' is not allowed
  75.             // Happens when context contain a closure, in that case, we assume that context always change.
  76.             $serialized random_bytes(8);
  77.         }
  78.         return md5($serialized);
  79.     }
  80.     private function convertHtmlToText(string $html): string
  81.     {
  82.         if (null !== $this->converter) {
  83.             return $this->converter->convert($html);
  84.         }
  85.         return strip_tags(preg_replace('{<(head|style)\b.*?</\1>}is'''$html));
  86.     }
  87. }