Skip to content

Commit 8f7d869

Browse files
committed
Merge branch 'upstream_master' into fix/css4-rgb-parsing
2 parents 6934301 + cc791ad commit 8f7d869

File tree

16 files changed

+261
-25
lines changed

16 files changed

+261
-25
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,13 @@ jobs:
5353
uses: shivammathur/setup-php@v2
5454
with:
5555
php-version: ${{ matrix.php-version }}
56+
ini-values: error_reporting=E_ALL
5657
tools: composer:v2
5758
coverage: "${{ matrix.coverage }}"
5859

60+
- name: Show the Composer configuration
61+
run: composer config --global --list
62+
5963
- name: Cache dependencies installed with composer
6064
uses: actions/cache@v3
6165
with:
@@ -108,6 +112,9 @@ jobs:
108112
tools: "composer:v2, phive"
109113
coverage: none
110114

115+
- name: Show the Composer configuration
116+
run: composer config --global --list
117+
111118
- name: Cache dependencies installed with composer
112119
uses: actions/cache@v3
113120
with:

.phive/phars.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<phive xmlns="https://phar.io/phive">
3-
<phar name="php-cs-fixer" version="^3.9.4" installed="3.9.4" location="./.phive/php-cs-fixer.phar" copy="false"/>
3+
<phar name="php-cs-fixer" version="^3.13.2" installed="3.13.2" location="./.phive/php-cs-fixer.phar" copy="false"/>
44
<phar name="phpcbf" version="^3.7.1" installed="3.7.1" location="./.phive/phpcbf.phar" copy="false"/>
55
<phar name="phpcs" version="^3.7.1" installed="3.7.1" location="./.phive/phpcs.phar" copy="false"/>
6-
<phar name="phpstan" version="^1.8.1" installed="1.8.1" location="./.phive/phpstan.phar" copy="false"/>
6+
<phar name="phpstan" version="^1.9.14" installed="1.9.14" location="./.phive/phpstan.phar" copy="false"/>
77
</phive>

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ $cssDocument = $parser->parse();
157157
foreach($cssDocument->getAllRuleSets() as $oRuleSet) {
158158
// Note that the added dash will make this remove all rules starting with
159159
// `font-` (like `font-size`, `font-weight`, etc.) as well as a potential
160-
// `font-rule`.
160+
// `font` rule.
161161
$oRuleSet->removeRule('font-');
162162
$oRuleSet->removeRule('cursor');
163163
}

src/Parsing/Anchor.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Parsing;
4+
5+
class Anchor
6+
{
7+
/**
8+
* @var int
9+
*/
10+
private $iPosition;
11+
12+
/**
13+
* @var \Sabberworm\CSS\Parsing\ParserState
14+
*/
15+
private $oParserState;
16+
17+
/**
18+
* @param int $iPosition
19+
* @param \Sabberworm\CSS\Parsing\ParserState $oParserState
20+
*/
21+
public function __construct($iPosition, ParserState $oParserState)
22+
{
23+
$this->iPosition = $iPosition;
24+
$this->oParserState = $oParserState;
25+
}
26+
27+
/**
28+
* @return void
29+
*/
30+
public function backtrack()
31+
{
32+
$this->oParserState->setPosition($this->iPosition);
33+
}
34+
}

src/Parsing/ParserState.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ public function getSettings()
112112
return $this->oParserSettings;
113113
}
114114

115+
/**
116+
* @return \Sabberworm\CSS\Parsing\Anchor
117+
*/
118+
public function anchor()
119+
{
120+
return new Anchor($this->iCurrentPosition, $this);
121+
}
122+
123+
/**
124+
* @param int $iPosition
125+
*
126+
* @return void
127+
*/
128+
public function setPosition($iPosition)
129+
{
130+
$this->iCurrentPosition = $iPosition;
131+
}
132+
115133
/**
116134
* @param bool $bIgnoreCase
117135
*
@@ -121,12 +139,15 @@ public function getSettings()
121139
*/
122140
public function parseIdentifier($bIgnoreCase = true)
123141
{
142+
if ($this->isEnd()) {
143+
throw new UnexpectedEOFException('', '', 'identifier', $this->iLineNo);
144+
}
124145
$sResult = $this->parseCharacter(true);
125146
if ($sResult === null) {
126147
throw new UnexpectedTokenException($sResult, $this->peek(5), 'identifier', $this->iLineNo);
127148
}
128149
$sCharacter = null;
129-
while (($sCharacter = $this->parseCharacter(true)) !== null) {
150+
while (!$this->isEnd() && ($sCharacter = $this->parseCharacter(true)) !== null) {
130151
if (preg_match('/[a-zA-Z0-9\x{00A0}-\x{FFFF}_-]/Sux', $sCharacter)) {
131152
$sResult .= $sCharacter;
132153
} else {

src/Property/Import.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,12 @@ public function setComments(array $aComments)
134134
{
135135
$this->aComments = $aComments;
136136
}
137+
138+
/**
139+
* @return string
140+
*/
141+
public function getMediaQuery()
142+
{
143+
return $this->sMediaQuery;
144+
}
137145
}

src/Value/CSSFunction.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Sabberworm\CSS\Value;
44

55
use Sabberworm\CSS\OutputFormat;
6+
use Sabberworm\CSS\Parsing\ParserState;
67

78
/**
89
* A `CSSFunction` represents a special kind of value that also contains a function name and where the values are the
@@ -32,6 +33,26 @@ public function __construct($sName, $aArguments, $sSeparator = ',', $iLineNo = 0
3233
parent::__construct($aArguments, $sSeparator, $iLineNo);
3334
}
3435

36+
/**
37+
* @param ParserState $oParserState
38+
* @param bool $bIgnoreCase
39+
*
40+
* @return CSSFunction
41+
*
42+
* @throws SourceException
43+
* @throws UnexpectedEOFException
44+
* @throws UnexpectedTokenException
45+
*/
46+
public static function parse(ParserState $oParserState, $bIgnoreCase = false)
47+
{
48+
$mResult = $oParserState->parseIdentifier($bIgnoreCase);
49+
$oParserState->consume('(');
50+
$aArguments = Value::parseValue($oParserState, ['=', ' ', ',']);
51+
$mResult = new CSSFunction($mResult, $aArguments, ',', $oParserState->currentLine());
52+
$oParserState->consume(')');
53+
return $mResult;
54+
}
55+
3556
/**
3657
* @return string
3758
*/

src/Value/CalcFunction.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,35 @@ class CalcFunction extends CSSFunction
1919
const T_OPERATOR = 2;
2020

2121
/**
22+
* @param ParserState $oParserState
23+
* @param bool $bIgnoreCase
24+
*
2225
* @return CalcFunction
2326
*
2427
* @throws UnexpectedTokenException
2528
* @throws UnexpectedEOFException
2629
*/
27-
public static function parse(ParserState $oParserState)
30+
public static function parse(ParserState $oParserState, $bIgnoreCase = false)
2831
{
2932
$aOperators = ['+', '-', '*', '/'];
30-
$sFunction = trim($oParserState->consumeUntil('(', false, true));
33+
$sFunction = $oParserState->parseIdentifier();
34+
if ($oParserState->peek() != '(') {
35+
// Found ; or end of line before an opening bracket
36+
throw new UnexpectedTokenException('(', $oParserState->peek(), 'literal', $oParserState->currentLine());
37+
} elseif (!in_array($sFunction, ['calc', '-moz-calc', '-webkit-calc'])) {
38+
// Found invalid calc definition. Example calc (...
39+
throw new UnexpectedTokenException('calc', $sFunction, 'literal', $oParserState->currentLine());
40+
}
41+
$oParserState->consume('(');
3142
$oCalcList = new CalcRuleValueList($oParserState->currentLine());
3243
$oList = new RuleValueList(',', $oParserState->currentLine());
3344
$iNestingLevel = 0;
3445
$iLastComponentType = null;
3546
while (!$oParserState->comes(')') || $iNestingLevel > 0) {
47+
if ($oParserState->isEnd() && $iNestingLevel === 0) {
48+
break;
49+
}
50+
3651
$oParserState->consumeWhiteSpace();
3752
if ($oParserState->comes('(')) {
3853
$iNestingLevel++;
@@ -83,7 +98,9 @@ public static function parse(ParserState $oParserState)
8398
$oParserState->consumeWhiteSpace();
8499
}
85100
$oList->addListComponent($oCalcList);
86-
$oParserState->consume(')');
101+
if (!$oParserState->isEnd()) {
102+
$oParserState->consume(')');
103+
}
87104
return new CalcFunction($sFunction, $oList, ',', $oParserState->currentLine());
88105
}
89106
}

src/Value/Color.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ public function __construct(array $aColor, $iLineNo = 0)
2323
}
2424

2525
/**
26+
* @param ParserState $oParserState
27+
* @param bool $bIgnoreCase
28+
*
2629
* @return Color|CSSFunction
2730
*
2831
* @throws UnexpectedEOFException
2932
* @throws UnexpectedTokenException
3033
*/
31-
public static function parse(ParserState $oParserState)
34+
public static function parse(ParserState $oParserState, $bIgnoreCase = false)
3235
{
3336
$aColor = [];
3437
if ($oParserState->comes('#')) {

src/Value/Size.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,16 @@ public static function parse(ParserState $oParserState, $bIsColorComponent = fal
7777
if ($oParserState->comes('-')) {
7878
$sSize .= $oParserState->consume('-');
7979
}
80-
while (is_numeric($oParserState->peek()) || $oParserState->comes('.')) {
80+
while (is_numeric($oParserState->peek()) || $oParserState->comes('.') || $oParserState->comes('e', true)) {
8181
if ($oParserState->comes('.')) {
8282
$sSize .= $oParserState->consume('.');
83+
} elseif ($oParserState->comes('e', true)) {
84+
$sLookahead = $oParserState->peek(1, 1);
85+
if (is_numeric($sLookahead) || $sLookahead === '+' || $sLookahead === '-') {
86+
$sSize .= $oParserState->consume(2);
87+
} else {
88+
break; // Reached the unit part of the number like "em" or "ex"
89+
}
8390
} else {
8491
$sSize .= $oParserState->consume(1);
8592
}

src/Value/URL.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,21 @@ public function __construct(CSSString $oURL, $iLineNo = 0)
3636
*/
3737
public static function parse(ParserState $oParserState)
3838
{
39-
$bUseUrl = $oParserState->comes('url', true);
39+
$oAnchor = $oParserState->anchor();
40+
$sIdentifier = '';
41+
for ($i = 0; $i < 3; $i++) {
42+
$sChar = $oParserState->parseCharacter(true);
43+
if ($sChar === null) {
44+
break;
45+
}
46+
$sIdentifier .= $sChar;
47+
}
48+
$bUseUrl = $oParserState->streql($sIdentifier, 'url');
4049
if ($bUseUrl) {
41-
$oParserState->consume('url');
4250
$oParserState->consumeWhiteSpace();
4351
$oParserState->consume('(');
52+
} else {
53+
$oAnchor->backtrack();
4454
}
4555
$oParserState->consumeWhiteSpace();
4656
$oResult = new URL(CSSString::parse($oParserState), $oParserState->currentLine());

src/Value/Value.php

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public static function parseValue(ParserState $oParserState, array $aListDelimit
4444
while (
4545
!($oParserState->comes('}') || $oParserState->comes(';') || $oParserState->comes('!')
4646
|| $oParserState->comes(')')
47-
|| $oParserState->comes('\\'))
47+
|| $oParserState->comes('\\')
48+
|| $oParserState->isEnd())
4849
) {
4950
if (count($aStack) > 0) {
5051
$bFoundDelimiter = false;
@@ -105,16 +106,25 @@ public static function parseValue(ParserState $oParserState, array $aListDelimit
105106
*/
106107
public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false)
107108
{
108-
$sResult = $oParserState->parseIdentifier($bIgnoreCase);
109+
$oAnchor = $oParserState->anchor();
110+
$mResult = $oParserState->parseIdentifier($bIgnoreCase);
109111

110112
if ($oParserState->comes('(')) {
111-
$oParserState->consume('(');
112-
$aArguments = Value::parseValue($oParserState, ['=', ' ', ',']);
113-
$sResult = new CSSFunction($sResult, $aArguments, ',', $oParserState->currentLine());
114-
$oParserState->consume(')');
113+
$oAnchor->backtrack();
114+
if ($oParserState->streql('url', $mResult)) {
115+
$mResult = URL::parse($oParserState);
116+
} elseif (
117+
$oParserState->streql('calc', $mResult)
118+
|| $oParserState->streql('-webkit-calc', $mResult)
119+
|| $oParserState->streql('-moz-calc', $mResult)
120+
) {
121+
$mResult = CalcFunction::parse($oParserState);
122+
} else {
123+
$mResult = CSSFunction::parse($oParserState, $bIgnoreCase);
124+
}
115125
}
116126

117-
return $sResult;
127+
return $mResult;
118128
}
119129

120130
/**
@@ -137,13 +147,6 @@ public static function parsePrimitiveValue(ParserState $oParserState)
137147
$oValue = Size::parse($oParserState);
138148
} elseif ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) {
139149
$oValue = Color::parse($oParserState);
140-
} elseif ($oParserState->comes('url', true)) {
141-
$oValue = URL::parse($oParserState);
142-
} elseif (
143-
$oParserState->comes('calc', true) || $oParserState->comes('-webkit-calc', true)
144-
|| $oParserState->comes('-moz-calc', true)
145-
) {
146-
$oValue = CalcFunction::parse($oParserState);
147150
} elseif ($oParserState->comes("'") || $oParserState->comes('"')) {
148151
$oValue = CSSString::parse($oParserState);
149152
} elseif ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) {

0 commit comments

Comments
 (0)