PluginProbe
WooCommerce / 11.0.0
WooCommerce v11.0.0
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 All 648 releases
woocommerce / lib / packages / Sabberworm / CSS / RuleSet / DeclarationBlock.php
DeclarationBlock.php
872 lines 31.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Automattic\WooCommerce\Vendor\Sabberworm\CSS\RuleSet;
4
5 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\CSSList\CSSList;
6 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\CSSList\KeyFrame;
7 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\OutputFormat;
8 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Parsing\OutputException;
9 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Parsing\ParserState;
10 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Parsing\UnexpectedEOFException;
11 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Parsing\UnexpectedTokenException;
12 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Property\KeyframeSelector;
13 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Property\Selector;
14 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Rule\Rule;
15 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Value\Color;
16 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Value\RuleValueList;
17 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Value\Size;
18 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Value\URL;
19 use Automattic\WooCommerce\Vendor\Sabberworm\CSS\Value\Value;
20
21 /**
22 * This class represents a `RuleSet` constrained by a `Selector`.
23 *
24 * It contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the
25 * matching elements.
26 *
27 * Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
28 */
29 class DeclarationBlock extends RuleSet
30 {
31 /**
32 * @var array<int, Selector|string>
33 */
34 private $aSelectors;
35
36 /**
37 * @param int $iLineNo
38 */
39 public function __construct($iLineNo = 0)
40 {
41 parent::__construct($iLineNo);
42 $this->aSelectors = [];
43 }
44
45 /**
46 * @param CSSList|null $oList
47 *
48 * @return DeclarationBlock|false
49 *
50 * @throws UnexpectedTokenException
51 * @throws UnexpectedEOFException
52 *
53 * @internal since V8.8.0
54 */
55 public static function parse(ParserState $oParserState, $oList = null)
56 {
57 $aComments = [];
58 $oResult = new DeclarationBlock($oParserState->currentLine());
59 try {
60 $aSelectorParts = [];
61 $sStringWrapperChar = false;
62 do {
63 $aSelectorParts[] = $oParserState->consume(1)
64 . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments);
65 if (in_array($oParserState->peek(), ['\'', '"']) && substr(end($aSelectorParts), -1) != "\\") {
66 if ($sStringWrapperChar === false) {
67 $sStringWrapperChar = $oParserState->peek();
68 } elseif ($sStringWrapperChar == $oParserState->peek()) {
69 $sStringWrapperChar = false;
70 }
71 }
72 } while (!in_array($oParserState->peek(), ['{', '}']) || $sStringWrapperChar !== false);
73 $oResult->setSelectors(implode('', $aSelectorParts), $oList);
74 if ($oParserState->comes('{')) {
75 $oParserState->consume(1);
76 }
77 } catch (UnexpectedTokenException $e) {
78 if ($oParserState->getSettings()->bLenientParsing) {
79 if (!$oParserState->comes('}')) {
80 $oParserState->consumeUntil('}', false, true);
81 }
82 return false;
83 } else {
84 throw $e;
85 }
86 }
87 $oResult->setComments($aComments);
88 RuleSet::parseRuleSet($oParserState, $oResult);
89 return $oResult;
90 }
91
92 /**
93 * @param array<int, Selector|string>|string $mSelector
94 * @param CSSList|null $oList
95 *
96 * @throws UnexpectedTokenException
97 */
98 public function setSelectors($mSelector, $oList = null)
99 {
100 if (is_array($mSelector)) {
101 $this->aSelectors = $mSelector;
102 } else {
103 $this->aSelectors = explode(',', $mSelector);
104 }
105 foreach ($this->aSelectors as $iKey => $mSelector) {
106 if (!($mSelector instanceof Selector)) {
107 if ($oList === null || !($oList instanceof KeyFrame)) {
108 if (!Selector::isValid($mSelector)) {
109 throw new UnexpectedTokenException(
110 "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
111 $mSelector,
112 "custom"
113 );
114 }
115 $this->aSelectors[$iKey] = new Selector($mSelector);
116 } else {
117 if (!KeyframeSelector::isValid($mSelector)) {
118 throw new UnexpectedTokenException(
119 "Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.",
120 $mSelector,
121 "custom"
122 );
123 }
124 $this->aSelectors[$iKey] = new KeyframeSelector($mSelector);
125 }
126 }
127 }
128 }
129
130 /**
131 * Remove one of the selectors of the block.
132 *
133 * @param Selector|string $mSelector
134 *
135 * @return bool
136 */
137 public function removeSelector($mSelector)
138 {
139 if ($mSelector instanceof Selector) {
140 $mSelector = $mSelector->getSelector();
141 }
142 foreach ($this->aSelectors as $iKey => $oSelector) {
143 if ($oSelector->getSelector() === $mSelector) {
144 unset($this->aSelectors[$iKey]);
145 return true;
146 }
147 }
148 return false;
149 }
150
151 /**
152 * @return array<int, Selector|string>
153 *
154 * @deprecated will be removed in version 9.0; use `getSelectors()` instead
155 */
156 public function getSelector()
157 {
158 return $this->getSelectors();
159 }
160
161 /**
162 * @param Selector|string $mSelector
163 * @param CSSList|null $oList
164 *
165 * @return void
166 *
167 * @deprecated will be removed in version 9.0; use `setSelectors()` instead
168 */
169 public function setSelector($mSelector, $oList = null)
170 {
171 $this->setSelectors($mSelector, $oList);
172 }
173
174 /**
175 * @return array<int, Selector|string>
176 */
177 public function getSelectors()
178 {
179 return $this->aSelectors;
180 }
181
182 /**
183 * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts.
184 *
185 * @return void
186 *
187 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
188 */
189 public function expandShorthands()
190 {
191 // border must be expanded before dimensions
192 $this->expandBorderShorthand();
193 $this->expandDimensionsShorthand();
194 $this->expandFontShorthand();
195 $this->expandBackgroundShorthand();
196 $this->expandListStyleShorthand();
197 }
198
199 /**
200 * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible.
201 *
202 * @return void
203 *
204 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
205 */
206 public function createShorthands()
207 {
208 $this->createBackgroundShorthand();
209 $this->createDimensionsShorthand();
210 // border must be shortened after dimensions
211 $this->createBorderShorthand();
212 $this->createFontShorthand();
213 $this->createListStyleShorthand();
214 }
215
216 /**
217 * Splits shorthand border declarations (e.g. `border: 1px red;`).
218 *
219 * Additional splitting happens in expandDimensionsShorthand.
220 *
221 * Multiple borders are not yet supported as of 3.
222 *
223 * @return void
224 *
225 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
226 */
227 public function expandBorderShorthand()
228 {
229 $aBorderRules = [
230 'border',
231 'border-left',
232 'border-right',
233 'border-top',
234 'border-bottom',
235 ];
236 $aBorderSizes = [
237 'thin',
238 'medium',
239 'thick',
240 ];
241 $aRules = $this->getRulesAssoc();
242 foreach ($aBorderRules as $sBorderRule) {
243 if (!isset($aRules[$sBorderRule])) {
244 continue;
245 }
246 $oRule = $aRules[$sBorderRule];
247 $mRuleValue = $oRule->getValue();
248 $aValues = [];
249 if (!$mRuleValue instanceof RuleValueList) {
250 $aValues[] = $mRuleValue;
251 } else {
252 $aValues = $mRuleValue->getListComponents();
253 }
254 foreach ($aValues as $mValue) {
255 if ($mValue instanceof Value) {
256 $mNewValue = clone $mValue;
257 } else {
258 $mNewValue = $mValue;
259 }
260 if ($mValue instanceof Size) {
261 $sNewRuleName = $sBorderRule . "-width";
262 } elseif ($mValue instanceof Color) {
263 $sNewRuleName = $sBorderRule . "-color";
264 } else {
265 if (in_array($mValue, $aBorderSizes)) {
266 $sNewRuleName = $sBorderRule . "-width";
267 } else {
268 $sNewRuleName = $sBorderRule . "-style";
269 }
270 }
271 $oNewRule = new Rule($sNewRuleName, $oRule->getLineNo(), $oRule->getColNo());
272 $oNewRule->setIsImportant($oRule->getIsImportant());
273 $oNewRule->addValue([$mNewValue]);
274 $this->addRule($oNewRule);
275 }
276 $this->removeRule($sBorderRule);
277 }
278 }
279
280 /**
281 * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`)
282 * into their constituent parts.
283 *
284 * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`.
285 *
286 * @return void
287 *
288 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
289 */
290 public function expandDimensionsShorthand()
291 {
292 $aExpansions = [
293 'margin' => 'margin-%s',
294 'padding' => 'padding-%s',
295 'border-color' => 'border-%s-color',
296 'border-style' => 'border-%s-style',
297 'border-width' => 'border-%s-width',
298 ];
299 $aRules = $this->getRulesAssoc();
300 foreach ($aExpansions as $sProperty => $sExpanded) {
301 if (!isset($aRules[$sProperty])) {
302 continue;
303 }
304 $oRule = $aRules[$sProperty];
305 $mRuleValue = $oRule->getValue();
306 $aValues = [];
307 if (!$mRuleValue instanceof RuleValueList) {
308 $aValues[] = $mRuleValue;
309 } else {
310 $aValues = $mRuleValue->getListComponents();
311 }
312 $top = $right = $bottom = $left = null;
313 switch (count($aValues)) {
314 case 1:
315 $top = $right = $bottom = $left = $aValues[0];
316 break;
317 case 2:
318 $top = $bottom = $aValues[0];
319 $left = $right = $aValues[1];
320 break;
321 case 3:
322 $top = $aValues[0];
323 $left = $right = $aValues[1];
324 $bottom = $aValues[2];
325 break;
326 case 4:
327 $top = $aValues[0];
328 $right = $aValues[1];
329 $bottom = $aValues[2];
330 $left = $aValues[3];
331 break;
332 }
333 foreach (['top', 'right', 'bottom', 'left'] as $sPosition) {
334 $oNewRule = new Rule(sprintf($sExpanded, $sPosition), $oRule->getLineNo(), $oRule->getColNo());
335 $oNewRule->setIsImportant($oRule->getIsImportant());
336 $oNewRule->addValue(${$sPosition});
337 $this->addRule($oNewRule);
338 }
339 $this->removeRule($sProperty);
340 }
341 }
342
343 /**
344 * Converts shorthand font declarations
345 * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`)
346 * into their constituent parts.
347 *
348 * @return void
349 *
350 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
351 */
352 public function expandFontShorthand()
353 {
354 $aRules = $this->getRulesAssoc();
355 if (!isset($aRules['font'])) {
356 return;
357 }
358 $oRule = $aRules['font'];
359 // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand
360 $aFontProperties = [
361 'font-style' => 'normal',
362 'font-variant' => 'normal',
363 'font-weight' => 'normal',
364 'font-size' => 'normal',
365 'line-height' => 'normal',
366 ];
367 $mRuleValue = $oRule->getValue();
368 $aValues = [];
369 if (!$mRuleValue instanceof RuleValueList) {
370 $aValues[] = $mRuleValue;
371 } else {
372 $aValues = $mRuleValue->getListComponents();
373 }
374 foreach ($aValues as $mValue) {
375 if (!$mValue instanceof Value) {
376 $mValue = mb_strtolower($mValue);
377 }
378 if (in_array($mValue, ['normal', 'inherit'])) {
379 foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) {
380 if (!isset($aFontProperties[$sProperty])) {
381 $aFontProperties[$sProperty] = $mValue;
382 }
383 }
384 } elseif (in_array($mValue, ['italic', 'oblique'])) {
385 $aFontProperties['font-style'] = $mValue;
386 } elseif ($mValue == 'small-caps') {
387 $aFontProperties['font-variant'] = $mValue;
388 } elseif (
389 in_array($mValue, ['bold', 'bolder', 'lighter'])
390 || ($mValue instanceof Size
391 && in_array($mValue->getSize(), range(100, 900, 100)))
392 ) {
393 $aFontProperties['font-weight'] = $mValue;
394 } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') {
395 list($oSize, $oHeight) = $mValue->getListComponents();
396 $aFontProperties['font-size'] = $oSize;
397 $aFontProperties['line-height'] = $oHeight;
398 } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) {
399 $aFontProperties['font-size'] = $mValue;
400 } else {
401 $aFontProperties['font-family'] = $mValue;
402 }
403 }
404 foreach ($aFontProperties as $sProperty => $mValue) {
405 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
406 $oNewRule->addValue($mValue);
407 $oNewRule->setIsImportant($oRule->getIsImportant());
408 $this->addRule($oNewRule);
409 }
410 $this->removeRule('font');
411 }
412
413 /**
414 * Converts shorthand background declarations
415 * (e.g. `background: url("chess.png") gray 50% repeat fixed;`)
416 * into their constituent parts.
417 *
418 * @see http://www.w3.org/TR/21/colors.html#propdef-background
419 *
420 * @return void
421 *
422 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
423 */
424 public function expandBackgroundShorthand()
425 {
426 $aRules = $this->getRulesAssoc();
427 if (!isset($aRules['background'])) {
428 return;
429 }
430 $oRule = $aRules['background'];
431 $aBgProperties = [
432 'background-color' => ['transparent'],
433 'background-image' => ['none'],
434 'background-repeat' => ['repeat'],
435 'background-attachment' => ['scroll'],
436 'background-position' => [
437 new Size(0, '%', false, $this->getLineNo()),
438 new Size(0, '%', false, $this->getLineNo()),
439 ],
440 ];
441 $mRuleValue = $oRule->getValue();
442 $aValues = [];
443 if (!$mRuleValue instanceof RuleValueList) {
444 $aValues[] = $mRuleValue;
445 } else {
446 $aValues = $mRuleValue->getListComponents();
447 }
448 if (count($aValues) == 1 && $aValues[0] == 'inherit') {
449 foreach ($aBgProperties as $sProperty => $mValue) {
450 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
451 $oNewRule->addValue('inherit');
452 $oNewRule->setIsImportant($oRule->getIsImportant());
453 $this->addRule($oNewRule);
454 }
455 $this->removeRule('background');
456 return;
457 }
458 $iNumBgPos = 0;
459 foreach ($aValues as $mValue) {
460 if (!$mValue instanceof Value) {
461 $mValue = mb_strtolower($mValue);
462 }
463 if ($mValue instanceof URL) {
464 $aBgProperties['background-image'] = $mValue;
465 } elseif ($mValue instanceof Color) {
466 $aBgProperties['background-color'] = $mValue;
467 } elseif (in_array($mValue, ['scroll', 'fixed'])) {
468 $aBgProperties['background-attachment'] = $mValue;
469 } elseif (in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'])) {
470 $aBgProperties['background-repeat'] = $mValue;
471 } elseif (
472 in_array($mValue, ['left', 'center', 'right', 'top', 'bottom'])
473 || $mValue instanceof Size
474 ) {
475 if ($iNumBgPos == 0) {
476 $aBgProperties['background-position'][0] = $mValue;
477 $aBgProperties['background-position'][1] = 'center';
478 } else {
479 $aBgProperties['background-position'][$iNumBgPos] = $mValue;
480 }
481 $iNumBgPos++;
482 }
483 }
484 foreach ($aBgProperties as $sProperty => $mValue) {
485 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
486 $oNewRule->setIsImportant($oRule->getIsImportant());
487 $oNewRule->addValue($mValue);
488 $this->addRule($oNewRule);
489 }
490 $this->removeRule('background');
491 }
492
493 /**
494 * @return void
495 *
496 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
497 */
498 public function expandListStyleShorthand()
499 {
500 $aListProperties = [
501 'list-style-type' => 'disc',
502 'list-style-position' => 'outside',
503 'list-style-image' => 'none',
504 ];
505 $aListStyleTypes = [
506 'none',
507 'disc',
508 'circle',
509 'square',
510 'decimal-leading-zero',
511 'decimal',
512 'lower-roman',
513 'upper-roman',
514 'lower-greek',
515 'lower-alpha',
516 'lower-latin',
517 'upper-alpha',
518 'upper-latin',
519 'hebrew',
520 'armenian',
521 'georgian',
522 'cjk-ideographic',
523 'hiragana',
524 'hira-gana-iroha',
525 'katakana-iroha',
526 'katakana',
527 ];
528 $aListStylePositions = [
529 'inside',
530 'outside',
531 ];
532 $aRules = $this->getRulesAssoc();
533 if (!isset($aRules['list-style'])) {
534 return;
535 }
536 $oRule = $aRules['list-style'];
537 $mRuleValue = $oRule->getValue();
538 $aValues = [];
539 if (!$mRuleValue instanceof RuleValueList) {
540 $aValues[] = $mRuleValue;
541 } else {
542 $aValues = $mRuleValue->getListComponents();
543 }
544 if (count($aValues) == 1 && $aValues[0] == 'inherit') {
545 foreach ($aListProperties as $sProperty => $mValue) {
546 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
547 $oNewRule->addValue('inherit');
548 $oNewRule->setIsImportant($oRule->getIsImportant());
549 $this->addRule($oNewRule);
550 }
551 $this->removeRule('list-style');
552 return;
553 }
554 foreach ($aValues as $mValue) {
555 if (!$mValue instanceof Value) {
556 $mValue = mb_strtolower($mValue);
557 }
558 if ($mValue instanceof Url) {
559 $aListProperties['list-style-image'] = $mValue;
560 } elseif (in_array($mValue, $aListStyleTypes)) {
561 $aListProperties['list-style-types'] = $mValue;
562 } elseif (in_array($mValue, $aListStylePositions)) {
563 $aListProperties['list-style-position'] = $mValue;
564 }
565 }
566 foreach ($aListProperties as $sProperty => $mValue) {
567 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
568 $oNewRule->setIsImportant($oRule->getIsImportant());
569 $oNewRule->addValue($mValue);
570 $this->addRule($oNewRule);
571 }
572 $this->removeRule('list-style');
573 }
574
575 /**
576 * @param array<array-key, string> $aProperties
577 * @param string $sShorthand
578 *
579 * @return void
580 *
581 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
582 */
583 public function createShorthandProperties(array $aProperties, $sShorthand)
584 {
585 $aRules = $this->getRulesAssoc();
586 $oRule = null;
587 $aNewValues = [];
588 foreach ($aProperties as $sProperty) {
589 if (!isset($aRules[$sProperty])) {
590 continue;
591 }
592 $oRule = $aRules[$sProperty];
593 if (!$oRule->getIsImportant()) {
594 $mRuleValue = $oRule->getValue();
595 $aValues = [];
596 if (!$mRuleValue instanceof RuleValueList) {
597 $aValues[] = $mRuleValue;
598 } else {
599 $aValues = $mRuleValue->getListComponents();
600 }
601 foreach ($aValues as $mValue) {
602 $aNewValues[] = $mValue;
603 }
604 $this->removeRule($sProperty);
605 }
606 }
607 if ($aNewValues !== [] && $oRule instanceof Rule) {
608 $oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo());
609 foreach ($aNewValues as $mValue) {
610 $oNewRule->addValue($mValue);
611 }
612 $this->addRule($oNewRule);
613 }
614 }
615
616 /**
617 * @return void
618 *
619 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
620 */
621 public function createBackgroundShorthand()
622 {
623 $aProperties = [
624 'background-color',
625 'background-image',
626 'background-repeat',
627 'background-position',
628 'background-attachment',
629 ];
630 $this->createShorthandProperties($aProperties, 'background');
631 }
632
633 /**
634 * @return void
635 *
636 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
637 */
638 public function createListStyleShorthand()
639 {
640 $aProperties = [
641 'list-style-type',
642 'list-style-position',
643 'list-style-image',
644 ];
645 $this->createShorthandProperties($aProperties, 'list-style');
646 }
647
648 /**
649 * Combines `border-color`, `border-style` and `border-width` into `border`.
650 *
651 * Should be run after `create_dimensions_shorthand`!
652 *
653 * @return void
654 *
655 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
656 */
657 public function createBorderShorthand()
658 {
659 $aProperties = [
660 'border-width',
661 'border-style',
662 'border-color',
663 ];
664 $this->createShorthandProperties($aProperties, 'border');
665 }
666
667 /**
668 * Looks for long format CSS dimensional properties
669 * (margin, padding, border-color, border-style and border-width)
670 * and converts them into shorthand CSS properties.
671 *
672 * @return void
673 *
674 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
675 */
676 public function createDimensionsShorthand()
677 {
678 $aPositions = ['top', 'right', 'bottom', 'left'];
679 $aExpansions = [
680 'margin' => 'margin-%s',
681 'padding' => 'padding-%s',
682 'border-color' => 'border-%s-color',
683 'border-style' => 'border-%s-style',
684 'border-width' => 'border-%s-width',
685 ];
686 $aRules = $this->getRulesAssoc();
687 foreach ($aExpansions as $sProperty => $sExpanded) {
688 $aFoldable = [];
689 foreach ($aRules as $sRuleName => $oRule) {
690 foreach ($aPositions as $sPosition) {
691 if ($sRuleName == sprintf($sExpanded, $sPosition)) {
692 $aFoldable[$sRuleName] = $oRule;
693 }
694 }
695 }
696 // All four dimensions must be present
697 if (count($aFoldable) == 4) {
698 $aValues = [];
699 foreach ($aPositions as $sPosition) {
700 $oRule = $aRules[sprintf($sExpanded, $sPosition)];
701 $mRuleValue = $oRule->getValue();
702 $aRuleValues = [];
703 if (!$mRuleValue instanceof RuleValueList) {
704 $aRuleValues[] = $mRuleValue;
705 } else {
706 $aRuleValues = $mRuleValue->getListComponents();
707 }
708 $aValues[$sPosition] = $aRuleValues;
709 }
710 $oNewRule = new Rule($sProperty, $oRule->getLineNo(), $oRule->getColNo());
711 if ((string)$aValues['left'][0] == (string)$aValues['right'][0]) {
712 if ((string)$aValues['top'][0] == (string)$aValues['bottom'][0]) {
713 if ((string)$aValues['top'][0] == (string)$aValues['left'][0]) {
714 // All 4 sides are equal
715 $oNewRule->addValue($aValues['top']);
716 } else {
717 // Top and bottom are equal, left and right are equal
718 $oNewRule->addValue($aValues['top']);
719 $oNewRule->addValue($aValues['left']);
720 }
721 } else {
722 // Only left and right are equal
723 $oNewRule->addValue($aValues['top']);
724 $oNewRule->addValue($aValues['left']);
725 $oNewRule->addValue($aValues['bottom']);
726 }
727 } else {
728 // No sides are equal
729 $oNewRule->addValue($aValues['top']);
730 $oNewRule->addValue($aValues['left']);
731 $oNewRule->addValue($aValues['bottom']);
732 $oNewRule->addValue($aValues['right']);
733 }
734 $this->addRule($oNewRule);
735 foreach ($aPositions as $sPosition) {
736 $this->removeRule(sprintf($sExpanded, $sPosition));
737 }
738 }
739 }
740 }
741
742 /**
743 * Looks for long format CSS font properties (e.g. `font-weight`) and
744 * tries to convert them into a shorthand CSS `font` property.
745 *
746 * At least `font-size` AND `font-family` must be present in order to create a shorthand declaration.
747 *
748 * @return void
749 *
750 * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
751 */
752 public function createFontShorthand()
753 {
754 $aFontProperties = [
755 'font-style',
756 'font-variant',
757 'font-weight',
758 'font-size',
759 'line-height',
760 'font-family',
761 ];
762 $aRules = $this->getRulesAssoc();
763 if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) {
764 return;
765 }
766 $oOldRule = isset($aRules['font-size']) ? $aRules['font-size'] : $aRules['font-family'];
767 $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo());
768 unset($oOldRule);
769 foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) {
770 if (isset($aRules[$sProperty])) {
771 $oRule = $aRules[$sProperty];
772 $mRuleValue = $oRule->getValue();
773 $aValues = [];
774 if (!$mRuleValue instanceof RuleValueList) {
775 $aValues[] = $mRuleValue;
776 } else {
777 $aValues = $mRuleValue->getListComponents();
778 }
779 if ($aValues[0] !== 'normal') {
780 $oNewRule->addValue($aValues[0]);
781 }
782 }
783 }
784 // Get the font-size value
785 $oRule = $aRules['font-size'];
786 $mRuleValue = $oRule->getValue();
787 $aFSValues = [];
788 if (!$mRuleValue instanceof RuleValueList) {
789 $aFSValues[] = $mRuleValue;
790 } else {
791 $aFSValues = $mRuleValue->getListComponents();
792 }
793 // But wait to know if we have line-height to add it
794 if (isset($aRules['line-height'])) {
795 $oRule = $aRules['line-height'];
796 $mRuleValue = $oRule->getValue();
797 $aLHValues = [];
798 if (!$mRuleValue instanceof RuleValueList) {
799 $aLHValues[] = $mRuleValue;
800 } else {
801 $aLHValues = $mRuleValue->getListComponents();
802 }
803 if ($aLHValues[0] !== 'normal') {
804 $val = new RuleValueList('/', $this->getLineNo());
805 $val->addListComponent($aFSValues[0]);
806 $val->addListComponent($aLHValues[0]);
807 $oNewRule->addValue($val);
808 }
809 } else {
810 $oNewRule->addValue($aFSValues[0]);
811 }
812 $oRule = $aRules['font-family'];
813 $mRuleValue = $oRule->getValue();
814 $aFFValues = [];
815 if (!$mRuleValue instanceof RuleValueList) {
816 $aFFValues[] = $mRuleValue;
817 } else {
818 $aFFValues = $mRuleValue->getListComponents();
819 }
820 $oFFValue = new RuleValueList(',', $this->getLineNo());
821 $oFFValue->setListComponents($aFFValues);
822 $oNewRule->addValue($oFFValue);
823
824 $this->addRule($oNewRule);
825 foreach ($aFontProperties as $sProperty) {
826 $this->removeRule($sProperty);
827 }
828 }
829
830 /**
831 * @return string
832 *
833 * @throws OutputException
834 *
835 * @deprecated in V8.8.0, will be removed in V9.0.0. Use `render` instead.
836 */
837 public function __toString()
838 {
839 return $this->render(new OutputFormat());
840 }
841
842 /**
843 * @param OutputFormat|null $oOutputFormat
844 *
845 * @return string
846 *
847 * @throws OutputException
848 */
849 public function render($oOutputFormat)
850 {
851 $sResult = $oOutputFormat->comments($this);
852 if (count($this->aSelectors) === 0) {
853 // If all the selectors have been removed, this declaration block becomes invalid
854 throw new OutputException(
855 'Attempt to print declaration block with missing selector',
856 $this->getLineNumber()
857 );
858 }
859 $sResult .= $oOutputFormat->sBeforeDeclarationBlock;
860 $sResult .= $oOutputFormat->implode(
861 $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(),
862 $this->aSelectors
863 );
864 $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors;
865 $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{';
866 $sResult .= $this->renderRules($oOutputFormat);
867 $sResult .= '}';
868 $sResult .= $oOutputFormat->sAfterDeclarationBlock;
869 return $sResult;
870 }
871 }
872