PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / trunk
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress vtrunk
4.17.2 4.17.1 4.17.0 4.16.19 4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / third-party / vendor / pelago / emogrifier / src / HtmlProcessor / CssToAttributeConverter.php
wp-user-avatar / third-party / vendor / pelago / emogrifier / src / HtmlProcessor Last commit date
AbstractHtmlProcessor.php 4 days ago CssToAttributeConverter.php 4 days ago CssVariableEvaluator.php 4 days ago HtmlNormalizer.php 4 days ago HtmlPruner.php 4 days ago
CssToAttributeConverter.php
218 lines
1 <?php
2
3 declare (strict_types=1);
4 namespace ProfilePressVendor\Pelago\Emogrifier\HtmlProcessor;
5
6 use ProfilePressVendor\Pelago\Emogrifier\Utilities\DeclarationBlockParser;
7 use function ProfilePressVendor\Safe\preg_match;
8 use function ProfilePressVendor\Safe\preg_replace;
9 use function ProfilePressVendor\Safe\preg_split;
10 /**
11 * This HtmlProcessor can convert style HTML attributes to the corresponding other visual HTML attributes,
12 * e.g. it converts style="width: 100px" to width="100".
13 *
14 * It will only add attributes, but leaves the style attribute untouched.
15 *
16 * To trigger the conversion, call the `convertCssToVisualAttributes` method.
17 */
18 final class CssToAttributeConverter extends AbstractHtmlProcessor
19 {
20 /**
21 * This multi-level array contains simple mappings of CSS properties to
22 * HTML attributes. If a mapping only applies to certain HTML nodes or
23 * only for certain values, the mapping is an object with an allowlist
24 * of nodes and values.
25 *
26 * @var array<
27 * non-empty-string,
28 * array{
29 * attribute: non-empty-string,
30 * nodes?: list<non-empty-string>,
31 * values?: list<non-empty-string>
32 * }
33 * >
34 */
35 private $cssToHtmlMap = ['background-color' => ['attribute' => 'bgcolor'], 'text-align' => ['attribute' => 'align', 'nodes' => ['p', 'div', 'td', 'th'], 'values' => ['left', 'right', 'center', 'justify']], 'float' => ['attribute' => 'align', 'nodes' => ['table', 'img'], 'values' => ['left', 'right']], 'border-spacing' => ['attribute' => 'cellspacing', 'nodes' => ['table']]];
36 /**
37 * Maps the CSS from the style nodes to visual HTML attributes.
38 *
39 * @return $this
40 */
41 public function convertCssToVisualAttributes(): self
42 {
43 $declarationBlockParser = new DeclarationBlockParser();
44 foreach ($this->getAllNodesWithStyleAttribute() as $node) {
45 $inlineStyleDeclarations = $declarationBlockParser->parse($node->getAttribute('style'));
46 $this->mapCssToHtmlAttributes($inlineStyleDeclarations, $node);
47 }
48 return $this;
49 }
50 /**
51 * Returns a list with all DOM nodes that have a style attribute.
52 *
53 * @return \DOMNodeList<\DOMElement>
54 */
55 private function getAllNodesWithStyleAttribute(): \DOMNodeList
56 {
57 $result = $this->getXPath()->query('//*[@style]');
58 \assert($result instanceof \DOMNodeList);
59 /** @var \DOMNodeList<\DOMElement> $result */
60 return $result;
61 }
62 /**
63 * Applies `$styles` to `$node`.
64 *
65 * This method maps CSS styles to HTML attributes and adds those to the node.
66 *
67 * @param array<non-empty-string, string> $styles
68 * the new CSS styles taken from the global styles to be applied to this node
69 */
70 private function mapCssToHtmlAttributes(array $styles, \DOMElement $node): void
71 {
72 foreach ($styles as $property => $value) {
73 // Strip !important indicator
74 $value = \trim(\str_replace('!important', '', $value));
75 $this->mapCssToHtmlAttribute($property, $value, $node);
76 }
77 }
78 /**
79 * Tries to apply the CSS style to `$node` as an attribute.
80 *
81 * This method maps a CSS rule to HTML attributes and adds those to the node.
82 *
83 * @param non-empty-string $property
84 */
85 private function mapCssToHtmlAttribute(string $property, string $value, \DOMElement $node): void
86 {
87 if (!$this->mapSimpleCssProperty($property, $value, $node)) {
88 $this->mapComplexCssProperty($property, $value, $node);
89 }
90 }
91 /**
92 * Looks up the CSS property in the mapping table and maps it if it matches the conditions.
93 *
94 * @param non-empty-string $property
95 *
96 * @return bool whether the property can be mapped using the simple mapping table
97 */
98 private function mapSimpleCssProperty(string $property, string $value, \DOMElement $node): bool
99 {
100 if (!isset($this->cssToHtmlMap[$property])) {
101 return \false;
102 }
103 $mapping = $this->cssToHtmlMap[$property];
104 $nodesMatch = !isset($mapping['nodes']) || \in_array($node->nodeName, $mapping['nodes'], \true);
105 $valuesMatch = !isset($mapping['values']) || \in_array($value, $mapping['values'], \true);
106 $canBeMapped = $nodesMatch && $valuesMatch;
107 if ($canBeMapped) {
108 $node->setAttribute($mapping['attribute'], $value);
109 }
110 return $canBeMapped;
111 }
112 /**
113 * Maps CSS properties that need special transformation to an HTML attribute.
114 *
115 * @param non-empty-string $property
116 */
117 private function mapComplexCssProperty(string $property, string $value, \DOMElement $node): void
118 {
119 switch ($property) {
120 case 'background':
121 $this->mapBackgroundProperty($node, $value);
122 break;
123 case 'width':
124 // intentional fall-through
125 case 'height':
126 $this->mapWidthOrHeightProperty($node, $value, $property);
127 break;
128 case 'margin':
129 $this->mapMarginProperty($node, $value);
130 break;
131 case 'border':
132 $this->mapBorderProperty($node, $value);
133 break;
134 default:
135 }
136 }
137 /**
138 * @param \DOMElement $node node to apply styles to
139 * @param string $value the value of the style rule to map
140 */
141 private function mapBackgroundProperty(\DOMElement $node, string $value): void
142 {
143 // parse out the color, if any
144 /** @var array<int, string> $styles */
145 $styles = \explode(' ', $value, 2);
146 $first = $styles[0];
147 if (\is_numeric($first[0]) || \strncmp($first, 'url', 3) === 0) {
148 return;
149 }
150 // as this is not a position or image, assume it's a color
151 $node->setAttribute('bgcolor', $first);
152 }
153 /**
154 * @param \DOMElement $node node to apply styles to
155 * @param string $value the value of the style rule to map
156 * @param non-empty-string $property the name of the CSS property to map
157 */
158 private function mapWidthOrHeightProperty(\DOMElement $node, string $value, string $property): void
159 {
160 // only parse values in px and %, but not values like "auto"
161 if (preg_match('/^(\d+)(\.(\d+))?(px|%)$/', $value) === 0) {
162 return;
163 }
164 $number = preg_replace('/[^0-9.%]/', '', $value);
165 $node->setAttribute($property, $number);
166 }
167 /**
168 * @param \DOMElement $node node to apply styles to
169 * @param string $value the value of the style rule to map
170 */
171 private function mapMarginProperty(\DOMElement $node, string $value): void
172 {
173 if (!$this->isTableOrImageNode($node)) {
174 return;
175 }
176 $margins = $this->parseCssShorthandValue($value);
177 if ($margins['left'] === 'auto' && $margins['right'] === 'auto') {
178 $node->setAttribute('align', 'center');
179 }
180 }
181 /**
182 * @param \DOMElement $node node to apply styles to
183 * @param string $value the value of the style rule to map
184 */
185 private function mapBorderProperty(\DOMElement $node, string $value): void
186 {
187 if (!$this->isTableOrImageNode($node)) {
188 return;
189 }
190 if ($value === 'none' || $value === '0') {
191 $node->setAttribute('border', '0');
192 }
193 }
194 private function isTableOrImageNode(\DOMElement $node): bool
195 {
196 return $node->nodeName === 'table' || $node->nodeName === 'img';
197 }
198 /**
199 * Parses a shorthand CSS value and splits it into individual values. For example: `padding: 0 auto;` - `0 auto` is
200 * split into top: 0, left: auto, bottom: 0, right: auto.
201 *
202 * @param string $value a CSS property value with 1, 2, 3 or 4 sizes
203 *
204 * @return array{top: string, right: string, bottom: string, left: string}
205 */
206 private function parseCssShorthandValue(string $value): array
207 {
208 $values = preg_split('/\s+/', $value);
209 /** @var list<string> $values */
210 $css = [];
211 $css['top'] = $values[0];
212 $css['right'] = \count($values) > 1 ? $values[1] : $css['top'];
213 $css['bottom'] = \count($values) > 2 ? $values[2] : $css['top'];
214 $css['left'] = \count($values) > 3 ? $values[3] : $css['right'];
215 return $css;
216 }
217 }
218