Skip to content

Commit 185f98c

Browse files
committed
Merge branch 'master' of https://github.com/sabberworm/PHP-CSS-Parser into fix/malformed-identifier
* 'master' of https://github.com/sabberworm/PHP-CSS-Parser: (57 commits) Update PHP-CS-Fixer and PHPStan Update README.md Update PHP-CS-Fixer and PHPStan Update PHP-CS-Fixer and PHPStan Update PHP-CS-Fixer and PHPStan Run the tests on CI with all warnings enabled Show the Composer configuration in the CI jobs Add PHPDoc Fixes for CI Add missing code after catchup merge Fix unit tests Code refactor for CI Streamline parsing functions Re-implement ParserState anchors with dedicated Anchor class Add back code that got dropped while resolving conflicts Fix indents Use PHPUnit 5.x features in the tests Add unit tests for interface implementation in `CSSList` classes Add more detailed unit tests for `Comment` Sync the code documentation from the README to the source files ...
2 parents a49bd92 + cc791ad commit 185f98c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+895
-222
lines changed

.github/dependabot.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2+
3+
version: 2
4+
updates:
5+
- package-ecosystem: "github-actions"
6+
directory: "/"
7+
schedule:
8+
interval: "daily"
9+
10+
- package-ecosystem: "composer"
11+
directory: "/"
12+
schedule:
13+
interval: "daily"
14+
allow:
15+
- dependency-type: "development"
16+
versioning-strategy: "increase"

.github/workflows/ci.yml

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ name: CI
1111
jobs:
1212
php-lint:
1313
name: PHP Lint
14-
runs-on: ubuntu-20.04
14+
runs-on: ubuntu-22.04
1515
strategy:
1616
matrix:
1717
php-version: [ '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2' ]
1818

1919
steps:
2020
- name: Checkout
21-
uses: actions/checkout@v2
21+
uses: actions/checkout@v3
2222

2323
- name: Install PHP
2424
uses: shivammathur/setup-php@v2
@@ -32,7 +32,7 @@ jobs:
3232
unit-tests:
3333
name: Unit tests
3434

35-
runs-on: ubuntu-20.04
35+
runs-on: ubuntu-22.04
3636

3737
needs: [ php-lint ]
3838

@@ -42,22 +42,26 @@ jobs:
4242
php-version: [ '5.6', '7.0', '7.1', '7.2', '7.3' ]
4343
coverage: [ 'none' ]
4444
include:
45-
- php-version: 7.4
45+
- php-version: '7.4'
4646
coverage: xdebug
4747

4848
steps:
4949
- name: Checkout
50-
uses: actions/checkout@v2
50+
uses: actions/checkout@v3
5151

5252
- name: Install PHP
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
60-
uses: actions/cache@v1
64+
uses: actions/cache@v3
6165
with:
6266
path: ~/.cache/composer
6367
key: php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}
@@ -82,7 +86,7 @@ jobs:
8286
static-analysis:
8387
name: Static Analysis
8488

85-
runs-on: ubuntu-20.04
89+
runs-on: ubuntu-22.04
8690

8791
needs: [ php-lint ]
8892

@@ -91,15 +95,15 @@ jobs:
9195
matrix:
9296
include:
9397
- command: sniffer
94-
php-version: 7.4
98+
php-version: '7.4'
9599
- command: fixer
96-
php-version: 7.4
100+
php-version: '7.4'
97101
- command: stan
98-
php-version: 7.4
102+
php-version: '7.4'
99103

100104
steps:
101105
- name: Checkout
102-
uses: actions/checkout@v2
106+
uses: actions/checkout@v3
103107

104108
- name: Install PHP
105109
uses: shivammathur/setup-php@v2
@@ -108,8 +112,11 @@ 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
112-
uses: actions/cache@v1
119+
uses: actions/cache@v3
113120
with:
114121
path: ~/.cache/composer
115122
key: php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}

.phive/phars.xml

Lines changed: 4 additions & 4 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.0.0" installed="3.0.0" location="./.phive/php-cs-fixer.phar" copy="false"/>
4-
<phar name="phpcbf" version="^3.6.0" location="./.phive/phpcbf.phar" copy="false" installed="3.6.0"/>
5-
<phar name="phpcs" version="^3.6.0" location="./.phive/phpcs.phar" copy="false" installed="3.6.0"/>
6-
<phar name="phpstan" version="^0.12.88" installed="0.12.88" location="./.phive/phpstan.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"/>
4+
<phar name="phpcbf" version="^3.7.1" installed="3.7.1" location="./.phive/phpcbf.phar" copy="false"/>
5+
<phar name="phpcs" version="^3.7.1" installed="3.7.1" location="./.phive/phpcs.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: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ The resulting CSS document structure can be manipulated prior to being output.
3333

3434
#### Charset
3535

36-
The charset option is used only if no `@charset` declaration is found in the CSS file. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that.
36+
The charset option will only be used if the CSS file does not contain an `@charset` declaration. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that.
3737

3838
```php
3939
$settings = \Sabberworm\CSS\Settings::create()
@@ -43,7 +43,7 @@ $parser = new \Sabberworm\CSS\Parser($css, $settings);
4343

4444
#### Strict parsing
4545

46-
To have the parser choke on invalid rules, supply a thusly configured `\Sabberworm\CSS\Settings` object:
46+
To have the parser throw an exception when encountering invalid/unknown constructs (as opposed to trying to ignore them and carry on parsing), supply a thusly configured `\Sabberworm\CSS\Settings` object:
4747

4848
```php
4949
$parser = new \Sabberworm\CSS\Parser(
@@ -52,6 +52,8 @@ $parser = new \Sabberworm\CSS\Parser(
5252
);
5353
```
5454

55+
Note that this will also disable a workaround for parsing the unquoted variant of the legacy IE-specific `filter` rule.
56+
5557
#### Disable multibyte functions
5658

5759
To achieve faster parsing, you can choose to have PHP-CSS-Parser use regular string functions instead of `mb_*` functions. This should work fine in most cases, even for UTF-8 files, as all the multibyte characters are in string literals. Still it’s not recommended using this with input you have no control over as it’s not thoroughly covered by test cases.
@@ -67,29 +69,26 @@ The resulting data structure consists mainly of five basic types: `CSSList`, `Ru
6769

6870
#### CSSList
6971

70-
`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc. `CSSList` has the following concrete subtypes:
71-
72-
* `Document` – representing the root of a CSS file.
73-
* `MediaQuery` – represents a subsection of a `CSSList` that only applies to an output device matching the contained media query.
72+
`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc.
7473

75-
To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use instanceof to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`.
74+
To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use `instanceof` to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`.
7675

7776
To append a new item (selector, media query, etc.) to an existing `CSSList`, construct it using the constructor for this class and use the `append($oItem)` method.
7877

7978
#### RuleSet
8079

8180
`RuleSet` is a container for individual rules. The most common form of a rule set is one constrained by a selector. The following concrete subtypes exist:
8281

83-
* `AtRuleSet` – for generic at-rules which do not match the ones specifically mentioned like `@import`, `@charset` or `@media`. A common example for this is `@font-face`.
82+
* `AtRuleSet` – for generic at-rules for generic at-rules which are not covered by specific classes, i.e., not `@import`, `@charset` or `@media`. A common example for this is `@font-face`.
8483
* `DeclarationBlock` – a `RuleSet` constrained by a `Selector`; contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the matching elements.
8584

8685
Note: A `CSSList` can contain other `CSSList`s (and `Import`s as well as a `Charset`), while a `RuleSet` can only contain `Rule`s.
8786

88-
If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` instance or a rule name; optionally suffixed by a dash to remove all related rules).
87+
If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` or a rule name; optionally suffixed by a dash to remove all related rules).
8988

9089
#### Rule
9190

92-
`Rule`s just have a key (the rule) and a value. These values are all instances of a `Value`.
91+
`Rule`s just have a string key (the rule) and a `Value`.
9392

9493
#### Value
9594

@@ -98,19 +97,21 @@ If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `g
9897
* `Size` – consists of a numeric `size` value and a unit.
9998
* `Color` – colors can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form.
10099
* `CSSString` – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes.
101-
* `URL` – URLs in CSS; always output in URL("") notation.
100+
* `URL` – URLs in CSS; always output in `URL("")` notation.
101+
102+
There is another abstract subclass of `Value`, `ValueList`: A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`).
102103

103-
There is another abstract subclass of `Value`, `ValueList`. A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`). There are two types of `ValueList`s:
104+
There are two types of `ValueList`s:
104105

105-
* `RuleValueList` – The default type, used to represent all multi-valued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list).
106+
* `RuleValueList` – The default type, used to represent all multivalued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list).
106107
* `CSSFunction` – A special kind of value that also contains a function name and where the values are the function’s arguments. Also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`.
107108

108109
#### Convenience methods
109110

110-
There are a few convenience methods on Document to ease finding, manipulating and deleting rules:
111+
There are a few convenience methods on `Document` to ease finding, manipulating and deleting rules:
111112

112-
* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested your selectors are. Aliased as `getAllSelectors()`.
113-
* `getAllRuleSets()` – does what it says; no matter how deeply nested your rule sets are.
113+
* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested the selectors are. Aliased as `getAllSelectors()`.
114+
* `getAllRuleSets()` – does what it says; no matter how deeply nested the rule sets are.
114115
* `getAllValues()` – finds all `Value` objects inside `Rule`s.
115116

116117
## To-Do
@@ -156,7 +157,7 @@ $cssDocument = $parser->parse();
156157
foreach($cssDocument->getAllRuleSets() as $oRuleSet) {
157158
// Note that the added dash will make this remove all rules starting with
158159
// `font-` (like `font-size`, `font-weight`, etc.) as well as a potential
159-
// `font-rule`.
160+
// `font` rule.
160161
$oRuleSet->removeRule('font-');
161162
$oRuleSet->removeRule('cursor');
162163
}
@@ -214,7 +215,8 @@ html, body {
214215

215216
```
216217

217-
#### Structure (`var_dump()`)
218+
<details>
219+
<summary><b>Structure (<code>var_dump()</code>)</b></summary>
218220

219221
```php
220222
class Sabberworm\CSS\CSSList\Document#4 (2) {
@@ -435,6 +437,7 @@ class Sabberworm\CSS\CSSList\Document#4 (2) {
435437
}
436438

437439
```
440+
</details>
438441

439442
#### Output (`render()`)
440443

@@ -458,7 +461,8 @@ html, body {font-size: 1.6em;}
458461

459462
```
460463

461-
#### Structure (`var_dump()`)
464+
<details>
465+
<summary><b>Structure (<code>var_dump()</code>)</b></summary>
462466

463467
```php
464468
class Sabberworm\CSS\CSSList\Document#4 (2) {
@@ -603,6 +607,7 @@ class Sabberworm\CSS\CSSList\Document#4 (2) {
603607
}
604608

605609
```
610+
</details>
606611

607612
#### Output (`render()`)
608613

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
"ext-iconv": "*"
2020
},
2121
"require-dev": {
22-
"phpunit/phpunit": "^4.8.36",
23-
"codacy/coverage": "^1.4"
22+
"phpunit/phpunit": "^5.7.27",
23+
"codacy/coverage": "^1.4.3"
2424
},
2525
"suggest": {
2626
"ext-mbstring": "for parsing UTF-8 CSS"

config/php-cs-fixer.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@
1212
// Disable constant visibility from the PSR12 rule set as this would break compatibility with PHP < 7.1.
1313
'visibility_required' => ['elements' => ['property', 'method']],
1414

15+
'@PHPUnit50Migration:risky' => true,
16+
'@PHPUnit52Migration:risky' => true,
17+
'@PHPUnit54Migration:risky' => true,
18+
'@PHPUnit55Migration:risky' => true,
19+
'@PHPUnit56Migration:risky' => true,
20+
'@PHPUnit57Migration:risky' => true,
21+
1522
'php_unit_construct' => true,
16-
'php_unit_dedicate_assert' => ['target' => '5.0'],
23+
'php_unit_dedicate_assert' => ['target' => '5.6'],
1724
'php_unit_expectation' => ['target' => '5.6'],
1825
'php_unit_fqcn_annotation' => true,
1926
'php_unit_method_casing' => true,

src/CSSList/AtRuleBlockList.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@ public function __toString()
6161
*/
6262
public function render(OutputFormat $oOutputFormat)
6363
{
64+
$sResult = $oOutputFormat->comments($this);
65+
$sResult .= $oOutputFormat->sBeforeAtRuleBlock;
6466
$sArgs = $this->sArgs;
6567
if ($sArgs) {
6668
$sArgs = ' ' . $sArgs;
6769
}
68-
$sResult = $oOutputFormat->sBeforeAtRuleBlock;
6970
$sResult .= "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
70-
$sResult .= parent::render($oOutputFormat);
71+
$sResult .= $this->renderListContents($oOutputFormat);
7172
$sResult .= '}';
7273
$sResult .= $oOutputFormat->sAfterAtRuleBlock;
7374
return $sResult;

src/CSSList/CSSList.php

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
use Sabberworm\CSS\Value\Value;
2525

2626
/**
27-
* A `CSSList` is the most generic container available. Its contents include `RuleSet` as well as other `CSSList`
28-
* objects.
27+
* This is the most generic container available. It can contain `DeclarationBlock`s (rule sets with a selector),
28+
* `RuleSet`s as well as other `CSSList` objects.
2929
*
30-
* Also, it may contain `Import` and `Charset` objects stemming from at-rules.
30+
* It can also contain `Import` and `Charset` objects stemming from at-rules.
3131
*/
3232
abstract class CSSList implements Renderable, Commentable
3333
{
@@ -69,8 +69,9 @@ public static function parseList(ParserState $oParserState, CSSList $oList)
6969
$oParserState = new ParserState($oParserState, Settings::create());
7070
}
7171
$bLenientParsing = $oParserState->getSettings()->bLenientParsing;
72+
$aComments = [];
7273
while (!$oParserState->isEnd()) {
73-
$comments = $oParserState->consumeWhiteSpace();
74+
$aComments = array_merge($aComments, $oParserState->consumeWhiteSpace());
7475
$oListItem = null;
7576
if ($bLenientParsing) {
7677
try {
@@ -86,11 +87,12 @@ public static function parseList(ParserState $oParserState, CSSList $oList)
8687
return;
8788
}
8889
if ($oListItem) {
89-
$oListItem->setComments($comments);
90+
$oListItem->addComments($aComments);
9091
$oList->append($oListItem);
9192
}
92-
$oParserState->consumeWhiteSpace();
93+
$aComments = $oParserState->consumeWhiteSpace();
9394
}
95+
$oList->addComments($aComments);
9496
if (!$bIsRoot && !$bLenientParsing) {
9597
throw new SourceException("Unexpected end of document", $oParserState->currentLine());
9698
}
@@ -125,7 +127,7 @@ private static function parseListItem(ParserState $oParserState, CSSList $oList)
125127
$oParserState->currentLine()
126128
);
127129
}
128-
$oParserState->setCharset($oAtRule->getCharset()->getString());
130+
$oParserState->setCharset($oAtRule->getCharset());
129131
}
130132
return $oAtRule;
131133
} elseif ($oParserState->comes('}')) {
@@ -172,10 +174,10 @@ private static function parseAtRule(ParserState $oParserState)
172174
$oParserState->consumeUntil([';', ParserState::EOF], true, true);
173175
return new Import($oLocation, $sMediaQuery ?: null, $iIdentifierLineNum);
174176
} elseif ($sIdentifier === 'charset') {
175-
$sCharset = CSSString::parse($oParserState);
177+
$oCharsetString = CSSString::parse($oParserState);
176178
$oParserState->consumeWhiteSpace();
177179
$oParserState->consumeUntil([';', ParserState::EOF], true, true);
178-
return new Charset($sCharset, $iIdentifierLineNum);
180+
return new Charset($oCharsetString, $iIdentifierLineNum);
179181
} elseif (self::identifierIs($sIdentifier, 'keyframes')) {
180182
$oResult = new KeyFrame($iIdentifierLineNum);
181183
$oResult->setVendorKeyFrame($sIdentifier);
@@ -272,7 +274,7 @@ public function prepend($oItem)
272274
}
273275

274276
/**
275-
* Appends an item to tje list of contents.
277+
* Appends an item to the list of contents.
276278
*
277279
* @param RuleSet|CSSList|Import|Charset $oItem
278280
*
@@ -402,7 +404,7 @@ public function __toString()
402404
/**
403405
* @return string
404406
*/
405-
public function render(OutputFormat $oOutputFormat)
407+
protected function renderListContents(OutputFormat $oOutputFormat)
406408
{
407409
$sResult = '';
408410
$bIsFirst = true;
@@ -442,6 +444,8 @@ public function render(OutputFormat $oOutputFormat)
442444
abstract public function isRootList();
443445

444446
/**
447+
* Returns the stored items.
448+
*
445449
* @return array<int, RuleSet|Import|Charset|CSSList>
446450
*/
447451
public function getContents()

0 commit comments

Comments
 (0)