CssConcatenator.php
64 lines
| 1 | <?php |
| 2 | declare (strict_types=1); |
| 3 | namespace MailPoetVendor\Pelago\Emogrifier\Utilities; |
| 4 | if (!defined('ABSPATH')) exit; |
| 5 | class CssConcatenator |
| 6 | { |
| 7 | private $mediaRules = []; |
| 8 | public function append(array $selectors, string $declarationsBlock, string $media = '') : void |
| 9 | { |
| 10 | $selectorsAsKeys = \array_flip($selectors); |
| 11 | $mediaRule = $this->getOrCreateMediaRuleToAppendTo($media); |
| 12 | $ruleBlocks = $mediaRule->ruleBlocks; |
| 13 | $lastRuleBlock = \end($ruleBlocks); |
| 14 | $hasSameDeclarationsAsLastRule = \is_object($lastRuleBlock) && $declarationsBlock === $lastRuleBlock->declarationsBlock; |
| 15 | if ($hasSameDeclarationsAsLastRule) { |
| 16 | $lastRuleBlock->selectorsAsKeys += $selectorsAsKeys; |
| 17 | } else { |
| 18 | $lastRuleBlockSelectors = \is_object($lastRuleBlock) ? $lastRuleBlock->selectorsAsKeys : []; |
| 19 | $hasSameSelectorsAsLastRule = \is_object($lastRuleBlock) && self::hasEquivalentSelectors($selectorsAsKeys, $lastRuleBlockSelectors); |
| 20 | if ($hasSameSelectorsAsLastRule) { |
| 21 | $lastDeclarationsBlockWithoutSemicolon = \rtrim(\rtrim($lastRuleBlock->declarationsBlock), ';'); |
| 22 | $lastRuleBlock->declarationsBlock = $lastDeclarationsBlockWithoutSemicolon . ';' . $declarationsBlock; |
| 23 | } else { |
| 24 | $mediaRule->ruleBlocks[] = (object) \compact('selectorsAsKeys', 'declarationsBlock'); |
| 25 | } |
| 26 | } |
| 27 | } |
| 28 | public function getCss() : string |
| 29 | { |
| 30 | return \implode('', \array_map([self::class, 'getMediaRuleCss'], $this->mediaRules)); |
| 31 | } |
| 32 | private function getOrCreateMediaRuleToAppendTo(string $media) : object |
| 33 | { |
| 34 | $lastMediaRule = \end($this->mediaRules); |
| 35 | if (\is_object($lastMediaRule) && $media === $lastMediaRule->media) { |
| 36 | return $lastMediaRule; |
| 37 | } |
| 38 | $newMediaRule = (object) ['media' => $media, 'ruleBlocks' => []]; |
| 39 | $this->mediaRules[] = $newMediaRule; |
| 40 | return $newMediaRule; |
| 41 | } |
| 42 | private static function hasEquivalentSelectors(array $selectorsAsKeys1, array $selectorsAsKeys2) : bool |
| 43 | { |
| 44 | return \count($selectorsAsKeys1) === \count($selectorsAsKeys2) && \count($selectorsAsKeys1) === \count($selectorsAsKeys1 + $selectorsAsKeys2); |
| 45 | } |
| 46 | private static function getMediaRuleCss(object $mediaRule) : string |
| 47 | { |
| 48 | $ruleBlocks = $mediaRule->ruleBlocks; |
| 49 | $css = \implode('', \array_map([self::class, 'getRuleBlockCss'], $ruleBlocks)); |
| 50 | $media = $mediaRule->media; |
| 51 | if ($media !== '') { |
| 52 | $css = $media . '{' . $css . '}'; |
| 53 | } |
| 54 | return $css; |
| 55 | } |
| 56 | private static function getRuleBlockCss(object $ruleBlock) : string |
| 57 | { |
| 58 | $selectorsAsKeys = $ruleBlock->selectorsAsKeys; |
| 59 | $selectors = \array_keys($selectorsAsKeys); |
| 60 | $declarationsBlock = $ruleBlock->declarationsBlock; |
| 61 | return \implode(',', $selectors) . '{' . $declarationsBlock . '}'; |
| 62 | } |
| 63 | } |
| 64 |