| @@ -5,49 +5,56 @@ | ||
| 5 | 5 | class Emogrifier |
| 6 | 6 | { |
| 7 | 7 | private $html = ''; |
| 8 | 8 | |
| 9 | - private $disableInvisibleNode = false; | |
| 10 | - | |
| 11 | 9 | public function __construct($html) |
| 12 | 10 | { |
| 13 | 11 | $this->html = (string) $html; |
| 14 | 12 | } |
| 15 | 13 | |
| 16 | - public function disableInvisibleNodeRemoval() | |
| 14 | + public function emogrify() | |
| 17 | 15 | { |
| 18 | - $this->disableInvisibleNode = true; | |
| 19 | - return $this; | |
| 16 | + if (!class_exists('\FluentEmogrifier\Vendor\TijsVerkoyen\CssToInlineStyles\CssToInlineStyles')) { | |
| 17 | + require_once __DIR__ . '/scoped-vendor/autoload.php'; | |
| 18 | + } | |
| 19 | + | |
| 20 | + return $this->handleTijsVerkoyen(); | |
| 20 | 21 | } |
| 21 | 22 | |
| 22 | - public function emogrify() | |
| 23 | + private function handleTijsVerkoyen() | |
| 23 | 24 | { |
| 24 | - // check if php version is less than 8.1 | |
| 25 | - if (version_compare(phpversion(), '8.1', '<')) { | |
| 26 | - return $this->handleLegacy(); | |
| 25 | + $css = ''; | |
| 26 | + $html = $this->html; | |
| 27 | + | |
| 28 | + if (preg_match_all('/<style[^>]*>(.*?)<\/style>/si', $html, $matches)) { | |
| 29 | + $css = implode("\n", $matches[1]); | |
| 30 | + $html = preg_replace('/<style[^>]*>.*?<\/style>/si', '', $html); | |
| 27 | 31 | } |
| 28 | 32 | |
| 29 | - if (!class_exists('\FluentEmogrifier\Vendor\Pelago\Emogrifier\CssInliner')) { | |
| 30 | - require_once __DIR__ . '/scoped-vendor/autoload.php'; | |
| 33 | + // Preserve @media queries — TijsVerkoyen strips them during CSS processing | |
| 34 | + // but email clients like Apple Mail and Gmail support them for responsive layouts. | |
| 35 | + // Regex handles both spaced (@media screen) and minified (@media(max-width:600px)) forms. | |
| 36 | + $mediaBlocks = ''; | |
| 37 | + if (preg_match_all('/@media\s*[^{]*\{(?:[^{}]*\{[^{}]*\})*[^{}]*\}/s', $css, $mediaMatches)) { | |
| 38 | + $mediaBlocks = implode("\n", $mediaMatches[0]); | |
| 31 | 39 | } |
| 32 | 40 | |
| 33 | - if (!class_exists('\FluentEmogrifier\Vendor\Pelago\Emogrifier\CssInliner')) { | |
| 34 | - return $this->handleLegacy(); | |
| 35 | - } | |
| 41 | + $inliner = new \FluentEmogrifier\Vendor\TijsVerkoyen\CssToInlineStyles\CssToInlineStyles(); | |
| 42 | + $result = $inliner->convert($html, $css); | |
| 36 | 43 | |
| 37 | - // check if css inlines is available or not | |
| 38 | - return \FluentEmogrifier\Vendor\Pelago\Emogrifier\CssInliner::fromHtml($this->html) | |
| 39 | - ->inlineCss() | |
| 40 | - ->render(); | |
| 41 | - } | |
| 44 | + // Re-inject @media blocks for responsive email support | |
| 45 | + if ($mediaBlocks) { | |
| 46 | + $styleTag = '<style type="text/css">' . $mediaBlocks . '</style>'; | |
| 42 | 47 | |
| 43 | - private function handleLegacy() | |
| 44 | - { | |
| 45 | - $emogrifier = new EmogrifierPhp7($this->html); | |
| 46 | - if ($this->disableInvisibleNode) { | |
| 47 | - $emogrifier->disableInvisibleNodeRemoval(); | |
| 48 | + if (stripos($result, '</head>') !== false) { | |
| 49 | + $result = str_ireplace('</head>', $styleTag . '</head>', $result); | |
| 50 | + } elseif (stripos($result, '<body') !== false) { | |
| 51 | + $result = preg_replace('/<body/i', $styleTag . '<body', $result, 1); | |
| 52 | + } else { | |
| 53 | + $result = $styleTag . $result; | |
| 54 | + } | |
| 48 | 55 | } |
| 49 | 56 | |
| 50 | - return $emogrifier->emogrify(); | |
| 57 | + return $result; | |
| 51 | 58 | } |
| 52 | 59 | |
| 53 | 60 | } |