• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MyIntervals / emogrifier / 39895912537

23 Oct 2026 03:36AM UTC coverage: 98.621% (-0.2%) from 98.825%
39895912537

Pull #1678

github

web-flow
Merge 81a9ff9b8 into a3808e41c
Pull Request #1678: [BUGFIX] Clear declaration cache after inlineCss

37 of 39 new or added lines in 5 files covered. (94.87%)

930 of 943 relevant lines covered (98.62%)

246.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.33
/src/Utilities/DeclarationBlockParser.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Pelago\Emogrifier\Utilities;
6

7
/**
8
 * Provides a common method for parsing CSS declaration blocks.
9
 * These might be from actual CSS, or from the `style` attribute of an HTML DOM element.
10
 *
11
 * Caches results in the parser instance.
12
 *
13
 * @internal
14
 */
15
final class DeclarationBlockParser
16
{
17
    /**
18
     * @var array<non-empty-string, array<non-empty-string, string>>
19
     */
20
    private $cache = [];
21

22
    /**
23
     * Clears the cache of parsed declaration blocks.
24
     */
NEW
25
    public function clearCache(): void
×
26
    {
NEW
27
        $this->cache = [];
×
28
    }
29

30
    /**
31
     * CSS custom properties (variables) have case-sensitive names, so their case must be preserved.
32
     * Standard CSS properties have case-insensitive names, which are converted to lowercase.
33
     *
34
     * @param non-empty-string $name
35
     *
36
     * @return non-empty-string
37
     */
38
    public static function normalizePropertyName(string $name): string
44 ✔
39
    {
40
        if (\substr($name, 0, 2) === '--') {
44 ✔
41
            return $name;
10 ✔
42
        }
43

44
        return \strtolower($name);
34 ✔
45
    }
46

47
    /**
48
     * Parses a CSS declaration block into property name/value pairs.
49
     *
50
     * Example:
51
     *
52
     * The declaration block
53
     *
54
     * ```css
55
     *   color: #000; font-weight: bold;
56
     * ```
57
     *
58
     * will be parsed into the following array:
59
     *
60
     * ```php
61
     *   [
62
     *     'color' => '#000',
63
     *     'font-weight' => 'bold',
64
     *   ]
65
     * ```
66
     *
67
     * @param string $declarationBlock the CSS declarations block (without the curly braces)
68
     *
69
     * @return array<non-empty-string, string>
70
     *         the CSS declarations with the property names as array keys and the property values as array values
71
     *
72
     * @throws \UnexpectedValueException if an empty property name is encountered (which cannot happen)
73
     */
74
    public function parse(string $declarationBlock): array
46 ✔
75
    {
76
        $trimmedDeclarationBlock = \trim($declarationBlock, "; \n\r\t\v\x00");
46 ✔
77
        if ($trimmedDeclarationBlock === '') {
46 ✔
78
            return [];
4 ✔
79
        }
80

81
        if (isset($this->cache[$trimmedDeclarationBlock])) {
42 ✔
82
            return $this->cache[$trimmedDeclarationBlock];
1 ✔
83
        }
84

85
        $preg = new Preg();
42 ✔
86

87
        $declarations = $preg->split('/;(?!base64|charset)/', $trimmedDeclarationBlock);
42 ✔
88
        $properties = [];
42 ✔
89
        foreach ($declarations as $declaration) {
42 ✔
90
            $matches = [];
42 ✔
91
            if ($preg->match(
42 ✔
92
                '/^(-?+[a-zA-Z_][a-zA-Z_0-9\\-]*+|--[a-zA-Z_0-9\\-]++)\\s*+:\\s*+(.++)$/s',
42 ✔
93
                \trim($declaration),
42 ✔
94
                $matches,
42 ✔
95
            ) === 0) {
42 ✔
96
                continue;
7 ✔
97
            }
98

99
            \assert(\count($matches) >= 3);
36 ✔
100
            $propertyName = $matches[1];
36 ✔
101
            \assert($propertyName !== '');
36 ✔
102
            $propertyValue = $matches[2];
36 ✔
103
            $properties[self::normalizePropertyName($propertyName)] = $propertyValue;
36 ✔
104
        }
105
        $this->cache[$trimmedDeclarationBlock] = $properties;
42 ✔
106

107
        return $properties;
42 ✔
108
    }
109
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc