Skip to content

Commit 376eb15

Browse files
MDL-65759 library: Update php-css-parser to 8.3.0
1 parent 5dae8c0 commit 376eb15

20 files changed

+1061
-740
lines changed

lib/php-css-parser/CSSList/AtRuleBlockList.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
3535
if($sArgs) {
3636
$sArgs = ' ' . $sArgs;
3737
}
38-
$sResult = "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
38+
$sResult = $oOutputFormat->sBeforeAtRuleBlock;
39+
$sResult .= "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
3940
$sResult .= parent::render($oOutputFormat);
4041
$sResult .= '}';
42+
$sResult .= $oOutputFormat->sAfterAtRuleBlock;
4143
return $sResult;
4244
}
4345

lib/php-css-parser/CSSList/CSSList.php

Lines changed: 188 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,22 @@
22

33
namespace Sabberworm\CSS\CSSList;
44

5+
use Sabberworm\CSS\Comment\Commentable;
6+
use Sabberworm\CSS\Parsing\ParserState;
7+
use Sabberworm\CSS\Parsing\SourceException;
8+
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
9+
use Sabberworm\CSS\Property\AtRule;
10+
use Sabberworm\CSS\Property\Charset;
11+
use Sabberworm\CSS\Property\CSSNamespace;
12+
use Sabberworm\CSS\Property\Import;
13+
use Sabberworm\CSS\Property\Selector;
514
use Sabberworm\CSS\Renderable;
15+
use Sabberworm\CSS\RuleSet\AtRuleSet;
616
use Sabberworm\CSS\RuleSet\DeclarationBlock;
717
use Sabberworm\CSS\RuleSet\RuleSet;
8-
use Sabberworm\CSS\Property\Selector;
9-
use Sabberworm\CSS\Comment\Commentable;
18+
use Sabberworm\CSS\Value\CSSString;
19+
use Sabberworm\CSS\Value\URL;
20+
use Sabberworm\CSS\Value\Value;
1021

1122
/**
1223
* A CSSList is the most generic container available. Its contents include RuleSet as well as other CSSList objects.
@@ -24,34 +35,187 @@ public function __construct($iLineNo = 0) {
2435
$this->iLineNo = $iLineNo;
2536
}
2637

38+
public static function parseList(ParserState $oParserState, CSSList $oList) {
39+
$bIsRoot = $oList instanceof Document;
40+
if(is_string($oParserState)) {
41+
$oParserState = new ParserState($oParserState);
42+
}
43+
$bLenientParsing = $oParserState->getSettings()->bLenientParsing;
44+
while(!$oParserState->isEnd()) {
45+
$comments = $oParserState->consumeWhiteSpace();
46+
$oListItem = null;
47+
if($bLenientParsing) {
48+
try {
49+
$oListItem = self::parseListItem($oParserState, $oList);
50+
} catch (UnexpectedTokenException $e) {
51+
$oListItem = false;
52+
}
53+
} else {
54+
$oListItem = self::parseListItem($oParserState, $oList);
55+
}
56+
if($oListItem === null) {
57+
// List parsing finished
58+
return;
59+
}
60+
if($oListItem) {
61+
$oListItem->setComments($comments);
62+
$oList->append($oListItem);
63+
}
64+
$oParserState->consumeWhiteSpace();
65+
}
66+
if(!$bIsRoot && !$bLenientParsing) {
67+
throw new SourceException("Unexpected end of document", $oParserState->currentLine());
68+
}
69+
}
70+
71+
private static function parseListItem(ParserState $oParserState, CSSList $oList) {
72+
$bIsRoot = $oList instanceof Document;
73+
if ($oParserState->comes('@')) {
74+
$oAtRule = self::parseAtRule($oParserState);
75+
if($oAtRule instanceof Charset) {
76+
if(!$bIsRoot) {
77+
throw new UnexpectedTokenException('@charset may only occur in root document', '', 'custom', $oParserState->currentLine());
78+
}
79+
if(count($oList->getContents()) > 0) {
80+
throw new UnexpectedTokenException('@charset must be the first parseable token in a document', '', 'custom', $oParserState->currentLine());
81+
}
82+
$oParserState->setCharset($oAtRule->getCharset()->getString());
83+
}
84+
return $oAtRule;
85+
} else if ($oParserState->comes('}')) {
86+
$oParserState->consume('}');
87+
if ($bIsRoot) {
88+
if ($oParserState->getSettings()->bLenientParsing) {
89+
while ($oParserState->comes('}')) $oParserState->consume('}');
90+
return DeclarationBlock::parse($oParserState);
91+
} else {
92+
throw new SourceException("Unopened {", $oParserState->currentLine());
93+
}
94+
} else {
95+
return null;
96+
}
97+
} else {
98+
return DeclarationBlock::parse($oParserState);
99+
}
100+
}
101+
102+
private static function parseAtRule(ParserState $oParserState) {
103+
$oParserState->consume('@');
104+
$sIdentifier = $oParserState->parseIdentifier();
105+
$iIdentifierLineNum = $oParserState->currentLine();
106+
$oParserState->consumeWhiteSpace();
107+
if ($sIdentifier === 'import') {
108+
$oLocation = URL::parse($oParserState);
109+
$oParserState->consumeWhiteSpace();
110+
$sMediaQuery = null;
111+
if (!$oParserState->comes(';')) {
112+
$sMediaQuery = $oParserState->consumeUntil(';');
113+
}
114+
$oParserState->consume(';');
115+
return new Import($oLocation, $sMediaQuery, $iIdentifierLineNum);
116+
} else if ($sIdentifier === 'charset') {
117+
$sCharset = CSSString::parse($oParserState);
118+
$oParserState->consumeWhiteSpace();
119+
$oParserState->consume(';');
120+
return new Charset($sCharset, $iIdentifierLineNum);
121+
} else if (self::identifierIs($sIdentifier, 'keyframes')) {
122+
$oResult = new KeyFrame($iIdentifierLineNum);
123+
$oResult->setVendorKeyFrame($sIdentifier);
124+
$oResult->setAnimationName(trim($oParserState->consumeUntil('{', false, true)));
125+
CSSList::parseList($oParserState, $oResult);
126+
return $oResult;
127+
} else if ($sIdentifier === 'namespace') {
128+
$sPrefix = null;
129+
$mUrl = Value::parsePrimitiveValue($oParserState);
130+
if (!$oParserState->comes(';')) {
131+
$sPrefix = $mUrl;
132+
$mUrl = Value::parsePrimitiveValue($oParserState);
133+
}
134+
$oParserState->consume(';');
135+
if ($sPrefix !== null && !is_string($sPrefix)) {
136+
throw new UnexpectedTokenException('Wrong namespace prefix', $sPrefix, 'custom', $iIdentifierLineNum);
137+
}
138+
if (!($mUrl instanceof CSSString || $mUrl instanceof URL)) {
139+
throw new UnexpectedTokenException('Wrong namespace url of invalid type', $mUrl, 'custom', $iIdentifierLineNum);
140+
}
141+
return new CSSNamespace($mUrl, $sPrefix, $iIdentifierLineNum);
142+
} else {
143+
//Unknown other at rule (font-face or such)
144+
$sArgs = trim($oParserState->consumeUntil('{', false, true));
145+
if (substr_count($sArgs, "(") != substr_count($sArgs, ")")) {
146+
if($oParserState->getSettings()->bLenientParsing) {
147+
return NULL;
148+
} else {
149+
throw new SourceException("Unmatched brace count in media query", $oParserState->currentLine());
150+
}
151+
}
152+
$bUseRuleSet = true;
153+
foreach(explode('/', AtRule::BLOCK_RULES) as $sBlockRuleName) {
154+
if(self::identifierIs($sIdentifier, $sBlockRuleName)) {
155+
$bUseRuleSet = false;
156+
break;
157+
}
158+
}
159+
if($bUseRuleSet) {
160+
$oAtRule = new AtRuleSet($sIdentifier, $sArgs, $iIdentifierLineNum);
161+
RuleSet::parseRuleSet($oParserState, $oAtRule);
162+
} else {
163+
$oAtRule = new AtRuleBlockList($sIdentifier, $sArgs, $iIdentifierLineNum);
164+
CSSList::parseList($oParserState, $oAtRule);
165+
}
166+
return $oAtRule;
167+
}
168+
}
169+
170+
/**
171+
* Tests an identifier for a given value. Since identifiers are all keywords, they can be vendor-prefixed. We need to check for these versions too.
172+
*/
173+
private static function identifierIs($sIdentifier, $sMatch) {
174+
return (strcasecmp($sIdentifier, $sMatch) === 0)
175+
?: preg_match("/^(-\\w+-)?$sMatch$/i", $sIdentifier) === 1;
176+
}
177+
178+
27179
/**
28180
* @return int
29181
*/
30182
public function getLineNo() {
31183
return $this->iLineNo;
32184
}
33185

186+
/**
187+
* Prepend item to list of contents.
188+
*
189+
* @param object $oItem Item.
190+
*/
191+
public function prepend($oItem) {
192+
array_unshift($this->aContents, $oItem);
193+
}
194+
195+
/**
196+
* Append item to list of contents.
197+
*
198+
* @param object $oItem Item.
199+
*/
34200
public function append($oItem) {
35201
$this->aContents[] = $oItem;
36202
}
37203

38204
/**
39-
* Insert an item before its sibling.
205+
* Splice the list of contents.
40206
*
41-
* @param mixed $oItem The item.
42-
* @param mixed $oSibling The sibling.
207+
* @param int $iOffset Offset.
208+
* @param int $iLength Length. Optional.
209+
* @param RuleSet[] $mReplacement Replacement. Optional.
43210
*/
44-
public function insert($oItem, $oSibling) {
45-
$iIndex = array_search($oSibling, $this->aContents);
46-
if ($iIndex === false) {
47-
return $this->append($oItem);
48-
}
49-
array_splice($this->aContents, $iIndex, 0, array($oItem));
211+
public function splice($iOffset, $iLength = null, $mReplacement = null) {
212+
array_splice($this->aContents, $iOffset, $iLength, $mReplacement);
50213
}
51214

52215
/**
53216
* Removes an item from the CSS list.
54217
* @param RuleSet|Import|Charset|CSSList $oItemToRemove May be a RuleSet (most likely a DeclarationBlock), a Import, a Charset or another CSSList (most likely a MediaQuery)
218+
* @return bool Whether the item was removed.
55219
*/
56220
public function remove($oItemToRemove) {
57221
$iKey = array_search($oItemToRemove, $this->aContents, true);
@@ -62,6 +226,19 @@ public function remove($oItemToRemove) {
62226
return false;
63227
}
64228

229+
/**
230+
* Replaces an item from the CSS list.
231+
* @param RuleSet|Import|Charset|CSSList $oItemToRemove May be a RuleSet (most likely a DeclarationBlock), a Import, a Charset or another CSSList (most likely a MediaQuery)
232+
*/
233+
public function replace($oOldItem, $oNewItem) {
234+
$iKey = array_search($oOldItem, $this->aContents, true);
235+
if ($iKey !== false) {
236+
array_splice($this->aContents, $iKey, 1, $oNewItem);
237+
return true;
238+
}
239+
return false;
240+
}
241+
65242
/**
66243
* Set the contents.
67244
* @param array $aContents Objects to set as content.

lib/php-css-parser/CSSList/Document.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Sabberworm\CSS\CSSList;
44

5+
use Sabberworm\CSS\Parsing\ParserState;
6+
57
/**
68
* The root CSSList of a parsed file. Contains all top-level css contents, mostly declaration blocks, but also any @-rules encountered.
79
*/
@@ -14,6 +16,12 @@ public function __construct($iLineNo = 0) {
1416
parent::__construct($iLineNo);
1517
}
1618

19+
public static function parse(ParserState $oParserState) {
20+
$oDocument = new Document($oParserState->currentLine());
21+
CSSList::parseList($oParserState, $oDocument);
22+
return $oDocument;
23+
}
24+
1725
/**
1826
* Gets all DeclarationBlock objects recursively.
1927
*/

lib/php-css-parser/OutputFormat.php

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
use Sabberworm\CSS\Parsing\OutputException;
66

7+
/**
8+
* Class OutputFormat
9+
*
10+
* @method OutputFormat setSemicolonAfterLastRule( bool $bSemicolonAfterLastRule ) Set whether semicolons are added after last rule.
11+
*/
712
class OutputFormat {
813
/**
914
* Value format
@@ -35,6 +40,10 @@ class OutputFormat {
3540
public $sSpaceAfterBlocks = '';
3641
public $sSpaceBetweenBlocks = "\n";
3742

43+
// Content injected in and around @-rule blocks.
44+
public $sBeforeAtRuleBlock = '';
45+
public $sAfterAtRuleBlock = '';
46+
3847
// This is what’s printed before and after the comma if a declaration block contains multiple selectors.
3948
public $sSpaceBeforeSelectorSeparator = '';
4049
public $sSpaceAfterSelectorSeparator = ' ';
@@ -43,7 +52,12 @@ class OutputFormat {
4352
public $sSpaceAfterListArgumentSeparator = '';
4453

4554
public $sSpaceBeforeOpeningBrace = ' ';
46-
55+
56+
// Content injected in and around declaration blocks.
57+
public $sBeforeDeclarationBlock = '';
58+
public $sAfterDeclarationBlockSelectors = '';
59+
public $sAfterDeclarationBlock = '';
60+
4761
/**
4862
* Indentation
4963
*/
@@ -141,17 +155,36 @@ public function getFormatter() {
141155
public function level() {
142156
return $this->iIndentationLevel;
143157
}
144-
158+
159+
/**
160+
* Create format.
161+
*
162+
* @return OutputFormat Format.
163+
*/
145164
public static function create() {
146165
return new OutputFormat();
147166
}
148-
167+
168+
/**
169+
* Create compact format.
170+
*
171+
* @return OutputFormat Format.
172+
*/
149173
public static function createCompact() {
150-
return self::create()->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator('');
174+
$format = self::create();
175+
$format->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator('');
176+
return $format;
151177
}
152-
178+
179+
/**
180+
* Create pretty format.
181+
*
182+
* @return OutputFormat Format.
183+
*/
153184
public static function createPretty() {
154-
return self::create()->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
185+
$format = self::create();
186+
$format->set('Space*Rules', "\n")->set('Space*Blocks', "\n")->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
187+
return $format;
155188
}
156189
}
157190

@@ -286,4 +319,4 @@ private function prepareSpace($sSpaceString) {
286319
private function indent() {
287320
return str_repeat($this->oFormat->sIndentation, $this->oFormat->level());
288321
}
289-
}
322+
}

0 commit comments

Comments
 (0)