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

MyIntervals / PHP-CSS-Parser / 12998096795

27 Jan 2025 08:52PM UTC coverage: 51.041%. Remained the same
12998096795

Pull #835

github

web-flow
Merge 3657ed1c5 into 9f5c7dc3a
Pull Request #835: [CLEANUP] Avoid Hungarian notation for `oRule`

43 of 71 new or added lines in 4 files covered. (60.56%)

1 existing line in 1 file now uncovered.

1054 of 2065 relevant lines covered (51.04%)

10.55 hits per line

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

83.83
/src/RuleSet/DeclarationBlock.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Sabberworm\CSS\RuleSet;
6

7
use Sabberworm\CSS\CSSList\CSSList;
8
use Sabberworm\CSS\CSSList\KeyFrame;
9
use Sabberworm\CSS\OutputFormat;
10
use Sabberworm\CSS\Parsing\OutputException;
11
use Sabberworm\CSS\Parsing\ParserState;
12
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
13
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
14
use Sabberworm\CSS\Property\KeyframeSelector;
15
use Sabberworm\CSS\Property\Selector;
16
use Sabberworm\CSS\Rule\Rule;
17
use Sabberworm\CSS\Value\Color;
18
use Sabberworm\CSS\Value\RuleValueList;
19
use Sabberworm\CSS\Value\Size;
20
use Sabberworm\CSS\Value\URL;
21
use Sabberworm\CSS\Value\Value;
22

23
/**
24
 * This class represents a `RuleSet` constrained by a `Selector`.
25
 *
26
 * It contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the
27
 * matching elements.
28
 *
29
 * Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
30
 */
31
class DeclarationBlock extends RuleSet
32
{
33
    /**
34
     * @var array<int, Selector|string>
35
     */
36
    private $aSelectors;
37

38
    /**
39
     * @param int $lineNumber
40
     */
41
    public function __construct($lineNumber = 0)
112✔
42
    {
43
        parent::__construct($lineNumber);
112✔
44
        $this->aSelectors = [];
112✔
45
    }
112✔
46

47
    /**
48
     * @param CSSList|null $oList
49
     *
50
     * @return DeclarationBlock|false
51
     *
52
     * @throws UnexpectedTokenException
53
     * @throws UnexpectedEOFException
54
     */
55
    public static function parse(ParserState $oParserState, $oList = null)
112✔
56
    {
57
        $aComments = [];
112✔
58
        $oResult = new DeclarationBlock($oParserState->currentLine());
112✔
59
        try {
60
            $aSelectorParts = [];
112✔
61
            $sStringWrapperChar = false;
112✔
62
            do {
63
                $aSelectorParts[] = $oParserState->consume(1)
112✔
64
                    . $oParserState->consumeUntil(['{', '}', '\'', '"'], false, false, $aComments);
111✔
65
                if (\in_array($oParserState->peek(), ['\'', '"'], true) && \substr(\end($aSelectorParts), -1) != '\\') {
110✔
66
                    if ($sStringWrapperChar === false) {
4✔
67
                        $sStringWrapperChar = $oParserState->peek();
4✔
68
                    } elseif ($sStringWrapperChar == $oParserState->peek()) {
4✔
69
                        $sStringWrapperChar = false;
4✔
70
                    }
71
                }
72
            } while (!\in_array($oParserState->peek(), ['{', '}'], true) || $sStringWrapperChar !== false);
110✔
73
            $oResult->setSelectors(\implode('', $aSelectorParts), $oList);
110✔
74
            if ($oParserState->comes('{')) {
110✔
75
                $oParserState->consume(1);
110✔
76
            }
77
        } catch (UnexpectedTokenException $e) {
8✔
78
            if ($oParserState->getSettings()->bLenientParsing) {
8✔
79
                if (!$oParserState->comes('}')) {
6✔
80
                    $oParserState->consumeUntil('}', false, true);
6✔
81
                }
82
                return false;
4✔
83
            } else {
84
                throw $e;
2✔
85
            }
86
        }
87
        $oResult->setComments($aComments);
110✔
88
        RuleSet::parseRuleSet($oParserState, $oResult);
110✔
89
        return $oResult;
103✔
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): void
110✔
99
    {
100
        if (\is_array($mSelector)) {
110✔
101
            $this->aSelectors = $mSelector;
×
102
        } else {
103
            $this->aSelectors = \explode(',', $mSelector);
110✔
104
        }
105
        foreach ($this->aSelectors as $iKey => $mSelector) {
110✔
106
            if (!($mSelector instanceof Selector)) {
110✔
107
                if ($oList === null || !($oList instanceof KeyFrame)) {
110✔
108
                    if (!Selector::isValid($mSelector)) {
109✔
109
                        throw new UnexpectedTokenException(
4✔
110
                            "Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
4✔
111
                            $mSelector,
112
                            'custom'
4✔
113
                        );
114
                    }
115
                    $this->aSelectors[$iKey] = new Selector($mSelector);
109✔
116
                } else {
117
                    if (!KeyframeSelector::isValid($mSelector)) {
6✔
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);
6✔
125
                }
126
            }
127
        }
128
    }
110✔
129

130
    /**
131
     * Remove one of the selectors of the block.
132
     *
133
     * @param Selector|string $mSelector
134
     */
135
    public function removeSelector($mSelector): bool
1✔
136
    {
137
        if ($mSelector instanceof Selector) {
1✔
138
            $mSelector = $mSelector->getSelector();
×
139
        }
140
        foreach ($this->aSelectors as $iKey => $oSelector) {
1✔
141
            if ($oSelector->getSelector() === $mSelector) {
1✔
142
                unset($this->aSelectors[$iKey]);
1✔
143
                return true;
1✔
144
            }
145
        }
146
        return false;
1✔
147
    }
148

149
    /**
150
     * @return array<int, Selector|string>
151
     */
152
    public function getSelectors()
6✔
153
    {
154
        return $this->aSelectors;
6✔
155
    }
156

157
    /**
158
     * Splits shorthand declarations (e.g. `margin` or `font`) into their constituent parts.
159
     *
160
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
161
     */
162
    public function expandShorthands(): void
2✔
163
    {
164
        // border must be expanded before dimensions
165
        $this->expandBorderShorthand();
2✔
166
        $this->expandDimensionsShorthand();
2✔
167
        $this->expandFontShorthand();
2✔
168
        $this->expandBackgroundShorthand();
2✔
169
        $this->expandListStyleShorthand();
2✔
170
    }
2✔
171

172
    /**
173
     * Creates shorthand declarations (e.g. `margin` or `font`) whenever possible.
174
     *
175
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
176
     */
177
    public function createShorthands(): void
1✔
178
    {
179
        $this->createBackgroundShorthand();
1✔
180
        $this->createDimensionsShorthand();
1✔
181
        // border must be shortened after dimensions
182
        $this->createBorderShorthand();
1✔
183
        $this->createFontShorthand();
1✔
184
        $this->createListStyleShorthand();
1✔
185
    }
1✔
186

187
    /**
188
     * Splits shorthand border declarations (e.g. `border: 1px red;`).
189
     *
190
     * Additional splitting happens in expandDimensionsShorthand.
191
     *
192
     * Multiple borders are not yet supported as of 3.
193
     *
194
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
195
     */
196
    public function expandBorderShorthand(): void
8✔
197
    {
198
        $aBorderRules = [
199
            'border',
8✔
200
            'border-left',
201
            'border-right',
202
            'border-top',
203
            'border-bottom',
204
        ];
205
        $aBorderSizes = [
206
            'thin',
8✔
207
            'medium',
208
            'thick',
209
        ];
210
        $aRules = $this->getRulesAssoc();
8✔
211
        foreach ($aBorderRules as $sBorderRule) {
8✔
212
            if (!isset($aRules[$sBorderRule])) {
8✔
213
                continue;
8✔
214
            }
215
            $rule = $aRules[$sBorderRule];
6✔
216
            $mRuleValue = $rule->getValue();
6✔
217
            $aValues = [];
6✔
218
            if (!$mRuleValue instanceof RuleValueList) {
6✔
219
                $aValues[] = $mRuleValue;
3✔
220
            } else {
221
                $aValues = $mRuleValue->getListComponents();
3✔
222
            }
223
            foreach ($aValues as $mValue) {
6✔
224
                if ($mValue instanceof Value) {
6✔
225
                    $mNewValue = clone $mValue;
5✔
226
                } else {
227
                    $mNewValue = $mValue;
4✔
228
                }
229
                if ($mValue instanceof Size) {
6✔
230
                    $sNewRuleName = $sBorderRule . '-width';
4✔
231
                } elseif ($mValue instanceof Color) {
5✔
232
                    $sNewRuleName = $sBorderRule . '-color';
3✔
233
                } else {
234
                    if (\in_array($mValue, $aBorderSizes, true)) {
4✔
235
                        $sNewRuleName = $sBorderRule . '-width';
×
236
                    } else {
237
                        $sNewRuleName = $sBorderRule . '-style';
4✔
238
                    }
239
                }
240
                $oNewRule = new Rule($sNewRuleName, $rule->getLineNo(), $rule->getColNo());
6✔
241
                $oNewRule->setIsImportant($rule->getIsImportant());
6✔
242
                $oNewRule->addValue([$mNewValue]);
6✔
243
                $this->addRule($oNewRule);
6✔
244
            }
245
            $this->removeRule($sBorderRule);
6✔
246
        }
247
    }
8✔
248

249
    /**
250
     * Splits shorthand dimensional declarations (e.g. `margin: 0px auto;`)
251
     * into their constituent parts.
252
     *
253
     * Handles `margin`, `padding`, `border-color`, `border-style` and `border-width`.
254
     *
255
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
256
     */
257
    public function expandDimensionsShorthand(): void
7✔
258
    {
259
        $aExpansions = [
260
            'margin' => 'margin-%s',
7✔
261
            'padding' => 'padding-%s',
262
            'border-color' => 'border-%s-color',
263
            'border-style' => 'border-%s-style',
264
            'border-width' => 'border-%s-width',
265
        ];
266
        $aRules = $this->getRulesAssoc();
7✔
267
        foreach ($aExpansions as $sProperty => $sExpanded) {
7✔
268
            if (!isset($aRules[$sProperty])) {
7✔
269
                continue;
6✔
270
            }
271
            $rule = $aRules[$sProperty];
5✔
272
            $mRuleValue = $rule->getValue();
5✔
273
            $aValues = [];
5✔
274
            if (!$mRuleValue instanceof RuleValueList) {
5✔
275
                $aValues[] = $mRuleValue;
3✔
276
            } else {
277
                $aValues = $mRuleValue->getListComponents();
3✔
278
            }
279
            $top = $right = $bottom = $left = null;
5✔
280
            switch (\count($aValues)) {
5✔
281
                case 1:
5✔
282
                    $top = $right = $bottom = $left = $aValues[0];
3✔
283
                    break;
3✔
284
                case 2:
3✔
285
                    $top = $bottom = $aValues[0];
1✔
286
                    $left = $right = $aValues[1];
1✔
287
                    break;
1✔
288
                case 3:
2✔
289
                    $top = $aValues[0];
2✔
290
                    $left = $right = $aValues[1];
2✔
291
                    $bottom = $aValues[2];
2✔
292
                    break;
2✔
293
                case 4:
×
294
                    $top = $aValues[0];
×
295
                    $right = $aValues[1];
×
296
                    $bottom = $aValues[2];
×
297
                    $left = $aValues[3];
×
298
                    break;
×
299
            }
300
            foreach (['top', 'right', 'bottom', 'left'] as $sPosition) {
5✔
301
                $oNewRule = new Rule(\sprintf($sExpanded, $sPosition), $rule->getLineNo(), $rule->getColNo());
5✔
302
                $oNewRule->setIsImportant($rule->getIsImportant());
5✔
303
                $oNewRule->addValue(${$sPosition});
5✔
304
                $this->addRule($oNewRule);
5✔
305
            }
306
            $this->removeRule($sProperty);
5✔
307
        }
308
    }
7✔
309

310
    /**
311
     * Converts shorthand font declarations
312
     * (e.g. `font: 300 italic 11px/14px verdana, helvetica, sans-serif;`)
313
     * into their constituent parts.
314
     *
315
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
316
     */
317
    public function expandFontShorthand(): void
8✔
318
    {
319
        $aRules = $this->getRulesAssoc();
8✔
320
        if (!isset($aRules['font'])) {
8✔
321
            return;
2✔
322
        }
323
        $rule = $aRules['font'];
6✔
324
        // reset properties to 'normal' per http://www.w3.org/TR/21/fonts.html#font-shorthand
325
        $aFontProperties = [
326
            'font-style' => 'normal',
6✔
327
            'font-variant' => 'normal',
328
            'font-weight' => 'normal',
329
            'font-size' => 'normal',
330
            'line-height' => 'normal',
331
        ];
332
        $mRuleValue = $rule->getValue();
6✔
333
        $aValues = [];
6✔
334
        if (!$mRuleValue instanceof RuleValueList) {
6✔
335
            $aValues[] = $mRuleValue;
×
336
        } else {
337
            $aValues = $mRuleValue->getListComponents();
6✔
338
        }
339
        foreach ($aValues as $mValue) {
6✔
340
            if (!$mValue instanceof Value) {
6✔
341
                $mValue = \mb_strtolower($mValue);
6✔
342
            }
343
            if (\in_array($mValue, ['normal', 'inherit'], true)) {
6✔
344
                foreach (['font-style', 'font-weight', 'font-variant'] as $sProperty) {
×
345
                    if (!isset($aFontProperties[$sProperty])) {
×
346
                        $aFontProperties[$sProperty] = $mValue;
×
347
                    }
348
                }
349
            } elseif (\in_array($mValue, ['italic', 'oblique'], true)) {
×
350
                $aFontProperties['font-style'] = $mValue;
5✔
351
            } elseif ($mValue == 'small-caps') {
6✔
352
                $aFontProperties['font-variant'] = $mValue;
1✔
353
            } elseif (
354
                \in_array($mValue, ['bold', 'bolder', 'lighter'], true)
×
355
                || ($mValue instanceof Size && \in_array($mValue->getSize(), \range(100.0, 900.0, 100.0), true))
6✔
356
            ) {
357
                $aFontProperties['font-weight'] = $mValue;
4✔
358
            } elseif ($mValue instanceof RuleValueList && $mValue->getListSeparator() == '/') {
6✔
359
                [$oSize, $oHeight] = $mValue->getListComponents();
3✔
360
                $aFontProperties['font-size'] = $oSize;
3✔
361
                $aFontProperties['line-height'] = $oHeight;
3✔
362
            } elseif ($mValue instanceof Size && $mValue->getUnit() !== null) {
6✔
363
                $aFontProperties['font-size'] = $mValue;
3✔
364
            } else {
365
                $aFontProperties['font-family'] = $mValue;
6✔
366
            }
367
        }
368
        foreach ($aFontProperties as $sProperty => $mValue) {
6✔
369
            $oNewRule = new Rule($sProperty, $rule->getLineNo(), $rule->getColNo());
6✔
370
            $oNewRule->addValue($mValue);
6✔
371
            $oNewRule->setIsImportant($rule->getIsImportant());
6✔
372
            $this->addRule($oNewRule);
6✔
373
        }
374
        $this->removeRule('font');
6✔
375
    }
6✔
376

377
    /**
378
     * Converts shorthand background declarations
379
     * (e.g. `background: url("chess.png") gray 50% repeat fixed;`)
380
     * into their constituent parts.
381
     *
382
     * @see http://www.w3.org/TR/21/colors.html#propdef-background
383
     *
384
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
385
     */
386
    public function expandBackgroundShorthand(): void
8✔
387
    {
388
        $aRules = $this->getRulesAssoc();
8✔
389
        if (!isset($aRules['background'])) {
8✔
390
            return;
2✔
391
        }
392
        $rule = $aRules['background'];
6✔
393
        $aBgProperties = [
394
            'background-color' => ['transparent'],
6✔
395
            'background-image' => ['none'],
396
            'background-repeat' => ['repeat'],
397
            'background-attachment' => ['scroll'],
398
            'background-position' => [
399
                new Size(0, '%', false, $this->lineNumber),
6✔
400
                new Size(0, '%', false, $this->lineNumber),
6✔
401
            ],
402
        ];
403
        $mRuleValue = $rule->getValue();
6✔
404
        $aValues = [];
6✔
405
        if (!$mRuleValue instanceof RuleValueList) {
6✔
406
            $aValues[] = $mRuleValue;
1✔
407
        } else {
408
            $aValues = $mRuleValue->getListComponents();
5✔
409
        }
410
        if (\count($aValues) == 1 && $aValues[0] == 'inherit') {
6✔
411
            foreach ($aBgProperties as $sProperty => $mValue) {
×
NEW
412
                $oNewRule = new Rule($sProperty, $rule->getLineNo(), $rule->getColNo());
×
413
                $oNewRule->addValue('inherit');
×
NEW
414
                $oNewRule->setIsImportant($rule->getIsImportant());
×
415
                $this->addRule($oNewRule);
×
416
            }
417
            $this->removeRule('background');
×
418
            return;
×
419
        }
420
        $iNumBgPos = 0;
6✔
421
        foreach ($aValues as $mValue) {
6✔
422
            if (!$mValue instanceof Value) {
6✔
423
                $mValue = \mb_strtolower($mValue);
4✔
424
            }
425
            if ($mValue instanceof URL) {
6✔
426
                $aBgProperties['background-image'] = $mValue;
5✔
427
            } elseif ($mValue instanceof Color) {
6✔
428
                $aBgProperties['background-color'] = $mValue;
6✔
429
            } elseif (\in_array($mValue, ['scroll', 'fixed'], true)) {
×
430
                $aBgProperties['background-attachment'] = $mValue;
×
431
            } elseif (\in_array($mValue, ['repeat', 'no-repeat', 'repeat-x', 'repeat-y'], true)) {
×
432
                $aBgProperties['background-repeat'] = $mValue;
4✔
433
            } elseif (
434
                \in_array($mValue, ['left', 'center', 'right', 'top', 'bottom'], true)
×
435
                || $mValue instanceof Size
3✔
436
            ) {
437
                if ($iNumBgPos == 0) {
3✔
438
                    $aBgProperties['background-position'][0] = $mValue;
3✔
439
                    $aBgProperties['background-position'][1] = 'center';
3✔
440
                } else {
441
                    $aBgProperties['background-position'][$iNumBgPos] = $mValue;
2✔
442
                }
443
                $iNumBgPos++;
3✔
444
            }
445
        }
446
        foreach ($aBgProperties as $sProperty => $mValue) {
6✔
447
            $oNewRule = new Rule($sProperty, $rule->getLineNo(), $rule->getColNo());
6✔
448
            $oNewRule->setIsImportant($rule->getIsImportant());
6✔
449
            $oNewRule->addValue($mValue);
6✔
450
            $this->addRule($oNewRule);
6✔
451
        }
452
        $this->removeRule('background');
6✔
453
    }
6✔
454

455
    /**
456
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
457
     */
458
    public function expandListStyleShorthand(): void
2✔
459
    {
460
        $aListProperties = [
461
            'list-style-type' => 'disc',
2✔
462
            'list-style-position' => 'outside',
463
            'list-style-image' => 'none',
464
        ];
465
        $aListStyleTypes = [
466
            'none',
2✔
467
            'disc',
468
            'circle',
469
            'square',
470
            'decimal-leading-zero',
471
            'decimal',
472
            'lower-roman',
473
            'upper-roman',
474
            'lower-greek',
475
            'lower-alpha',
476
            'lower-latin',
477
            'upper-alpha',
478
            'upper-latin',
479
            'hebrew',
480
            'armenian',
481
            'georgian',
482
            'cjk-ideographic',
483
            'hiragana',
484
            'hira-gana-iroha',
485
            'katakana-iroha',
486
            'katakana',
487
        ];
488
        $aListStylePositions = [
489
            'inside',
2✔
490
            'outside',
491
        ];
492
        $aRules = $this->getRulesAssoc();
2✔
493
        if (!isset($aRules['list-style'])) {
2✔
494
            return;
2✔
495
        }
NEW
496
        $rule = $aRules['list-style'];
×
NEW
497
        $mRuleValue = $rule->getValue();
×
498
        $aValues = [];
×
499
        if (!$mRuleValue instanceof RuleValueList) {
×
500
            $aValues[] = $mRuleValue;
×
501
        } else {
502
            $aValues = $mRuleValue->getListComponents();
×
503
        }
504
        if (\count($aValues) == 1 && $aValues[0] == 'inherit') {
×
505
            foreach ($aListProperties as $sProperty => $mValue) {
×
NEW
506
                $oNewRule = new Rule($sProperty, $rule->getLineNo(), $rule->getColNo());
×
507
                $oNewRule->addValue('inherit');
×
NEW
508
                $oNewRule->setIsImportant($rule->getIsImportant());
×
509
                $this->addRule($oNewRule);
×
510
            }
511
            $this->removeRule('list-style');
×
512
            return;
×
513
        }
514
        foreach ($aValues as $mValue) {
×
515
            if (!$mValue instanceof Value) {
×
516
                $mValue = \mb_strtolower($mValue);
×
517
            }
518
            if ($mValue instanceof Url) {
×
519
                $aListProperties['list-style-image'] = $mValue;
×
520
            } elseif (\in_array($mValue, $aListStyleTypes, true)) {
×
521
                $aListProperties['list-style-types'] = $mValue;
×
522
            } elseif (\in_array($mValue, $aListStylePositions, true)) {
×
523
                $aListProperties['list-style-position'] = $mValue;
×
524
            }
525
        }
526
        foreach ($aListProperties as $sProperty => $mValue) {
×
NEW
527
            $oNewRule = new Rule($sProperty, $rule->getLineNo(), $rule->getColNo());
×
NEW
528
            $oNewRule->setIsImportant($rule->getIsImportant());
×
529
            $oNewRule->addValue($mValue);
×
530
            $this->addRule($oNewRule);
×
531
        }
532
        $this->removeRule('list-style');
×
533
    }
×
534

535
    /**
536
     * @param array<array-key, string> $aProperties
537
     * @param string $sShorthand
538
     *
539
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
540
     */
541
    public function createShorthandProperties(array $aProperties, $sShorthand): void
12✔
542
    {
543
        $aRules = $this->getRulesAssoc();
12✔
544
        $rule = null;
12✔
545
        $aNewValues = [];
12✔
546
        foreach ($aProperties as $sProperty) {
12✔
547
            if (!isset($aRules[$sProperty])) {
12✔
548
                continue;
11✔
549
            }
550
            $rule = $aRules[$sProperty];
10✔
551
            if (!$rule->getIsImportant()) {
10✔
552
                $mRuleValue = $rule->getValue();
10✔
553
                $aValues = [];
10✔
554
                if (!$mRuleValue instanceof RuleValueList) {
10✔
555
                    $aValues[] = $mRuleValue;
10✔
556
                } else {
557
                    $aValues = $mRuleValue->getListComponents();
1✔
558
                }
559
                foreach ($aValues as $mValue) {
10✔
560
                    $aNewValues[] = $mValue;
10✔
561
                }
562
                $this->removeRule($sProperty);
10✔
563
            }
564
        }
565
        if ($aNewValues !== [] && $rule instanceof Rule) {
12✔
566
            $oNewRule = new Rule($sShorthand, $rule->getLineNo(), $rule->getColNo());
10✔
567
            foreach ($aNewValues as $mValue) {
10✔
568
                $oNewRule->addValue($mValue);
10✔
569
            }
570
            $this->addRule($oNewRule);
10✔
571
        }
572
    }
12✔
573

574
    /**
575
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
576
     */
577
    public function createBackgroundShorthand(): void
8✔
578
    {
579
        $aProperties = [
580
            'background-color',
8✔
581
            'background-image',
582
            'background-repeat',
583
            'background-position',
584
            'background-attachment',
585
        ];
586
        $this->createShorthandProperties($aProperties, 'background');
8✔
587
    }
8✔
588

589
    /**
590
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
591
     */
592
    public function createListStyleShorthand(): void
1✔
593
    {
594
        $aProperties = [
595
            'list-style-type',
1✔
596
            'list-style-position',
597
            'list-style-image',
598
        ];
599
        $this->createShorthandProperties($aProperties, 'list-style');
1✔
600
    }
1✔
601

602
    /**
603
     * Combines `border-color`, `border-style` and `border-width` into `border`.
604
     *
605
     * Should be run after `create_dimensions_shorthand`!
606
     *
607
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
608
     */
609
    public function createBorderShorthand(): void
5✔
610
    {
611
        $aProperties = [
612
            'border-width',
5✔
613
            'border-style',
614
            'border-color',
615
        ];
616
        $this->createShorthandProperties($aProperties, 'border');
5✔
617
    }
5✔
618

619
    /**
620
     * Looks for long format CSS dimensional properties
621
     * (margin, padding, border-color, border-style and border-width)
622
     * and converts them into shorthand CSS properties.
623
     *
624
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
625
     */
626
    public function createDimensionsShorthand(): void
6✔
627
    {
628
        $aPositions = ['top', 'right', 'bottom', 'left'];
6✔
629
        $aExpansions = [
630
            'margin' => 'margin-%s',
6✔
631
            'padding' => 'padding-%s',
632
            'border-color' => 'border-%s-color',
633
            'border-style' => 'border-%s-style',
634
            'border-width' => 'border-%s-width',
635
        ];
636
        $aRules = $this->getRulesAssoc();
6✔
637
        foreach ($aExpansions as $sProperty => $sExpanded) {
6✔
638
            $aFoldable = [];
6✔
639
            foreach ($aRules as $sRuleName => $rule) {
6✔
640
                foreach ($aPositions as $sPosition) {
6✔
641
                    if ($sRuleName == \sprintf($sExpanded, $sPosition)) {
6✔
642
                        $aFoldable[$sRuleName] = $rule;
5✔
643
                    }
644
                }
645
            }
646
            // All four dimensions must be present
647
            if (\count($aFoldable) == 4) {
6✔
648
                $aValues = [];
4✔
649
                foreach ($aPositions as $sPosition) {
4✔
650
                    $rule = $aRules[\sprintf($sExpanded, $sPosition)];
4✔
651
                    $mRuleValue = $rule->getValue();
4✔
652
                    $aRuleValues = [];
4✔
653
                    if (!$mRuleValue instanceof RuleValueList) {
4✔
654
                        $aRuleValues[] = $mRuleValue;
4✔
655
                    } else {
656
                        $aRuleValues = $mRuleValue->getListComponents();
×
657
                    }
658
                    $aValues[$sPosition] = $aRuleValues;
4✔
659
                }
660
                $oNewRule = new Rule($sProperty, $rule->getLineNo(), $rule->getColNo());
4✔
661
                if ((string) $aValues['left'][0] == (string) $aValues['right'][0]) {
4✔
662
                    if ((string) $aValues['top'][0] == (string) $aValues['bottom'][0]) {
3✔
663
                        if ((string) $aValues['top'][0] == (string) $aValues['left'][0]) {
2✔
664
                            // All 4 sides are equal
665
                            $oNewRule->addValue($aValues['top']);
1✔
666
                        } else {
667
                            // Top and bottom are equal, left and right are equal
668
                            $oNewRule->addValue($aValues['top']);
1✔
669
                            $oNewRule->addValue($aValues['left']);
2✔
670
                        }
671
                    } else {
672
                        // Only left and right are equal
673
                        $oNewRule->addValue($aValues['top']);
1✔
674
                        $oNewRule->addValue($aValues['left']);
1✔
675
                        $oNewRule->addValue($aValues['bottom']);
3✔
676
                    }
677
                } else {
678
                    // No sides are equal
679
                    $oNewRule->addValue($aValues['top']);
1✔
680
                    $oNewRule->addValue($aValues['left']);
1✔
681
                    $oNewRule->addValue($aValues['bottom']);
1✔
682
                    $oNewRule->addValue($aValues['right']);
1✔
683
                }
684
                $this->addRule($oNewRule);
4✔
685
                foreach ($aPositions as $sPosition) {
4✔
686
                    $this->removeRule(\sprintf($sExpanded, $sPosition));
4✔
687
                }
688
            }
689
        }
690
    }
6✔
691

692
    /**
693
     * Looks for long format CSS font properties (e.g. `font-weight`) and
694
     * tries to convert them into a shorthand CSS `font` property.
695
     *
696
     * At least `font-size` AND `font-family` must be present in order to create a shorthand declaration.
697
     *
698
     * @deprecated since 8.7.0, will be removed without substitution in version 9.0 in #511
699
     */
700
    public function createFontShorthand(): void
7✔
701
    {
702
        $aFontProperties = [
703
            'font-style',
7✔
704
            'font-variant',
705
            'font-weight',
706
            'font-size',
707
            'line-height',
708
            'font-family',
709
        ];
710
        $aRules = $this->getRulesAssoc();
7✔
711
        if (!isset($aRules['font-size']) || !isset($aRules['font-family'])) {
7✔
712
            return;
1✔
713
        }
714
        $oOldRule = $aRules['font-size'] ?? $aRules['font-family'];
6✔
715
        $oNewRule = new Rule('font', $oOldRule->getLineNo(), $oOldRule->getColNo());
6✔
716
        unset($oOldRule);
6✔
717
        foreach (['font-style', 'font-variant', 'font-weight'] as $sProperty) {
6✔
718
            if (isset($aRules[$sProperty])) {
6✔
719
                $rule = $aRules[$sProperty];
5✔
720
                $mRuleValue = $rule->getValue();
5✔
721
                $aValues = [];
5✔
722
                if (!$mRuleValue instanceof RuleValueList) {
5✔
723
                    $aValues[] = $mRuleValue;
5✔
724
                } else {
725
                    $aValues = $mRuleValue->getListComponents();
×
726
                }
727
                if ($aValues[0] !== 'normal') {
5✔
728
                    $oNewRule->addValue($aValues[0]);
5✔
729
                }
730
            }
731
        }
732
        // Get the font-size value
733
        $rule = $aRules['font-size'];
6✔
734
        $mRuleValue = $rule->getValue();
6✔
735
        $aFSValues = [];
6✔
736
        if (!$mRuleValue instanceof RuleValueList) {
6✔
737
            $aFSValues[] = $mRuleValue;
6✔
738
        } else {
739
            $aFSValues = $mRuleValue->getListComponents();
×
740
        }
741
        // But wait to know if we have line-height to add it
742
        if (isset($aRules['line-height'])) {
6✔
743
            $rule = $aRules['line-height'];
2✔
744
            $mRuleValue = $rule->getValue();
2✔
745
            $aLHValues = [];
2✔
746
            if (!$mRuleValue instanceof RuleValueList) {
2✔
747
                $aLHValues[] = $mRuleValue;
2✔
748
            } else {
749
                $aLHValues = $mRuleValue->getListComponents();
×
750
            }
751
            if ($aLHValues[0] !== 'normal') {
2✔
752
                $val = new RuleValueList('/', $this->lineNumber);
2✔
753
                $val->addListComponent($aFSValues[0]);
2✔
754
                $val->addListComponent($aLHValues[0]);
2✔
755
                $oNewRule->addValue($val);
2✔
756
            }
757
        } else {
758
            $oNewRule->addValue($aFSValues[0]);
4✔
759
        }
760
        $rule = $aRules['font-family'];
6✔
761
        $mRuleValue = $rule->getValue();
6✔
762
        $aFFValues = [];
6✔
763
        if (!$mRuleValue instanceof RuleValueList) {
6✔
764
            $aFFValues[] = $mRuleValue;
5✔
765
        } else {
766
            $aFFValues = $mRuleValue->getListComponents();
1✔
767
        }
768
        $oFFValue = new RuleValueList(',', $this->lineNumber);
6✔
769
        $oFFValue->setListComponents($aFFValues);
6✔
770
        $oNewRule->addValue($oFFValue);
6✔
771

772
        $this->addRule($oNewRule);
6✔
773
        foreach ($aFontProperties as $sProperty) {
6✔
774
            $this->removeRule($sProperty);
6✔
775
        }
776
    }
6✔
777

778
    /**
779
     * @throws OutputException
780
     */
781
    public function __toString(): string
×
782
    {
783
        return $this->render(new OutputFormat());
×
784
    }
785

786
    /**
787
     * @throws OutputException
788
     */
789
    public function render(OutputFormat $oOutputFormat): string
89✔
790
    {
791
        $sResult = $oOutputFormat->comments($this);
89✔
792
        if (\count($this->aSelectors) === 0) {
89✔
793
            // If all the selectors have been removed, this declaration block becomes invalid
794
            throw new OutputException('Attempt to print declaration block with missing selector', $this->lineNumber);
1✔
795
        }
796
        $sResult .= $oOutputFormat->sBeforeDeclarationBlock;
89✔
797
        $sResult .= $oOutputFormat->implode(
89✔
798
            $oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(),
89✔
799
            $this->aSelectors
89✔
800
        );
801
        $sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors;
89✔
802
        $sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{';
89✔
803
        $sResult .= $this->renderRules($oOutputFormat);
89✔
804
        $sResult .= '}';
89✔
805
        $sResult .= $oOutputFormat->sAfterDeclarationBlock;
89✔
806
        return $sResult;
89✔
807
    }
808
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc