AbstractHtmlProcessor.php
2 years ago
CssToAttributeConverter.php
2 years ago
HtmlNormalizer.php
2 years ago
HtmlPruner.php
2 years ago
index.php
2 years ago
HtmlPruner.php
65 lines
| 1 | <?php |
| 2 | declare (strict_types=1); |
| 3 | namespace MailPoetVendor\Pelago\Emogrifier\HtmlProcessor; |
| 4 | if (!defined('ABSPATH')) exit; |
| 5 | use MailPoetVendor\Pelago\Emogrifier\CssInliner; |
| 6 | use MailPoetVendor\Pelago\Emogrifier\Utilities\ArrayIntersector; |
| 7 | class HtmlPruner extends AbstractHtmlProcessor |
| 8 | { |
| 9 | private const DISPLAY_NONE_MATCHER = '//*[@style and contains(translate(translate(@style," ",""),"NOE","noe"),"display:none")' . ' and not(@class and contains(concat(" ", normalize-space(@class), " "), " -emogrifier-keep "))]'; |
| 10 | public function removeElementsWithDisplayNone() : self |
| 11 | { |
| 12 | $elementsWithStyleDisplayNone = $this->getXPath()->query(self::DISPLAY_NONE_MATCHER); |
| 13 | if ($elementsWithStyleDisplayNone->length === 0) { |
| 14 | return $this; |
| 15 | } |
| 16 | foreach ($elementsWithStyleDisplayNone as $element) { |
| 17 | $parentNode = $element->parentNode; |
| 18 | if ($parentNode !== null) { |
| 19 | $parentNode->removeChild($element); |
| 20 | } |
| 21 | } |
| 22 | return $this; |
| 23 | } |
| 24 | public function removeRedundantClasses(array $classesToKeep = []) : self |
| 25 | { |
| 26 | $elementsWithClassAttribute = $this->getXPath()->query('//*[@class]'); |
| 27 | if ($classesToKeep !== []) { |
| 28 | $this->removeClassesFromElements($elementsWithClassAttribute, $classesToKeep); |
| 29 | } else { |
| 30 | // Avoid unnecessary processing if there are no classes to keep. |
| 31 | $this->removeClassAttributeFromElements($elementsWithClassAttribute); |
| 32 | } |
| 33 | return $this; |
| 34 | } |
| 35 | private function removeClassesFromElements(\DOMNodeList $elements, array $classesToKeep) : void |
| 36 | { |
| 37 | $classesToKeepIntersector = new ArrayIntersector($classesToKeep); |
| 38 | foreach ($elements as $element) { |
| 39 | $elementClasses = \preg_split('/\\s++/', \trim($element->getAttribute('class'))); |
| 40 | $elementClassesToKeep = $classesToKeepIntersector->intersectWith($elementClasses); |
| 41 | if ($elementClassesToKeep !== []) { |
| 42 | $element->setAttribute('class', \implode(' ', $elementClassesToKeep)); |
| 43 | } else { |
| 44 | $element->removeAttribute('class'); |
| 45 | } |
| 46 | } |
| 47 | } |
| 48 | private function removeClassAttributeFromElements(\DOMNodeList $elements) : void |
| 49 | { |
| 50 | foreach ($elements as $element) { |
| 51 | $element->removeAttribute('class'); |
| 52 | } |
| 53 | } |
| 54 | public function removeRedundantClassesAfterCssInlined(CssInliner $cssInliner) : self |
| 55 | { |
| 56 | $classesToKeepAsKeys = []; |
| 57 | foreach ($cssInliner->getMatchingUninlinableSelectors() as $selector) { |
| 58 | \preg_match_all('/\\.(-?+[_a-zA-Z][\\w\\-]*+)/', $selector, $matches); |
| 59 | $classesToKeepAsKeys += \array_fill_keys($matches[1], \true); |
| 60 | } |
| 61 | $this->removeRedundantClasses(\array_keys($classesToKeepAsKeys)); |
| 62 | return $this; |
| 63 | } |
| 64 | } |
| 65 |