Stand: SMTP-Test, Admin-Mail-Tab, Notifiable-Fix, Lazy-Quill
- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er) - Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link - Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest - Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen) - Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
612
vendor/sabberworm/php-css-parser/CHANGELOG.md
vendored
Normal file
612
vendor/sabberworm/php-css-parser/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,612 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
Please also have a look at our
|
||||
[API and deprecation policy](docs/API-and-deprecation-policy.md).
|
||||
|
||||
## x.y.z
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Documentation
|
||||
|
||||
## 9.2.0: New features and deprecations
|
||||
|
||||
### Added
|
||||
|
||||
- Add `OutputFormat::setSpaceAroundSelectorCombinator()` (#1504)
|
||||
- Add support for escaped quotes in the selectors (#1485, #1489)
|
||||
- Provide line number in exception message for mismatched parentheses in
|
||||
selector (#1435)
|
||||
- Add support for CSS container queries (#1400)
|
||||
|
||||
### Changed
|
||||
|
||||
- `RuleSet\RuleContainer` is renamed to `RuleSet\DeclarationList` (#1530, #1539)
|
||||
- Methods like `setRule()` in `RuleSet` and `DeclarationBlock` have been renamed
|
||||
to `setDeclaration()`, etc. (#1521)
|
||||
- `Rule\Rule` class is renamed to `Property\Declaration`
|
||||
(#1508, #1512, #1513, #1522)
|
||||
- `Rule::setRule()` and `getRule()` are replaced with `setPropertyName()` and
|
||||
`getPropertyName()` (#1506)
|
||||
- `Selector` is now represented as a sequence of `Selector\Component` objects
|
||||
which can be accessed via `getComponents()`, manipulated individually, or set
|
||||
via `setComponents()` (#1478, #1486, #1487, #1488, #1494, #1496, #1536, #1537)
|
||||
- `Selector::setSelector()` and `Selector` constructor will now throw exception
|
||||
upon provision of an invalid selectior (#1498, #1502)
|
||||
- Clean up extra whitespace in CSS selector (#1398)
|
||||
- The array keys passed to `DeclarationBlock::setSelectors()` are no longer
|
||||
preserved (#1407)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `RuleSet\RuleContainer` is deprecated; use `RuleSet\DeclarationList` instead
|
||||
(#1530)
|
||||
- Methods like `setRule()` in `RuleSet` and `DeclarationBlock` are deprecated;
|
||||
there are direct replacements such as `setDeclaration()` (#1521)
|
||||
- `Rule\Rule` class is deprecated; `Property\Declaration` is a direct
|
||||
replacement (#1508)
|
||||
- `Rule::setRule()` and `getRule()` are deprecated and replaced with
|
||||
`setPropertyName()` and `getPropertyName()` (#1506, #1519)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Do not escape characters that do not need escaping in CSS string (#1444)
|
||||
- Reject selector comprising only whitespace (#1433)
|
||||
- Improve recovery parsing when a rogue `}` is encountered (#1425, #1426)
|
||||
- Parse comment(s) immediately preceding a selector (#1421, #1424)
|
||||
- Parse consecutive comments (#1421)
|
||||
- Support attribute selectors with values containing commas in
|
||||
`DeclarationBlock::setSelectors()` (#1419)
|
||||
- Allow `removeDeclarationBlockBySelector()` to be order-insensitve (#1406)
|
||||
- Fix parsing of `calc` expressions when a newline immediately precedes or
|
||||
follows a `+` or `-` operator (#1399)
|
||||
- Use typesafe versions of PHP functions (#1379, #1380, #1382, #1383, #1384)
|
||||
|
||||
## 9.1.0: Add support for PHP 8.5
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for PHP 8.5 (#1355)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improve performance of selector validation
|
||||
(avoiding silent PCRE catastrophic failure) (#1372)
|
||||
- Use typesafe versions of PHP functions (#1368, #1370)
|
||||
|
||||
## 9.0.0: New features, deprecation removals and bug fixes
|
||||
|
||||
### Added
|
||||
|
||||
- Interface `RuleContainer` for `RuleSet` `Rule` manipulation methods (#1256)
|
||||
- Partial support for CSS Color Module Level 4:
|
||||
- `rgb` and `rgba`, and `hsl` and `hsla` are now aliases (#797)
|
||||
- Parse color functions that use the "modern" syntax (#800)
|
||||
- Render RGB functions with "modern" syntax when required (#840)
|
||||
- Support `none` as color function component value (#859)
|
||||
- Add a class diagram to the README (#482)
|
||||
- Add more tests (#449)
|
||||
|
||||
### Changed
|
||||
|
||||
- `DeclarationBlock` no longer extends `RuleSet` and instead has a `RuleSet` as
|
||||
a property; use `getRuleSet()` to access it directly (#1194)
|
||||
- The default line (and column) number is now `null` (not zero) (#1288)
|
||||
- `setPosition()` (in `Rule` and other classes) now has fluent interface,
|
||||
returning itself (#1259)
|
||||
- `RuleSet::removeRule()` now only allows `Rule` as the parameter
|
||||
(implementing classes are `AtRuleSet` and `DeclarationBlock`);
|
||||
use `removeMatchingRules()` or `removeAllRules()` for other functions (#1255)
|
||||
- `RuleSet::getRules()` and `getRulesAssoc()` now only allow `string` or `null`
|
||||
as the parameter (implementing classes are `AtRuleSet` and `DeclarationBlock`)
|
||||
(#1253)
|
||||
- Initialize `KeyFrame` properties to sensible defaults (#1146)
|
||||
- Make `OutputFormat` `final` (#1128)
|
||||
- Make `Selector` a `Renderable` (#1017)
|
||||
- Only allow `string` for some `OutputFormat` properties (#885)
|
||||
- Use more native type declarations and strict mode
|
||||
(#641, #772, #774, #778, #804, #841, #873, #875, #891, #922, #923, #933, #958,
|
||||
#964, #967, #1000, #1044, #1134, #1136, #1137, #1139, #1140, #1141, #1145,
|
||||
#1162, #1163, #1166, #1172, #1174, #1178, #1179, #1181, #1183, #1184, #1186,
|
||||
#1187, #1190, #1192, #1193, #1203)
|
||||
- Add visibility to all class/interface constants (#469)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove `getLineNo()` from these classes (use `getLineNumber()` instead):
|
||||
`Comment`, `CSSList`, `SourceException`, `Charset`, `CSSNamespace`, `Import`,
|
||||
`Rule`, `DeclarationBlock`, `RuleSet`, `CSSFunction`, `Value` (#1258)
|
||||
- Remove `Rule::getColNo()` (use `getColumnNumber()` instead) (#1287)
|
||||
- Passing a string as the first argument to `getAllValues()` is no longer
|
||||
supported and will not work;
|
||||
the search pattern should now be passed as the second argument (#1243)
|
||||
- Passing a Boolean as the second argument to `getAllValues()` is no longer
|
||||
supported and will not work; the flag for searching in function arguments
|
||||
should now be passed as the third argument (#1243)
|
||||
- Remove `__toString()` (#1046)
|
||||
- Drop magic method forwarding in `OutputFormat` (#898)
|
||||
- Drop `atRuleArgs()` from the `AtRule` interface (#1141)
|
||||
- Remove `OutputFormat::get()` and `::set()` (#1108, #1110)
|
||||
- Drop special support for vendor prefixes (#1083)
|
||||
- Remove the IE hack in `Rule` (#995)
|
||||
- Drop `getLineNo()` from the `Renderable` interface (#1038)
|
||||
- Remove `OutputFormat::level()` (#874)
|
||||
- Remove expansion of shorthand properties (#838)
|
||||
- Remove `Parser::setCharset/getCharset` (#808)
|
||||
- Remove `Rule::getValues()` (#582)
|
||||
- Remove `Rule::setValues()` (#562)
|
||||
- Remove `Document::getAllSelectors()` (#561)
|
||||
- Remove `DeclarationBlock::getSelector()` (#559)
|
||||
- Remove `DeclarationBlock::setSelector()` (#560)
|
||||
- Drop support for PHP < 7.2 (#420)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove trailing semicolon from declaration blocks with 'compact'
|
||||
`OutputFormat` (#1345)
|
||||
- Parse selector functions (like `:not`) with comma-separated arguments (#1292)
|
||||
- Parse quoted attribute selector value containing comma (#1323)
|
||||
- Allow comma in selectors (e.g. `:not(html, body)`) (#1293)
|
||||
- Insert `Rule` before sibling even with different property name
|
||||
(in `RuleSet::addRule()`) (#1270)
|
||||
- Ensure `RuleSet::addRule()` sets non-negative column number when sibling
|
||||
provided (#1268)
|
||||
- Don't render `rgb` colors with percentage values using hex notation (#803)
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add an API and deprecation policy (#720)
|
||||
|
||||
@ziegenberg is a new contributor to this release and did a lot of the heavy
|
||||
lifting. Thanks! :heart:
|
||||
|
||||
## 8.9.0: New features, bug fixes and deprecations
|
||||
|
||||
### Added
|
||||
|
||||
- `RuleSet::removeMatchingRules()` method
|
||||
(for the implementing classes `AtRuleSet` and `DeclarationBlock`) (#1249)
|
||||
- `RuleSet::removeAllRules()` method
|
||||
(for the implementing classes `AtRuleSet` and `DeclarationBlock`) (#1249)
|
||||
- Add Interface `CSSElement` (#1231)
|
||||
- Methods `getLineNumber` and `getColumnNumber` which return a nullable `int`
|
||||
for the following classes:
|
||||
`Comment`, `CSSList`, `SourceException`, `Charset`, `CSSNamespace`, `Import`,
|
||||
`Rule`, `DeclarationBlock`, `RuleSet`, `CSSFunction`, `Value` (#1225, #1263)
|
||||
- `Positionable` interface for CSS items that may have a position
|
||||
(line and perhaps column number) in the parsed CSS (#1221)
|
||||
|
||||
### Changed
|
||||
|
||||
- Parameters for `getAllValues()` are deconflated, so it now takes three (all
|
||||
optional), allowing `$element` and `$ruleSearchPattern` to be specified
|
||||
separately (#1241)
|
||||
- Implement `Positionable` in the following CSS item classes:
|
||||
`Comment`, `CSSList`, `SourceException`, `Charset`, `CSSNamespace`, `Import`,
|
||||
`Rule`, `DeclarationBlock`, `RuleSet`, `CSSFunction`, `Value` (#1225)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Support for PHP < 7.2 is deprecated; version 9.0 will require PHP 7.2 or later
|
||||
(#1264)
|
||||
- Passing a `string` or `null` to `RuleSet::removeRule()` is deprecated
|
||||
(implementing classes are `AtRuleSet` and `DeclarationBlock`);
|
||||
use `removeMatchingRules()` or `removeAllRules()` instead (#1249)
|
||||
- Passing a `Rule` to `RuleSet::getRules()` or `getRulesAssoc()` is deprecated,
|
||||
affecting the implementing classes `AtRuleSet` and `DeclarationBlock`
|
||||
(call e.g. `getRules($rule->getRule())` instead) (#1248)
|
||||
- Passing a string as the first argument to `getAllValues()` is deprecated;
|
||||
the search pattern should now be passed as the second argument (#1241)
|
||||
- Passing a Boolean as the second argument to `getAllValues()` is deprecated;
|
||||
the flag for searching in function arguments should now be passed as the third
|
||||
argument (#1241)
|
||||
- `getLineNo()` is deprecated in these classes (use `getLineNumber()` instead):
|
||||
`Comment`, `CSSList`, `SourceException`, `Charset`, `CSSNamespace`, `Import`,
|
||||
`Rule`, `DeclarationBlock`, `RuleSet`, `CSSFunction`, `Value` (#1225, #1233)
|
||||
- `Rule::getColNo()` is deprecated (use `getColumnNumber()` instead)
|
||||
(#1225, #1233)
|
||||
- Providing zero as the line number argument to `Rule::setPosition()` is
|
||||
deprecated (pass `null` instead if there is no line number) (#1225, #1233)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Set line number when `RuleSet::addRule()` called with only column number set
|
||||
(#1265)
|
||||
- Ensure first rule added with `RuleSet::addRule()` has valid position (#1262)
|
||||
|
||||
## 8.8.0: Bug fixes and deprecations
|
||||
|
||||
### Added
|
||||
|
||||
- `OutputFormat` properties for space around specific list separators (#880)
|
||||
|
||||
### Changed
|
||||
|
||||
- Mark the `OutputFormat` constructor as `@internal` (#1131)
|
||||
- Mark `OutputFormatter` as `@internal` (#896)
|
||||
- Mark `Selector::isValid()` as `@internal` (#1037)
|
||||
- Mark parsing-related methods of most CSS elements as `@internal` (#908)
|
||||
- Mark `OutputFormat::nextLevel()` as `@internal` (#901)
|
||||
- Make all non-private properties `@internal` (#886)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecate extending `OutputFormat` (#1131)
|
||||
- Deprecate `OutputFormat::get()` and `::set()` (#1107)
|
||||
- Deprecate support for `-webkit-calc` and `-moz-calc` (#1086)
|
||||
- Deprecate magic method forwarding from `OutputFormat` to `OutputFormatter`
|
||||
(#894)
|
||||
- Deprecate `__toString()` (#1006)
|
||||
- Deprecate greedy calculation of selector specificity (#1018)
|
||||
- Deprecate the IE hack in `Rule` (#993, #1003)
|
||||
- `OutputFormat` properties for space around list separators as an array (#880)
|
||||
- Deprecate `OutputFormat::level()` (#870)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Include comments for all rules in declaration block (#1169)
|
||||
- Render rules in line and column number order (#1059)
|
||||
- Create `Size` with correct types in `expandBackgroundShorthand` (#814)
|
||||
- Parse `@font-face` `src` property as comma-delimited list (#794)
|
||||
|
||||
## 8.7.0: Add support for PHP 8.4
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for PHP 8.4 (#643, #657)
|
||||
|
||||
### Changed
|
||||
|
||||
- Mark parsing-internal classes and methods as `@internal` (#674)
|
||||
- Block installations on unsupported higher PHP versions (#691)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecate the expansion of shorthand properties
|
||||
(#578, #580, #579, #577, #576, #575, #574, #573, #572, #571, #570, #569, #566,
|
||||
#567, #558, #714)
|
||||
- Deprecate `Parser::setCharset()` and `Parser::getCharset()` (#688)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix type errors in PHP strict mode (#664)
|
||||
|
||||
## 8.6.0
|
||||
|
||||
### Added
|
||||
|
||||
- Support arithmetic operators in CSS function arguments (#607)
|
||||
- Add support for inserting an item in a CSS list (#545)
|
||||
- Add support for the `dvh`, `lvh` and `svh` length units (#415)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve performance of `Value::parseValue` with many delimiters by refactoring
|
||||
to remove `array_search()` (#413)
|
||||
|
||||
## 8.5.2
|
||||
|
||||
### Changed
|
||||
|
||||
- Mark all class constants as `@internal` (#472)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix undefined local variable in `CalcFunction::parse()` (#593)
|
||||
|
||||
## 8.5.1
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix PHP notice caused by parsing invalid color values having less than
|
||||
6 characters (#485)
|
||||
- Fix (regression) failure to parse at-rules with strict parsing (#456)
|
||||
|
||||
## 8.5.0
|
||||
|
||||
### Added
|
||||
|
||||
- Add a method to get an import's media queries (#384)
|
||||
- Add more unit tests (#381, #382)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Retain CSSList and Rule comments when rendering CSS (#351)
|
||||
- Replace invalid `turns` unit with `turn` (#350)
|
||||
- Also allow string values for rules (#348)
|
||||
- Fix invalid calc parsing (#169)
|
||||
- Handle scientific notation when parsing sizes (#179)
|
||||
- Fix PHP 8.1 compatibility in `ParserState::strsplit()` (#344)
|
||||
|
||||
## 8.4.0
|
||||
|
||||
### Features
|
||||
|
||||
* Support for PHP 8.x
|
||||
* PHPDoc annotations
|
||||
* Allow usage of CSS variables inside color functions (by parsing them as
|
||||
regular functions)
|
||||
* Use PSR-12 code style
|
||||
* *No deprecations*
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Improved handling of whitespace in `calc()`
|
||||
* Fix parsing units whose prefix is also a valid unit, like `vmin`
|
||||
* Allow passing an object to `CSSList#replace`
|
||||
* Fix PHP 7.3 warnings
|
||||
* Correctly parse keyframes with `%`
|
||||
* Don’t convert large numbers to scientific notation
|
||||
* Allow a file to end after an `@import`
|
||||
* Preserve case of CSS variables as specced
|
||||
* Allow identifiers to use escapes the same way as strings
|
||||
* No longer use `eval` for the comparison in `getSelectorsBySpecificity`, in
|
||||
case it gets passed untrusted input (CVE-2020-13756). Also fixed in 8.3.1,
|
||||
8.2.1, 8.1.1, 8.0.1, 7.0.4, 6.0.2, 5.2.1, 5.1.3, 5.0.9, 4.0.1, 3.0.1, 2.0.1,
|
||||
1.0.1.
|
||||
* Prevent an infinite loop when parsing invalid grid line names
|
||||
* Remove invalid unit `vm`
|
||||
* Retain rule order after expanding shorthands
|
||||
|
||||
### Backwards-incompatible changes
|
||||
|
||||
* PHP ≥ 5.6 is now required
|
||||
* HHVM compatibility target dropped
|
||||
|
||||
## 8.3.0 (2019-02-22)
|
||||
|
||||
* Refactor parsing logic to mostly reside in the class files whose data
|
||||
structure is to be parsed (this should eventually allow us to unit-test
|
||||
specific parts of the parsing logic individually).
|
||||
* Fix error in parsing `calc` expessions when the first operand is a negative
|
||||
number, thanks to @raxbg.
|
||||
* Support parsing CSS4 colors in hex notation with alpha values, thanks to
|
||||
@raxbg.
|
||||
* Swallow more errors in lenient mode, thanks to @raxbg.
|
||||
* Allow specifying arbitrary strings to output before and after declaration
|
||||
blocks, thanks to @westonruter.
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 8.2.0 (2018-07-13)
|
||||
|
||||
* Support parsing `calc()`, thanks to @raxbg.
|
||||
* Support parsing grid-lines, again thanks to @raxbg.
|
||||
* Support parsing legacy IE filters (`progid:`) in lenient mode, thanks to
|
||||
@FMCorz
|
||||
* Performance improvements parsing large files, again thanks to @FMCorz
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 8.1.0 (2016-07-19)
|
||||
|
||||
* Comments are no longer silently ignored but stored with the object with which
|
||||
they appear (no render support, though). Thanks to @FMCorz.
|
||||
* The IE hacks using `\0` and `\9` can now be parsed (and rendered) in lenient
|
||||
mode. Thanks (again) to @FMCorz.
|
||||
* Media queries with or without spaces before the query are parsed. Still no
|
||||
*real* parsing support, though. Sorry…
|
||||
* PHPUnit is now listed as a dev-dependency in composer.json.
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 8.0.0 (2016-06-30)
|
||||
|
||||
* Store source CSS line numbers in tokens and parsing exceptions.
|
||||
* *No deprecations*
|
||||
|
||||
### Backwards-incompatible changes
|
||||
|
||||
* Unrecoverable parser errors throw an exception of type
|
||||
`Sabberworm\CSS\Parsing\SourceException` instead of `\Exception`.
|
||||
|
||||
## 7.0.3 (2016-04-27)
|
||||
|
||||
* Fixed parsing empty CSS when multibyte is off
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 7.0.2 (2016-02-11)
|
||||
|
||||
* 150 time performance boost thanks
|
||||
to @[ossinkine](https://github.com/ossinkine)
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 7.0.1 (2015-12-25)
|
||||
|
||||
* No more suppressed `E_NOTICE`
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 7.0.0 (2015-08-24)
|
||||
|
||||
* Compatibility with PHP 7. Well timed, eh?
|
||||
* *No deprecations*
|
||||
|
||||
### Backwards-incompatible changes
|
||||
|
||||
* The `Sabberworm\CSS\Value\String` class has been renamed to
|
||||
`Sabberworm\CSS\Value\CSSString`.
|
||||
|
||||
## 6.0.1 (2015-08-24)
|
||||
|
||||
* Remove some declarations in interfaces incompatible with PHP 5.3 (< 5.3.9)
|
||||
* *No deprecations*
|
||||
|
||||
## 6.0.0 (2014-07-03)
|
||||
|
||||
* Format output using Sabberworm\CSS\OutputFormat
|
||||
* *No backwards-incompatible changes*
|
||||
|
||||
### Deprecations
|
||||
|
||||
* The parse() method replaces __toString with an optional argument (instance of
|
||||
the OutputFormat class)
|
||||
|
||||
## 5.2.0 (2014-06-30)
|
||||
|
||||
* Support removing a selector from a declaration block using
|
||||
`$oBlock->removeSelector($mSelector)`
|
||||
* Introduce a specialized exception (Sabberworm\CSS\Parsing\OuputException) for
|
||||
exceptions during output rendering
|
||||
|
||||
* *No deprecations*
|
||||
|
||||
#### Backwards-incompatible changes
|
||||
|
||||
* Outputting a declaration block that has no selectors throws an OuputException
|
||||
instead of outputting an invalid ` {…}` into the CSS document.
|
||||
|
||||
## 5.1.2 (2013-10-30)
|
||||
|
||||
* Remove the use of consumeUntil in comment parsing. This makes it possible to
|
||||
parse comments such as `/** Perfectly valid **/`
|
||||
* Add fr relative size unit
|
||||
* Fix some issues with HHVM
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.1.1 (2013-10-28)
|
||||
|
||||
* Updated CHANGELOG.md to reflect changes since 5.0.4
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.1.0 (2013-10-24)
|
||||
|
||||
* Performance enhancements by Michael M Slusarz
|
||||
* More rescue entry points for lenient parsing (unexpected tokens between
|
||||
declaration blocks and unclosed comments)
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.8 (2013-08-15)
|
||||
|
||||
* Make default settings’ multibyte parsing option dependent on whether or not
|
||||
the mbstring extension is actually installed.
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.7 (2013-08-04)
|
||||
|
||||
* Fix broken decimal point output optimization
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.6 (2013-05-31)
|
||||
|
||||
* Fix broken unit test
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.5 (2013-04-17)
|
||||
|
||||
* Initial support for lenient parsing (setting this parser option will catch
|
||||
some exceptions internally and recover the parser’s state as neatly as
|
||||
possible).
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.4 (2013-03-21)
|
||||
|
||||
* Don’t output floats with locale-aware separator chars
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.3 (2013-03-21)
|
||||
|
||||
* More size units recognized
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.2 (2013-03-21)
|
||||
|
||||
* CHANGELOG.md file added to distribution
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.1 (2013-03-20)
|
||||
|
||||
* Internal cleanup
|
||||
* *No backwards-incompatible changes*
|
||||
* *No deprecations*
|
||||
|
||||
## 5.0.0 (2013-03-20)
|
||||
|
||||
* Correctly parse all known CSS 3 units (including Hz and kHz).
|
||||
* Output RGB colors in short (#aaa or #ababab) notation
|
||||
* Be case-insensitive when parsing identifiers.
|
||||
* *No deprecations*
|
||||
|
||||
### Backwards-incompatible changes
|
||||
|
||||
* `Sabberworm\CSS\Value\Color`’s `__toString` method overrides `CSSList`’s to
|
||||
maybe return something other than `type(value, …)` (see above).
|
||||
|
||||
## 4.0.0 (2013-03-19)
|
||||
|
||||
* Support for more @-rules
|
||||
* Generic interface `Sabberworm\CSS\Property\AtRule`, implemented by all @-rule
|
||||
classes
|
||||
* *No deprecations*
|
||||
|
||||
### Backwards-incompatible changes
|
||||
|
||||
* `Sabberworm\CSS\RuleSet\AtRule` renamed to `Sabberworm\CSS\RuleSet\AtRuleSet`
|
||||
* `Sabberworm\CSS\CSSList\MediaQuery` renamed to
|
||||
`Sabberworm\CSS\RuleSet\CSSList\AtRuleBlockList` with differing semantics and
|
||||
API (which also works for other block-list-based @-rules like `@supports`).
|
||||
|
||||
## 3.0.0 (2013-03-06)
|
||||
|
||||
* Support for lenient parsing (on by default)
|
||||
* *No deprecations*
|
||||
|
||||
### Backwards-incompatible changes
|
||||
|
||||
* All properties (like whether or not to use `mb_`-functions, which default
|
||||
charset to use and – new – whether or not to be forgiving when parsing) are
|
||||
now encapsulated in an instance of `Sabberworm\CSS\Settings` which can be
|
||||
passed as the second argument to `Sabberworm\CSS\Parser->__construct()`.
|
||||
* Specifying a charset as the second argument to
|
||||
`Sabberworm\CSS\Parser->__construct()` is no longer supported. Use
|
||||
`Sabberworm\CSS\Settings::create()->withDefaultCharset('some-charset')`
|
||||
instead.
|
||||
* Setting `Sabberworm\CSS\Parser->bUseMbFunctions` has no effect. Use
|
||||
`Sabberworm\CSS\Settings::create()->withMultibyteSupport(true/false)` instead.
|
||||
* `Sabberworm\CSS\Parser->parse()` may throw a
|
||||
`Sabberworm\CSS\Parsing\UnexpectedTokenException` when in strict parsing mode.
|
||||
|
||||
## 2.0.0 (2013-01-29)
|
||||
|
||||
* Allow multiple rules of the same type per rule set
|
||||
|
||||
### Backwards-incompatible changes
|
||||
|
||||
* `Sabberworm\CSS\RuleSet->getRules()` returns an index-based array instead of
|
||||
an associative array. Use `Sabberworm\CSS\RuleSet->getRulesAssoc()` (which
|
||||
eliminates duplicate rules and lets the later rule of the same name win).
|
||||
* `Sabberworm\CSS\RuleSet->removeRule()` works as it did before except when
|
||||
passed an instance of `Sabberworm\CSS\Rule\Rule`, in which case it would only
|
||||
remove the exact rule given instead of all the rules of the same type. To get
|
||||
the old behaviour, use `Sabberworm\CSS\RuleSet->removeRule($oRule->getRule()`;
|
||||
|
||||
## 1.0
|
||||
|
||||
Initial release of a stable public API.
|
||||
|
||||
## 0.9
|
||||
|
||||
Last version not to use PSR-0 project organization semantics.
|
||||
21
vendor/sabberworm/php-css-parser/LICENSE
vendored
Normal file
21
vendor/sabberworm/php-css-parser/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2011 Raphael Schweikert, https://www.sabberworm.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
841
vendor/sabberworm/php-css-parser/README.md
vendored
Normal file
841
vendor/sabberworm/php-css-parser/README.md
vendored
Normal file
@@ -0,0 +1,841 @@
|
||||
# PHP CSS Parser
|
||||
|
||||
[](https://github.com/MyIntervals/PHP-CSS-Parser/actions/)
|
||||
[](https://coveralls.io/github/MyIntervals/PHP-CSS-Parser?branch=main)
|
||||
|
||||
A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output as (optimized) CSS.
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation using Composer
|
||||
|
||||
```bash
|
||||
composer require sabberworm/php-css-parser
|
||||
```
|
||||
|
||||
### Extraction
|
||||
|
||||
To use the CSS Parser, create a new instance. The constructor takes the following form:
|
||||
|
||||
```php
|
||||
new \Sabberworm\CSS\Parser($css);
|
||||
```
|
||||
|
||||
To read a file, for example, you’d do the following:
|
||||
|
||||
```php
|
||||
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
|
||||
$cssDocument = $parser->parse();
|
||||
```
|
||||
|
||||
The resulting CSS document structure can be manipulated prior to being output.
|
||||
|
||||
### Options
|
||||
|
||||
#### Charset
|
||||
|
||||
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.
|
||||
|
||||
```php
|
||||
$settings = \Sabberworm\CSS\Settings::create()
|
||||
->withDefaultCharset('windows-1252');
|
||||
$parser = new \Sabberworm\CSS\Parser($css, $settings);
|
||||
```
|
||||
|
||||
#### Strict parsing
|
||||
|
||||
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:
|
||||
|
||||
```php
|
||||
$parser = new \Sabberworm\CSS\Parser(
|
||||
file_get_contents('somefile.css'),
|
||||
\Sabberworm\CSS\Settings::create()->beStrict()
|
||||
);
|
||||
```
|
||||
|
||||
Note that this will also disable a workaround for parsing the unquoted variant of the legacy IE-specific `filter` rule.
|
||||
|
||||
#### Disable multibyte functions
|
||||
|
||||
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.
|
||||
|
||||
```php
|
||||
$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false);
|
||||
$parser = new \Sabberworm\CSS\Parser($css, $settings);
|
||||
```
|
||||
|
||||
### Manipulation
|
||||
|
||||
The resulting data structure consists mainly of five basic types: `CSSList`, `RuleSet`, `Rule`, `Selector` and `Value`. There are two additional types used: `Import` and `Charset`, which you won’t use often.
|
||||
|
||||
#### CSSList
|
||||
|
||||
`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.
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
#### RuleSet
|
||||
|
||||
`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:
|
||||
|
||||
* `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`.
|
||||
* `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.
|
||||
|
||||
Note: A `CSSList` can contain other `CSSList`s (and `Import`s as well as a `Charset`), while a `RuleSet` can only contain `Rule`s.
|
||||
|
||||
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).
|
||||
|
||||
#### Rule
|
||||
|
||||
`Rule`s just have a string key (the rule) and a `Value`.
|
||||
|
||||
#### Value
|
||||
|
||||
`Value` is an abstract class that only defines the `render` method. The concrete subclasses for atomic value types are:
|
||||
|
||||
* `Size` – consists of a numeric `size` value and a unit.
|
||||
* `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.
|
||||
* `CSSString` – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes.
|
||||
* `URL` – URLs in CSS; always output in `URL("")` notation.
|
||||
|
||||
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:
|
||||
|
||||
* `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).
|
||||
* `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);`.
|
||||
|
||||
#### Convenience methods
|
||||
|
||||
There are a few convenience methods on `Document` to ease finding, manipulating and deleting rules:
|
||||
|
||||
* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested the selectors are. Aliased as `getAllSelectors()`.
|
||||
* `getAllRuleSets()` – does what it says; no matter how deeply nested the rule sets are.
|
||||
* `getAllValues()` – finds all `Value` objects inside `Rule`s.
|
||||
|
||||
## To-Do
|
||||
|
||||
* More convenience methods (like `selectorsWithElement($sId/Class/TagName)`, `attributesOfType($type)`, `removeAttributesOfType($type)`)
|
||||
* Real multibyte support. Currently, only multibyte charsets whose first 255 code points take up only one byte and are identical with ASCII are supported (yes, UTF-8 fits this description).
|
||||
* Named color support (using `Color` instead of an anonymous string literal)
|
||||
|
||||
## Use cases
|
||||
|
||||
### Use `Parser` to prepend an ID to all selectors
|
||||
|
||||
```php
|
||||
$myId = "#my_id";
|
||||
$parser = new \Sabberworm\CSS\Parser($css);
|
||||
$cssDocument = $parser->parse();
|
||||
foreach ($cssDocument->getAllDeclarationBlocks() as $block) {
|
||||
foreach ($block->getSelectors() as $selector) {
|
||||
// Loop over all selector parts (the comma-separated strings in a
|
||||
// selector) and prepend the ID.
|
||||
$selector->setSelector($myId.' '.$selector->getSelector());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Shrink all absolute sizes to half
|
||||
|
||||
```php
|
||||
$parser = new \Sabberworm\CSS\Parser($css);
|
||||
$cssDocument = $parser->parse();
|
||||
foreach ($cssDocument->getAllValues() as $value) {
|
||||
if ($value instanceof CSSSize && !$value->isRelative()) {
|
||||
$value->setSize($value->getSize() / 2);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Remove unwanted rules
|
||||
|
||||
```php
|
||||
$parser = new \Sabberworm\CSS\Parser($css);
|
||||
$cssDocument = $parser->parse();
|
||||
foreach($cssDocument->getAllRuleSets() as $oRuleSet) {
|
||||
// Note that the added dash will make this remove all rules starting with
|
||||
// `font-` (like `font-size`, `font-weight`, etc.) as well as a potential
|
||||
// `font` rule.
|
||||
$oRuleSet->removeRule('font-');
|
||||
$oRuleSet->removeRule('cursor');
|
||||
}
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
To output the entire CSS document into a variable, just use `->render()`:
|
||||
|
||||
```php
|
||||
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css'));
|
||||
$cssDocument = $parser->parse();
|
||||
print $cssDocument->render();
|
||||
```
|
||||
|
||||
If you want to format the output, pass an instance of type `\Sabberworm\CSS\OutputFormat`:
|
||||
|
||||
```php
|
||||
$format = \Sabberworm\CSS\OutputFormat::create()
|
||||
->indentWithSpaces(4)->setSpaceBetweenRules("\n");
|
||||
print $cssDocument->render($format);
|
||||
```
|
||||
|
||||
Or use one of the predefined formats:
|
||||
|
||||
```php
|
||||
print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty());
|
||||
print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact());
|
||||
```
|
||||
|
||||
To see what you can do with output formatting, look at the tests in `tests/OutputFormatTest.php`.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 (At-Rules)
|
||||
|
||||
#### Input
|
||||
|
||||
```css
|
||||
@charset "utf-8";
|
||||
|
||||
@font-face {
|
||||
font-family: "CrassRoots";
|
||||
src: url("../media/cr.ttf");
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
@keyframes mymove {
|
||||
from { top: 0px; }
|
||||
to { top: 200px; }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Structure (<code>var_dump()</code>)</b></summary>
|
||||
|
||||
```php
|
||||
class Sabberworm\CSS\CSSList\Document#4 (2) {
|
||||
protected $contents =>
|
||||
array(4) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Property\Charset#6 (2) {
|
||||
private $charset =>
|
||||
class Sabberworm\CSS\Value\CSSString#5 (2) {
|
||||
private $string =>
|
||||
string(5) "utf-8"
|
||||
protected $lineNumber =>
|
||||
int(1)
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(1)
|
||||
}
|
||||
[1] =>
|
||||
class Sabberworm\CSS\RuleSet\AtRuleSet#7 (4) {
|
||||
private $type =>
|
||||
string(9) "font-face"
|
||||
private $arguments =>
|
||||
string(0) ""
|
||||
private $rules =>
|
||||
array(2) {
|
||||
'font-family' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#8 (4) {
|
||||
private $rule =>
|
||||
string(11) "font-family"
|
||||
private $value =>
|
||||
class Sabberworm\CSS\Value\CSSString#9 (2) {
|
||||
private $string =>
|
||||
string(10) "CrassRoots"
|
||||
protected $lineNumber =>
|
||||
int(4)
|
||||
}
|
||||
private $isImportant =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(4)
|
||||
}
|
||||
}
|
||||
'src' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#10 (4) {
|
||||
private $rule =>
|
||||
string(3) "src"
|
||||
private $value =>
|
||||
class Sabberworm\CSS\Value\URL#11 (2) {
|
||||
private $url =>
|
||||
class Sabberworm\CSS\Value\CSSString#12 (2) {
|
||||
private $string =>
|
||||
string(15) "../media/cr.ttf"
|
||||
protected $lineNumber =>
|
||||
int(5)
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(5)
|
||||
}
|
||||
private $isImportant =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(5)
|
||||
}
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(3)
|
||||
}
|
||||
[2] =>
|
||||
class Sabberworm\CSS\RuleSet\DeclarationBlock#13 (3) {
|
||||
private $selectors =>
|
||||
array(2) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Property\Selector#14 (2) {
|
||||
private $selector =>
|
||||
string(4) "html"
|
||||
private $specificity =>
|
||||
NULL
|
||||
}
|
||||
[1] =>
|
||||
class Sabberworm\CSS\Property\Selector#15 (2) {
|
||||
private $selector =>
|
||||
string(4) "body"
|
||||
private $specificity =>
|
||||
NULL
|
||||
}
|
||||
}
|
||||
private $rules =>
|
||||
array(1) {
|
||||
'font-size' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#16 (4) {
|
||||
private $rule =>
|
||||
string(9) "font-size"
|
||||
private $value =>
|
||||
class Sabberworm\CSS\Value\Size#17 (4) {
|
||||
private $size =>
|
||||
double(1.6)
|
||||
private $unit =>
|
||||
string(2) "em"
|
||||
private $isColorComponent =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(9)
|
||||
}
|
||||
private $isImportant =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(9)
|
||||
}
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(8)
|
||||
}
|
||||
[3] =>
|
||||
class Sabberworm\CSS\CSSList\KeyFrame#18 (4) {
|
||||
private $vendorKeyFrame =>
|
||||
string(9) "keyframes"
|
||||
private $animationName =>
|
||||
string(6) "mymove"
|
||||
protected $contents =>
|
||||
array(2) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\RuleSet\DeclarationBlock#19 (3) {
|
||||
private $selectors =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Property\Selector#20 (2) {
|
||||
private $selector =>
|
||||
string(4) "from"
|
||||
private $specificity =>
|
||||
NULL
|
||||
}
|
||||
}
|
||||
private $rules =>
|
||||
array(1) {
|
||||
'top' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#21 (4) {
|
||||
private $rule =>
|
||||
string(3) "top"
|
||||
private $value =>
|
||||
class Sabberworm\CSS\Value\Size#22 (4) {
|
||||
private $size =>
|
||||
double(0)
|
||||
private $unit =>
|
||||
string(2) "px"
|
||||
private $isColorComponent =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(13)
|
||||
}
|
||||
private $isImportant =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(13)
|
||||
}
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(13)
|
||||
}
|
||||
[1] =>
|
||||
class Sabberworm\CSS\RuleSet\DeclarationBlock#23 (3) {
|
||||
private $selectors =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Property\Selector#24 (2) {
|
||||
private $selector =>
|
||||
string(2) "to"
|
||||
private $specificity =>
|
||||
NULL
|
||||
}
|
||||
}
|
||||
private $rules =>
|
||||
array(1) {
|
||||
'top' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#25 (4) {
|
||||
private $rule =>
|
||||
string(3) "top"
|
||||
private $value =>
|
||||
class Sabberworm\CSS\Value\Size#26 (4) {
|
||||
private $size =>
|
||||
double(200)
|
||||
private $unit =>
|
||||
string(2) "px"
|
||||
private $isColorComponent =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(14)
|
||||
}
|
||||
private $isImportant =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(14)
|
||||
}
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(14)
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(12)
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(1)
|
||||
}
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
#### Output (`render()`)
|
||||
|
||||
```css
|
||||
@charset "utf-8";
|
||||
@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");}
|
||||
html, body {font-size: 1.6em;}
|
||||
@keyframes mymove {from {top: 0px;} to {top: 200px;}}
|
||||
```
|
||||
|
||||
### Example 2 (Values)
|
||||
|
||||
#### Input
|
||||
|
||||
```css
|
||||
#header {
|
||||
margin: 10px 2em 1cm 2%;
|
||||
font-family: Verdana, Helvetica, "Gill Sans", sans-serif;
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><b>Structure (<code>var_dump()</code>)</b></summary>
|
||||
|
||||
```php
|
||||
class Sabberworm\CSS\CSSList\Document#4 (2) {
|
||||
protected $contents =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\RuleSet\DeclarationBlock#5 (3) {
|
||||
private $selectors =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Property\Selector#6 (2) {
|
||||
private $selector =>
|
||||
string(7) "#header"
|
||||
private $specificity =>
|
||||
NULL
|
||||
}
|
||||
}
|
||||
private $rules =>
|
||||
array(3) {
|
||||
'margin' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#7 (4) {
|
||||
private $rule =>
|
||||
string(6) "margin"
|
||||
private $value =>
|
||||
class Sabberworm\CSS\Value\RuleValueList#12 (3) {
|
||||
protected $components =>
|
||||
array(4) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Value\Size#8 (4) {
|
||||
private $size =>
|
||||
double(10)
|
||||
private $unit =>
|
||||
string(2) "px"
|
||||
private $isColorComponent =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(2)
|
||||
}
|
||||
[1] =>
|
||||
class Sabberworm\CSS\Value\Size#9 (4) {
|
||||
private $size =>
|
||||
double(2)
|
||||
private $unit =>
|
||||
string(2) "em"
|
||||
private $isColorComponent =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(2)
|
||||
}
|
||||
[2] =>
|
||||
class Sabberworm\CSS\Value\Size#10 (4) {
|
||||
private $size =>
|
||||
double(1)
|
||||
private $unit =>
|
||||
string(2) "cm"
|
||||
private $isColorComponent =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(2)
|
||||
}
|
||||
[3] =>
|
||||
class Sabberworm\CSS\Value\Size#11 (4) {
|
||||
private $size =>
|
||||
double(2)
|
||||
private $unit =>
|
||||
string(1) "%"
|
||||
private $isColorComponent =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(2)
|
||||
}
|
||||
}
|
||||
protected $separator =>
|
||||
string(1) " "
|
||||
protected $lineNumber =>
|
||||
int(2)
|
||||
}
|
||||
private $isImportant =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(2)
|
||||
}
|
||||
}
|
||||
'font-family' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#13 (4) {
|
||||
private $rule =>
|
||||
string(11) "font-family"
|
||||
private $value =>
|
||||
class Sabberworm\CSS\Value\RuleValueList#15 (3) {
|
||||
protected $components =>
|
||||
array(4) {
|
||||
[0] =>
|
||||
string(7) "Verdana"
|
||||
[1] =>
|
||||
string(9) "Helvetica"
|
||||
[2] =>
|
||||
class Sabberworm\CSS\Value\CSSString#14 (2) {
|
||||
private $string =>
|
||||
string(9) "Gill Sans"
|
||||
protected $lineNumber =>
|
||||
int(3)
|
||||
}
|
||||
[3] =>
|
||||
string(10) "sans-serif"
|
||||
}
|
||||
protected $sSeparator =>
|
||||
string(1) ","
|
||||
protected $lineNumber =>
|
||||
int(3)
|
||||
}
|
||||
private $isImportant =>
|
||||
bool(false)
|
||||
protected $lineNumber =>
|
||||
int(3)
|
||||
}
|
||||
}
|
||||
'color' =>
|
||||
array(1) {
|
||||
[0] =>
|
||||
class Sabberworm\CSS\Rule\Rule#16 (4) {
|
||||
private $rule =>
|
||||
string(5) "color"
|
||||
private $value =>
|
||||
string(3) "red"
|
||||
private $isImportant =>
|
||||
bool(true)
|
||||
protected $lineNumber =>
|
||||
int(4)
|
||||
}
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(1)
|
||||
}
|
||||
}
|
||||
protected $lineNumber =>
|
||||
int(1)
|
||||
}
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
#### Output (`render()`)
|
||||
|
||||
```css
|
||||
#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;}
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
direction LR
|
||||
|
||||
class Anchor {
|
||||
}
|
||||
class AtRule {
|
||||
<<interface>>
|
||||
}
|
||||
class AtRuleBlockList {
|
||||
}
|
||||
class AtRuleSet {
|
||||
}
|
||||
class CSSBlockList {
|
||||
<<abstract>>
|
||||
}
|
||||
class CSSElement {
|
||||
<<interface>>
|
||||
}
|
||||
class CSSFunction {
|
||||
}
|
||||
class CSSList {
|
||||
<<abstract>>
|
||||
}
|
||||
class CSSListItem {
|
||||
<<interface>>
|
||||
}
|
||||
class CSSNamespace {
|
||||
}
|
||||
class CSSString {
|
||||
}
|
||||
class CalcFunction {
|
||||
}
|
||||
class CalcRuleValueList {
|
||||
}
|
||||
class Charset {
|
||||
}
|
||||
class Color {
|
||||
}
|
||||
class Comment {
|
||||
}
|
||||
class Commentable {
|
||||
<<interface>>
|
||||
}
|
||||
class DeclarationBlock {
|
||||
}
|
||||
class Document {
|
||||
}
|
||||
class Import {
|
||||
}
|
||||
class KeyFrame {
|
||||
}
|
||||
class KeyframeSelector {
|
||||
}
|
||||
class LineName {
|
||||
}
|
||||
class OutputException {
|
||||
}
|
||||
class OutputFormat {
|
||||
}
|
||||
class OutputFormatter {
|
||||
}
|
||||
class Parser {
|
||||
}
|
||||
class ParserState {
|
||||
}
|
||||
class Positionable {
|
||||
<<interface>>
|
||||
}
|
||||
class PrimitiveValue {
|
||||
<<abstract>>
|
||||
}
|
||||
class Renderable {
|
||||
<<interface>>
|
||||
}
|
||||
class Rule {
|
||||
}
|
||||
class RuleContainer {
|
||||
<<interface>>
|
||||
}
|
||||
class RuleSet {
|
||||
}
|
||||
class RuleValueList {
|
||||
}
|
||||
class Selector {
|
||||
}
|
||||
class Settings {
|
||||
}
|
||||
class Size {
|
||||
}
|
||||
class SourceException {
|
||||
}
|
||||
class SpecificityCalculator {
|
||||
}
|
||||
class URL {
|
||||
}
|
||||
class UnexpectedEOFException {
|
||||
}
|
||||
class UnexpectedTokenException {
|
||||
}
|
||||
class Value {
|
||||
<<abstract>>
|
||||
}
|
||||
class ValueList {
|
||||
<<abstract>>
|
||||
}
|
||||
|
||||
Anchor ..> ParserState: dependency
|
||||
CSSListItem <|-- AtRule: inheritance
|
||||
AtRule <|.. AtRuleBlockList: realization
|
||||
CSSBlockList <|-- AtRuleBlockList: inheritance
|
||||
AtRule <|.. AtRuleSet: realization
|
||||
RuleSet <|-- AtRuleSet: inheritance
|
||||
CSSList <|-- CSSBlockList: inheritance
|
||||
Renderable <|-- CSSElement: inheritance
|
||||
ValueList <|-- CSSFunction: inheritance
|
||||
CSSElement <|.. CSSList: realization
|
||||
CSSListItem <|.. CSSList: realization
|
||||
CSSList ..> Charset: dependency
|
||||
CSSList ..> Import: dependency
|
||||
Positionable <|.. CSSList: realization
|
||||
Commentable <|-- CSSListItem: inheritance
|
||||
Renderable <|-- CSSListItem: inheritance
|
||||
AtRule <|.. CSSNamespace: realization
|
||||
Positionable <|.. CSSNamespace: realization
|
||||
PrimitiveValue <|-- CSSString: inheritance
|
||||
CSSFunction <|-- CalcFunction: inheritance
|
||||
RuleValueList <|-- CalcRuleValueList: inheritance
|
||||
AtRule <|.. Charset: realization
|
||||
Charset ..> CSSString: dependency
|
||||
Positionable <|.. Charset: realization
|
||||
CSSFunction <|-- Color: inheritance
|
||||
Positionable <|.. Comment: realization
|
||||
Renderable <|.. Comment: realization
|
||||
CSSElement <|.. DeclarationBlock: realization
|
||||
CSSListItem <|.. DeclarationBlock: realization
|
||||
Positionable <|.. DeclarationBlock: realization
|
||||
RuleContainer <|.. DeclarationBlock: realization
|
||||
DeclarationBlock ..> RuleSet : dependency
|
||||
DeclarationBlock ..> Selector: dependency
|
||||
CSSBlockList <|-- Document: inheritance
|
||||
AtRule <|.. Import: realization
|
||||
Positionable <|.. Import: realization
|
||||
AtRule <|.. KeyFrame: realization
|
||||
CSSList <|-- KeyFrame: inheritance
|
||||
Selector <|-- KeyframeSelector: inheritance
|
||||
ValueList <|-- LineName: inheritance
|
||||
SourceException <|-- OutputException: inheritance
|
||||
OutputFormat ..> OutputFormatter: dependency
|
||||
OutputFormatter ..> OutputFormat: dependency
|
||||
Parser ..> ParserState: dependency
|
||||
ParserState ..> Settings: dependency
|
||||
Value <|-- PrimitiveValue: inheritance
|
||||
CSSElement <|.. Rule: realization
|
||||
Commentable <|.. Rule: realization
|
||||
Positionable <|.. Rule: realization
|
||||
Rule ..> RuleValueList: dependency
|
||||
CSSElement <|.. RuleSet: realization
|
||||
CSSListItem <|.. RuleSet: realization
|
||||
Positionable <|.. RuleSet: realization
|
||||
RuleSet ..> Rule: dependency
|
||||
RuleContainer <|.. RuleSet: realization
|
||||
ValueList <|-- RuleValueList: inheritance
|
||||
Renderable <|.. Selector: realization
|
||||
PrimitiveValue <|-- Size: inheritance
|
||||
Exception <|-- SourceException: inheritance
|
||||
Positionable <|.. SourceException: realization
|
||||
URL ..> CSSString: dependency
|
||||
PrimitiveValue <|-- URL: inheritance
|
||||
UnexpectedTokenException <|-- UnexpectedEOFException: inheritance
|
||||
SourceException <|-- UnexpectedTokenException: inheritance
|
||||
CSSElement <|.. Value: realization
|
||||
Positionable <|.. Value: realization
|
||||
Value <|-- ValueList: inheritance
|
||||
|
||||
CSSList ..> CSSList: dependency
|
||||
CSSList ..> Comment: dependency
|
||||
CSSList ..> RuleSet: dependency
|
||||
CSSNamespace ..> Comment: dependency
|
||||
Charset ..> Comment: dependency
|
||||
Import ..> Comment: dependency
|
||||
OutputFormat ..> OutputFormat: dependency
|
||||
Rule ..> Comment: dependency
|
||||
RuleSet ..> Comment: dependency
|
||||
ValueList ..> Value: dependency
|
||||
```
|
||||
|
||||
## API and deprecation policy
|
||||
|
||||
Please have a look at our
|
||||
[API and deprecation policy](docs/API-and-deprecation-policy.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions in the form of bug reports, feature requests, or pull requests are
|
||||
more than welcome. :pray: Please have a look at our
|
||||
[contribution guidelines](CONTRIBUTING.md) to learn more about how to
|
||||
contribute to PHP-CSS-Parser.
|
||||
|
||||
## Contributors/Thanks to
|
||||
|
||||
* [oliverklee](https://github.com/oliverklee) for lots of refactorings, code modernizations and CI integrations
|
||||
* [raxbg](https://github.com/raxbg) for contributions to parse `calc`, grid lines, and various bugfixes.
|
||||
* [westonruter](https://github.com/westonruter) for bugfixes and improvements.
|
||||
* [FMCorz](https://github.com/FMCorz) for many patches and suggestions, for being able to parse comments and IE hacks (in lenient mode).
|
||||
* [Lullabot](https://github.com/Lullabot) for a patch that allows to know the line number for each parsed token.
|
||||
* [ju1ius](https://github.com/ju1ius) for the specificity parsing code and the ability to expand/compact shorthand properties.
|
||||
* [ossinkine](https://github.com/ossinkine) for a 150 time performance boost.
|
||||
* [GaryJones](https://github.com/GaryJones) for lots of input and [https://css-specificity.info/](https://css-specificity.info/).
|
||||
* [docteurklein](https://github.com/docteurklein) for output formatting and `CSSList->remove()` inspiration.
|
||||
* [nicolopignatelli](https://github.com/nicolopignatelli) for PSR-0 compatibility.
|
||||
* [diegoembarcadero](https://github.com/diegoembarcadero) for keyframe at-rule parsing.
|
||||
* [goetas](https://github.com/goetas) for `@namespace` at-rule support.
|
||||
* [ziegenberg](https://github.com/ziegenberg) for general housekeeping and cleanup.
|
||||
* [View full list](https://github.com/sabberworm/PHP-CSS-Parser/contributors)
|
||||
|
||||
## Misc
|
||||
|
||||
### Legacy Support
|
||||
|
||||
The latest pre-PSR-0 version of this project can be checked with the `0.9.0` tag.
|
||||
141
vendor/sabberworm/php-css-parser/composer.json
vendored
Normal file
141
vendor/sabberworm/php-css-parser/composer.json
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"name": "sabberworm/php-css-parser",
|
||||
"description": "Parser for CSS Files written in PHP",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"parser",
|
||||
"css",
|
||||
"stylesheet"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Raphael Schweikert"
|
||||
},
|
||||
{
|
||||
"name": "Oliver Klee",
|
||||
"email": "github@oliverklee.de"
|
||||
},
|
||||
{
|
||||
"name": "Jake Hotson",
|
||||
"email": "jake.github@qzdesign.co.uk"
|
||||
}
|
||||
],
|
||||
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
||||
"require": {
|
||||
"php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"ext-iconv": "*",
|
||||
"thecodingmachine/safe": "^1.3 || ^2.5 || ^3.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-parallel-lint/php-parallel-lint": "1.4.0",
|
||||
"phpstan/extension-installer": "1.4.3",
|
||||
"phpstan/phpstan": "1.12.32 || 2.1.32",
|
||||
"phpstan/phpstan-phpunit": "1.4.2 || 2.0.8",
|
||||
"phpstan/phpstan-strict-rules": "1.6.2 || 2.0.7",
|
||||
"phpunit/phpunit": "8.5.52",
|
||||
"rawr/phpunit-data-provider": "3.3.1",
|
||||
"rector/rector": "1.2.10 || 2.2.8",
|
||||
"rector/type-perfect": "1.0.0 || 2.1.0",
|
||||
"squizlabs/php_codesniffer": "4.0.1",
|
||||
"thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "for parsing UTF-8 CSS"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Sabberworm\\CSS\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/Rule/Rule.php",
|
||||
"src/RuleSet/RuleContainer.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Sabberworm\\CSS\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"phpstan/extension-installer": true
|
||||
},
|
||||
"preferred-install": {
|
||||
"*": "dist"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "9.3.x-dev"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"check": [
|
||||
"@check:static",
|
||||
"@check:dynamic"
|
||||
],
|
||||
"check:composer:normalize": "\"./.phive/composer-normalize\" --dry-run",
|
||||
"check:dynamic": [
|
||||
"@check:tests"
|
||||
],
|
||||
"check:php:codesniffer": "phpcs --standard=config/phpcs.xml bin config src tests",
|
||||
"check:php:fixer": "\"./.phive/php-cs-fixer\" --config=config/php-cs-fixer.php fix --dry-run -v --show-progress=dots --diff bin config src tests",
|
||||
"check:php:lint": "parallel-lint bin config src tests",
|
||||
"check:php:rector": "rector process --no-progress-bar --dry-run --config=config/rector.php",
|
||||
"check:php:stan": "phpstan --no-progress --configuration=config/phpstan.neon",
|
||||
"check:static": [
|
||||
"@check:composer:normalize",
|
||||
"@check:php:fixer",
|
||||
"@check:php:codesniffer",
|
||||
"@check:php:lint",
|
||||
"@check:php:rector",
|
||||
"@check:php:stan"
|
||||
],
|
||||
"check:tests": [
|
||||
"@check:tests:unit"
|
||||
],
|
||||
"check:tests:coverage": "phpunit --do-not-cache-result --coverage-clover=coverage.xml",
|
||||
"check:tests:sof": "phpunit --stop-on-failure --do-not-cache-result",
|
||||
"check:tests:unit": "phpunit --do-not-cache-result",
|
||||
"fix": [
|
||||
"@fix:php"
|
||||
],
|
||||
"fix:composer:normalize": "\"./.phive/composer-normalize\" --no-check-lock",
|
||||
"fix:php": [
|
||||
"@fix:composer:normalize",
|
||||
"@fix:php:rector",
|
||||
"@fix:php:codesniffer",
|
||||
"@fix:php:fixer"
|
||||
],
|
||||
"fix:php:codesniffer": "phpcbf --standard=config/phpcs.xml bin config src tests",
|
||||
"fix:php:fixer": "\"./.phive/php-cs-fixer\" --config=config/php-cs-fixer.php fix bin config src tests",
|
||||
"fix:php:rector": "rector process --config=config/rector.php",
|
||||
"phpstan:baseline": "phpstan --configuration=config/phpstan.neon --generate-baseline=config/phpstan-baseline.neon --allow-empty-baseline",
|
||||
"phpstan:clearcache": "phpstan clear-result-cache"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"check": "Runs all dynamic and static code checks.",
|
||||
"check:composer:normalize": "Checks the formatting and structure of the composer.json.",
|
||||
"check:dynamic": "Runs all dynamic code checks (i.e., currently, the unit tests).",
|
||||
"check:php:codesniffer": "Checks the code style with PHP_CodeSniffer.",
|
||||
"check:php:fixer": "Checks the code style with PHP CS Fixer.",
|
||||
"check:php:lint": "Checks the syntax of the PHP code.",
|
||||
"check:php:rector": "Checks the code for possible code updates and refactoring.",
|
||||
"check:php:stan": "Checks the types with PHPStan.",
|
||||
"check:static": "Runs all static code analysis checks for the code.",
|
||||
"check:tests": "Runs all dynamic tests (i.e., currently, the unit tests).",
|
||||
"check:tests:coverage": "Runs the unit tests with code coverage.",
|
||||
"check:tests:sof": "Runs the unit tests and stops at the first failure.",
|
||||
"check:tests:unit": "Runs all unit tests.",
|
||||
"fix": "Runs all fixers",
|
||||
"fix:composer:normalize": "Reformats and sorts the composer.json file.",
|
||||
"fix:php": "Autofixes all autofixable issues in the PHP code.",
|
||||
"fix:php:codesniffer": "Reformats the code with PHP_CodeSniffer.",
|
||||
"fix:php:fixer": "Fixes autofixable issues found by PHP CS Fixer.",
|
||||
"fix:php:rector": "Fixes autofixable issues found by Rector.",
|
||||
"phpstan:baseline": "Updates the PHPStan baseline file to match the code.",
|
||||
"phpstan:clearcache": "Clears the PHPStan cache."
|
||||
}
|
||||
}
|
||||
17
vendor/sabberworm/php-css-parser/src/CSSElement.php
vendored
Normal file
17
vendor/sabberworm/php-css-parser/src/CSSElement.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS;
|
||||
|
||||
/**
|
||||
* Represents any entity in the CSS that is encapsulated by a class.
|
||||
*
|
||||
* Its primary purpose is to provide a type for use with `Document::getAllValues()`
|
||||
* when a subset of values from a particular part of the document is required.
|
||||
*
|
||||
* Thus, elements which don't contain `Value`s (such as statement at-rules) don't need to implement this.
|
||||
*
|
||||
* It extends `Renderable` because every element is renderable.
|
||||
*/
|
||||
interface CSSElement extends Renderable {}
|
||||
72
vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php
vendored
Normal file
72
vendor/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\CSSList;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Property\AtRule;
|
||||
|
||||
/**
|
||||
* A `BlockList` constructed by an unknown at-rule. `@media` rules are rendered into `AtRuleBlockList` objects.
|
||||
*/
|
||||
class AtRuleBlockList extends CSSBlockList implements AtRule
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $arguments;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $type
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $type, string $arguments = '', ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
$this->type = $type;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleName(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function atRuleArgs(): string
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
$result = $formatter->comments($this);
|
||||
$result .= $outputFormat->getContentBeforeAtRuleBlock();
|
||||
$arguments = $this->arguments;
|
||||
if ($arguments !== '') {
|
||||
$arguments = ' ' . $arguments;
|
||||
}
|
||||
$result .= "@{$this->type}$arguments{$formatter->spaceBeforeOpeningBrace()}{";
|
||||
$result .= $this->renderListContents($outputFormat);
|
||||
$result .= '}';
|
||||
$result .= $outputFormat->getContentAfterAtRuleBlock();
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function isRootList(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
184
vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php
vendored
Normal file
184
vendor/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\CSSList;
|
||||
|
||||
use Sabberworm\CSS\CSSElement;
|
||||
use Sabberworm\CSS\Property\Declaration;
|
||||
use Sabberworm\CSS\Property\Selector;
|
||||
use Sabberworm\CSS\RuleSet\DeclarationBlock;
|
||||
use Sabberworm\CSS\RuleSet\DeclarationList;
|
||||
use Sabberworm\CSS\RuleSet\RuleSet;
|
||||
use Sabberworm\CSS\Value\CSSFunction;
|
||||
use Sabberworm\CSS\Value\Value;
|
||||
use Sabberworm\CSS\Value\ValueList;
|
||||
|
||||
/**
|
||||
* A `CSSBlockList` is a `CSSList` whose `DeclarationBlock`s are guaranteed to contain valid declaration blocks or
|
||||
* at-rules.
|
||||
*
|
||||
* Most `CSSList`s conform to this category but some at-rules (such as `@keyframes`) do not.
|
||||
*/
|
||||
abstract class CSSBlockList extends CSSList
|
||||
{
|
||||
/**
|
||||
* Gets all `DeclarationBlock` objects recursively, no matter how deeply nested the selectors are.
|
||||
*
|
||||
* @return list<DeclarationBlock>
|
||||
*/
|
||||
public function getAllDeclarationBlocks(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->contents as $item) {
|
||||
if ($item instanceof DeclarationBlock) {
|
||||
$result[] = $item;
|
||||
} elseif ($item instanceof CSSBlockList) {
|
||||
$result = \array_merge($result, $item->getAllDeclarationBlocks());
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all `RuleSet` objects recursively found in the tree, no matter how deeply nested the rule sets are.
|
||||
*
|
||||
* @return list<RuleSet>
|
||||
*/
|
||||
public function getAllRuleSets(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->contents as $item) {
|
||||
if ($item instanceof RuleSet) {
|
||||
$result[] = $item;
|
||||
} elseif ($item instanceof CSSBlockList) {
|
||||
$result = \array_merge($result, $item->getAllRuleSets());
|
||||
} elseif ($item instanceof DeclarationBlock) {
|
||||
$result[] = $item->getRuleSet();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all `Value` objects found recursively in `Declaration`s in the tree.
|
||||
*
|
||||
* @param CSSElement|null $element
|
||||
* This is the `CSSList` or `RuleSet` to start the search from (defaults to the whole document).
|
||||
* @param string|null $ruleSearchPattern
|
||||
* This allows filtering rules by property name
|
||||
* (e.g. if "color" is passed, only `Value`s from `color` properties will be returned,
|
||||
* or if "font-" is provided, `Value`s from all font rules, like `font-size`, and including `font` itself,
|
||||
* will be returned).
|
||||
* @param bool $searchInFunctionArguments whether to also return `Value` objects used as `CSSFunction` arguments.
|
||||
*
|
||||
* @return list<Value>
|
||||
*
|
||||
* @see RuleSet->getRules()
|
||||
*/
|
||||
public function getAllValues(
|
||||
?CSSElement $element = null,
|
||||
?string $ruleSearchPattern = null,
|
||||
bool $searchInFunctionArguments = false
|
||||
): array {
|
||||
$element = $element ?? $this;
|
||||
|
||||
$result = [];
|
||||
if ($element instanceof CSSBlockList) {
|
||||
foreach ($element->getContents() as $contentItem) {
|
||||
// Statement at-rules are skipped since they do not contain values.
|
||||
if ($contentItem instanceof CSSElement) {
|
||||
$result = \array_merge(
|
||||
$result,
|
||||
$this->getAllValues($contentItem, $ruleSearchPattern, $searchInFunctionArguments)
|
||||
);
|
||||
}
|
||||
}
|
||||
} elseif ($element instanceof DeclarationList) {
|
||||
foreach ($element->getRules($ruleSearchPattern) as $rule) {
|
||||
$result = \array_merge(
|
||||
$result,
|
||||
$this->getAllValues($rule, $ruleSearchPattern, $searchInFunctionArguments)
|
||||
);
|
||||
}
|
||||
} elseif ($element instanceof Declaration) {
|
||||
$value = $element->getValue();
|
||||
// `string` values are discarded.
|
||||
if ($value instanceof CSSElement) {
|
||||
$result = \array_merge(
|
||||
$result,
|
||||
$this->getAllValues($value, $ruleSearchPattern, $searchInFunctionArguments)
|
||||
);
|
||||
}
|
||||
} elseif ($element instanceof ValueList) {
|
||||
if ($searchInFunctionArguments || !($element instanceof CSSFunction)) {
|
||||
foreach ($element->getListComponents() as $component) {
|
||||
// `string` components are discarded.
|
||||
if ($component instanceof CSSElement) {
|
||||
$result = \array_merge(
|
||||
$result,
|
||||
$this->getAllValues($component, $ruleSearchPattern, $searchInFunctionArguments)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($element instanceof Value) {
|
||||
$result[] = $element;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Selector>
|
||||
*/
|
||||
protected function getAllSelectors(?string $specificitySearch = null): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->getAllDeclarationBlocks() as $declarationBlock) {
|
||||
foreach ($declarationBlock->getSelectors() as $selector) {
|
||||
if ($specificitySearch === null) {
|
||||
$result[] = $selector;
|
||||
} else {
|
||||
$comparator = '===';
|
||||
$expressionParts = \explode(' ', $specificitySearch);
|
||||
$targetSpecificity = $expressionParts[0];
|
||||
if (\count($expressionParts) > 1) {
|
||||
$comparator = $expressionParts[0];
|
||||
$targetSpecificity = $expressionParts[1];
|
||||
}
|
||||
$targetSpecificity = (int) $targetSpecificity;
|
||||
$selectorSpecificity = $selector->getSpecificity();
|
||||
$comparatorMatched = false;
|
||||
switch ($comparator) {
|
||||
case '<=':
|
||||
$comparatorMatched = $selectorSpecificity <= $targetSpecificity;
|
||||
break;
|
||||
case '<':
|
||||
$comparatorMatched = $selectorSpecificity < $targetSpecificity;
|
||||
break;
|
||||
case '>=':
|
||||
$comparatorMatched = $selectorSpecificity >= $targetSpecificity;
|
||||
break;
|
||||
case '>':
|
||||
$comparatorMatched = $selectorSpecificity > $targetSpecificity;
|
||||
break;
|
||||
default:
|
||||
$comparatorMatched = $selectorSpecificity === $targetSpecificity;
|
||||
break;
|
||||
}
|
||||
if ($comparatorMatched) {
|
||||
$result[] = $selector;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
478
vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php
vendored
Normal file
478
vendor/sabberworm/php-css-parser/src/CSSList/CSSList.php
vendored
Normal file
@@ -0,0 +1,478 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\CSSList;
|
||||
|
||||
use Sabberworm\CSS\Comment\CommentContainer;
|
||||
use Sabberworm\CSS\CSSElement;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\Property\AtRule;
|
||||
use Sabberworm\CSS\Property\Charset;
|
||||
use Sabberworm\CSS\Property\CSSNamespace;
|
||||
use Sabberworm\CSS\Property\Import;
|
||||
use Sabberworm\CSS\Property\Selector;
|
||||
use Sabberworm\CSS\RuleSet\AtRuleSet;
|
||||
use Sabberworm\CSS\RuleSet\DeclarationBlock;
|
||||
use Sabberworm\CSS\RuleSet\RuleSet;
|
||||
use Sabberworm\CSS\Value\CSSString;
|
||||
use Sabberworm\CSS\Value\URL;
|
||||
use Sabberworm\CSS\Value\Value;
|
||||
|
||||
use function Safe\preg_match;
|
||||
|
||||
/**
|
||||
* This is the most generic container available. It can contain `DeclarationBlock`s (rule sets with a selector),
|
||||
* `RuleSet`s as well as other `CSSList` objects.
|
||||
*
|
||||
* It can also contain `Import` and `Charset` objects stemming from at-rules.
|
||||
*
|
||||
* Note that `CSSListItem` extends both `Commentable` and `Renderable`,
|
||||
* so those interfaces must also be implemented by concrete subclasses.
|
||||
*/
|
||||
abstract class CSSList implements CSSElement, CSSListItem, Positionable
|
||||
{
|
||||
use CommentContainer;
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* @var array<int<0, max>, CSSListItem>
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $contents = [];
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(?int $lineNumber = null)
|
||||
{
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws SourceException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parseList(ParserState $parserState, CSSList $list): void
|
||||
{
|
||||
$isRoot = $list instanceof Document;
|
||||
$usesLenientParsing = $parserState->getSettings()->usesLenientParsing();
|
||||
$comments = [];
|
||||
while (!$parserState->isEnd()) {
|
||||
$parserState->consumeWhiteSpace($comments);
|
||||
$listItem = null;
|
||||
if ($usesLenientParsing) {
|
||||
try {
|
||||
$positionBeforeParse = $parserState->currentColumn();
|
||||
$listItem = self::parseListItem($parserState, $list);
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
$listItem = false;
|
||||
// If the failed parsing did not consume anything that was to come ...
|
||||
if ($parserState->currentColumn() === $positionBeforeParse && !$parserState->isEnd()) {
|
||||
// ... the unexpected token needs to be skipped, otherwise there'll be an infinite loop.
|
||||
$parserState->consume(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$listItem = self::parseListItem($parserState, $list);
|
||||
}
|
||||
if ($listItem === null) {
|
||||
// List parsing finished
|
||||
return;
|
||||
}
|
||||
if ($listItem) {
|
||||
$listItem->addComments($comments);
|
||||
$list->append($listItem);
|
||||
}
|
||||
$comments = [];
|
||||
$parserState->consumeWhiteSpace($comments);
|
||||
}
|
||||
$list->addComments($comments);
|
||||
if (!$isRoot && !$usesLenientParsing) {
|
||||
throw new SourceException('Unexpected end of document', $parserState->currentLine());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSSListItem|false|null
|
||||
* If `null` is returned, it means the end of the list has been reached.
|
||||
* If `false` is returned, it means an invalid item has been encountered,
|
||||
* but parsing of the next item should proceed.
|
||||
*
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseListItem(ParserState $parserState, CSSList $list)
|
||||
{
|
||||
$isRoot = $list instanceof Document;
|
||||
if ($parserState->comes('@')) {
|
||||
$atRule = self::parseAtRule($parserState);
|
||||
if ($atRule instanceof Charset) {
|
||||
if (!$isRoot) {
|
||||
throw new UnexpectedTokenException(
|
||||
'@charset may only occur in root document',
|
||||
'',
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
if (\count($list->getContents()) > 0) {
|
||||
throw new UnexpectedTokenException(
|
||||
'@charset must be the first parseable token in a document',
|
||||
'',
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
$parserState->setCharset($atRule->getCharset());
|
||||
}
|
||||
return $atRule;
|
||||
} elseif ($parserState->comes('}')) {
|
||||
if ($isRoot) {
|
||||
if ($parserState->getSettings()->usesLenientParsing()) {
|
||||
$parserState->consume(1);
|
||||
return self::parseListItem($parserState, $list);
|
||||
} else {
|
||||
throw new SourceException('Unopened {', $parserState->currentLine());
|
||||
}
|
||||
} else {
|
||||
// End of list
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return DeclarationBlock::parse($parserState, $list) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*/
|
||||
private static function parseAtRule(ParserState $parserState): ?CSSListItem
|
||||
{
|
||||
$parserState->consume('@');
|
||||
$identifier = $parserState->parseIdentifier();
|
||||
$identifierLineNumber = $parserState->currentLine();
|
||||
$parserState->consumeWhiteSpace();
|
||||
if ($identifier === 'import') {
|
||||
$location = URL::parse($parserState);
|
||||
$parserState->consumeWhiteSpace();
|
||||
$mediaQuery = null;
|
||||
if (!$parserState->comes(';')) {
|
||||
$mediaQuery = \trim($parserState->consumeUntil([';', ParserState::EOF]));
|
||||
if ($mediaQuery === '') {
|
||||
$mediaQuery = null;
|
||||
}
|
||||
}
|
||||
$parserState->consumeUntil([';', ParserState::EOF], true, true);
|
||||
return new Import($location, $mediaQuery, $identifierLineNumber);
|
||||
} elseif ($identifier === 'charset') {
|
||||
$charsetString = CSSString::parse($parserState);
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consumeUntil([';', ParserState::EOF], true, true);
|
||||
return new Charset($charsetString, $identifierLineNumber);
|
||||
} elseif (self::identifierIs($identifier, 'keyframes')) {
|
||||
$result = new KeyFrame($identifierLineNumber);
|
||||
$result->setVendorKeyFrame($identifier);
|
||||
$result->setAnimationName(\trim($parserState->consumeUntil('{', false, true)));
|
||||
CSSList::parseList($parserState, $result);
|
||||
if ($parserState->comes('}')) {
|
||||
$parserState->consume('}');
|
||||
}
|
||||
return $result;
|
||||
} elseif ($identifier === 'namespace') {
|
||||
$prefix = null;
|
||||
$url = Value::parsePrimitiveValue($parserState);
|
||||
if (!$parserState->comes(';')) {
|
||||
$prefix = $url;
|
||||
$url = Value::parsePrimitiveValue($parserState);
|
||||
}
|
||||
$parserState->consumeUntil([';', ParserState::EOF], true, true);
|
||||
if ($prefix !== null && !\is_string($prefix)) {
|
||||
throw new UnexpectedTokenException('Wrong namespace prefix', $prefix, 'custom', $identifierLineNumber);
|
||||
}
|
||||
if (!($url instanceof CSSString || $url instanceof URL)) {
|
||||
throw new UnexpectedTokenException(
|
||||
'Wrong namespace url of invalid type',
|
||||
$url,
|
||||
'custom',
|
||||
$identifierLineNumber
|
||||
);
|
||||
}
|
||||
return new CSSNamespace($url, $prefix, $identifierLineNumber);
|
||||
} else {
|
||||
// Unknown other at rule (font-face or such)
|
||||
$arguments = \trim($parserState->consumeUntil('{', false, true));
|
||||
if (\substr_count($arguments, '(') !== \substr_count($arguments, ')')) {
|
||||
if ($parserState->getSettings()->usesLenientParsing()) {
|
||||
return null;
|
||||
} else {
|
||||
throw new SourceException('Unmatched brace count in media query', $parserState->currentLine());
|
||||
}
|
||||
}
|
||||
$useRuleSet = true;
|
||||
foreach (\explode('/', AtRule::BLOCK_RULES) as $blockRuleName) {
|
||||
if (self::identifierIs($identifier, $blockRuleName)) {
|
||||
$useRuleSet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($useRuleSet) {
|
||||
$atRule = new AtRuleSet($identifier, $arguments, $identifierLineNumber);
|
||||
RuleSet::parseRuleSet($parserState, $atRule);
|
||||
} else {
|
||||
$atRule = new AtRuleBlockList($identifier, $arguments, $identifierLineNumber);
|
||||
CSSList::parseList($parserState, $atRule);
|
||||
if ($parserState->comes('}')) {
|
||||
$parserState->consume('}');
|
||||
}
|
||||
}
|
||||
return $atRule;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private static function identifierIs(string $identifier, string $match): bool
|
||||
{
|
||||
if (\strcasecmp($identifier, $match) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return preg_match("/^(-\\w+-)?$match$/i", $identifier) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends an item to the list of contents.
|
||||
*/
|
||||
public function prepend(CSSListItem $item): void
|
||||
{
|
||||
\array_unshift($this->contents, $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an item to the list of contents.
|
||||
*/
|
||||
public function append(CSSListItem $item): void
|
||||
{
|
||||
$this->contents[] = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splices the list of contents.
|
||||
*
|
||||
* @param array<int, CSSListItem> $replacement
|
||||
*/
|
||||
public function splice(int $offset, ?int $length = null, ?array $replacement = null): void
|
||||
{
|
||||
\array_splice($this->contents, $offset, $length, $replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an item in the CSS list before its sibling. If the desired sibling cannot be found,
|
||||
* the item is appended at the end.
|
||||
*/
|
||||
public function insertBefore(CSSListItem $item, CSSListItem $sibling): void
|
||||
{
|
||||
if (\in_array($sibling, $this->contents, true)) {
|
||||
$this->replace($sibling, [$item, $sibling]);
|
||||
} else {
|
||||
$this->append($item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the CSS list.
|
||||
*
|
||||
* @param CSSListItem $itemToRemove
|
||||
* May be a `RuleSet` (most likely a `DeclarationBlock`), an `Import`,
|
||||
* a `Charset` or another `CSSList` (most likely a `MediaQuery`)
|
||||
*
|
||||
* @return bool whether the item was removed
|
||||
*/
|
||||
public function remove(CSSListItem $itemToRemove): bool
|
||||
{
|
||||
$key = \array_search($itemToRemove, $this->contents, true);
|
||||
if ($key !== false) {
|
||||
unset($this->contents[$key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an item from the CSS list.
|
||||
*
|
||||
* @param CSSListItem $oldItem
|
||||
* May be a `RuleSet` (most likely a `DeclarationBlock`), an `Import`, a `Charset`
|
||||
* or another `CSSList` (most likely a `MediaQuery`)
|
||||
* @param CSSListItem|array<CSSListItem> $newItem
|
||||
*/
|
||||
public function replace(CSSListItem $oldItem, $newItem): bool
|
||||
{
|
||||
$key = \array_search($oldItem, $this->contents, true);
|
||||
if ($key !== false) {
|
||||
if (\is_array($newItem)) {
|
||||
\array_splice($this->contents, $key, 1, $newItem);
|
||||
} else {
|
||||
\array_splice($this->contents, $key, 1, [$newItem]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, CSSListItem> $contents
|
||||
*/
|
||||
public function setContents(array $contents): void
|
||||
{
|
||||
$this->contents = [];
|
||||
foreach ($contents as $content) {
|
||||
$this->append($content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a declaration block from the CSS list if it matches all given selectors.
|
||||
*
|
||||
* @param DeclarationBlock|array<Selector>|string $selectors the selectors to match
|
||||
* @param bool $removeAll whether to stop at the first declaration block found or remove all blocks
|
||||
*/
|
||||
public function removeDeclarationBlockBySelector($selectors, bool $removeAll = false): void
|
||||
{
|
||||
if ($selectors instanceof DeclarationBlock) {
|
||||
$selectors = $selectors->getSelectors();
|
||||
}
|
||||
if (!\is_array($selectors)) {
|
||||
$selectors = \explode(',', $selectors);
|
||||
}
|
||||
foreach ($selectors as $key => &$selector) {
|
||||
if (!($selector instanceof Selector)) {
|
||||
if (!Selector::isValid($selector)) {
|
||||
throw new UnexpectedTokenException(
|
||||
"Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
|
||||
$selector,
|
||||
'custom'
|
||||
);
|
||||
}
|
||||
$selector = new Selector($selector);
|
||||
}
|
||||
}
|
||||
foreach ($this->contents as $key => $item) {
|
||||
if (!($item instanceof DeclarationBlock)) {
|
||||
continue;
|
||||
}
|
||||
if (self::selectorsMatch($item->getSelectors(), $selectors)) {
|
||||
unset($this->contents[$key]);
|
||||
if (!$removeAll) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function renderListContents(OutputFormat $outputFormat): string
|
||||
{
|
||||
$result = '';
|
||||
$isFirst = true;
|
||||
$nextLevelFormat = $outputFormat;
|
||||
if (!$this->isRootList()) {
|
||||
$nextLevelFormat = $outputFormat->nextLevel();
|
||||
}
|
||||
$nextLevelFormatter = $nextLevelFormat->getFormatter();
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
foreach ($this->contents as $listItem) {
|
||||
$renderedCss = $formatter->safely(static function () use ($nextLevelFormat, $listItem): string {
|
||||
return $listItem->render($nextLevelFormat);
|
||||
});
|
||||
if ($renderedCss === null) {
|
||||
continue;
|
||||
}
|
||||
if ($isFirst) {
|
||||
$isFirst = false;
|
||||
$result .= $nextLevelFormatter->spaceBeforeBlocks();
|
||||
} else {
|
||||
$result .= $nextLevelFormatter->spaceBetweenBlocks();
|
||||
}
|
||||
$result .= $renderedCss;
|
||||
}
|
||||
|
||||
if (!$isFirst) {
|
||||
// Had some output
|
||||
$result .= $formatter->spaceAfterBlocks();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the list can not be further outdented. Only important when rendering.
|
||||
*/
|
||||
abstract public function isRootList(): bool;
|
||||
|
||||
/**
|
||||
* Returns the stored items.
|
||||
*
|
||||
* @return array<int<0, max>, CSSListItem>
|
||||
*/
|
||||
public function getContents(): array
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Selector> $selectors1
|
||||
* @param list<Selector> $selectors2
|
||||
*/
|
||||
private static function selectorsMatch(array $selectors1, array $selectors2): bool
|
||||
{
|
||||
$selectorStrings1 = self::getSelectorStrings($selectors1);
|
||||
$selectorStrings2 = self::getSelectorStrings($selectors2);
|
||||
|
||||
\sort($selectorStrings1);
|
||||
\sort($selectorStrings2);
|
||||
|
||||
return $selectorStrings1 === $selectorStrings2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Selector> $selectors
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private static function getSelectorStrings(array $selectors): array
|
||||
{
|
||||
return \array_map(
|
||||
static function (Selector $selector): string {
|
||||
return $selector->getSelector();
|
||||
},
|
||||
$selectors
|
||||
);
|
||||
}
|
||||
}
|
||||
18
vendor/sabberworm/php-css-parser/src/CSSList/CSSListItem.php
vendored
Normal file
18
vendor/sabberworm/php-css-parser/src/CSSList/CSSListItem.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\CSSList;
|
||||
|
||||
use Sabberworm\CSS\Comment\Commentable;
|
||||
use Sabberworm\CSS\Renderable;
|
||||
|
||||
/**
|
||||
* Represents anything that can be in the `$contents` of a `CSSList`.
|
||||
*
|
||||
* The interface does not define any methods to implement.
|
||||
* It's purpose is to allow a single type to be specified for `CSSList::$contents` and manipulation methods thereof.
|
||||
* It extends `Commentable` and `Renderable` because all `CSSListItem`s are both.
|
||||
* This allows implementations to call methods from those interfaces without any additional type checks.
|
||||
*/
|
||||
interface CSSListItem extends Commentable, Renderable {}
|
||||
65
vendor/sabberworm/php-css-parser/src/CSSList/Document.php
vendored
Normal file
65
vendor/sabberworm/php-css-parser/src/CSSList/Document.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\CSSList;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Property\Selector;
|
||||
|
||||
/**
|
||||
* This class represents the root of a parsed CSS file. It contains all top-level CSS contents: mostly declaration
|
||||
* blocks, but also any at-rules encountered (`Import` and `Charset`).
|
||||
*/
|
||||
class Document extends CSSBlockList
|
||||
{
|
||||
/**
|
||||
* @throws SourceException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState): Document
|
||||
{
|
||||
$document = new Document($parserState->currentLine());
|
||||
CSSList::parseList($parserState, $document);
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all `Selector` objects with the requested specificity found recursively in the tree.
|
||||
*
|
||||
* Note that this does not yield the full `DeclarationBlock` that the selector belongs to
|
||||
* (and, currently, there is no way to get to that).
|
||||
*
|
||||
* @param string|null $specificitySearch
|
||||
* An optional filter by specificity.
|
||||
* May contain a comparison operator and a number or just a number (defaults to "==").
|
||||
*
|
||||
* @return list<Selector>
|
||||
*
|
||||
* @example `getSelectorsBySpecificity('>= 100')`
|
||||
*/
|
||||
public function getSelectorsBySpecificity(?string $specificitySearch = null): array
|
||||
{
|
||||
return $this->getAllSelectors($specificitySearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides `render()` to make format argument optional.
|
||||
*/
|
||||
public function render(?OutputFormat $outputFormat = null): string
|
||||
{
|
||||
if ($outputFormat === null) {
|
||||
$outputFormat = new OutputFormat();
|
||||
}
|
||||
return $outputFormat->getFormatter()->comments($this) . $this->renderListContents($outputFormat);
|
||||
}
|
||||
|
||||
public function isRootList(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
87
vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php
vendored
Normal file
87
vendor/sabberworm/php-css-parser/src/CSSList/KeyFrame.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\CSSList;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Property\AtRule;
|
||||
|
||||
class KeyFrame extends CSSList implements AtRule
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private $vendorKeyFrame = 'keyframes';
|
||||
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private $animationName = 'none';
|
||||
|
||||
/**
|
||||
* @param non-empty-string $vendorKeyFrame
|
||||
*/
|
||||
public function setVendorKeyFrame(string $vendorKeyFrame): void
|
||||
{
|
||||
$this->vendorKeyFrame = $vendorKeyFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getVendorKeyFrame(): string
|
||||
{
|
||||
return $this->vendorKeyFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $animationName
|
||||
*/
|
||||
public function setAnimationName(string $animationName): void
|
||||
{
|
||||
$this->animationName = $animationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getAnimationName(): string
|
||||
{
|
||||
return $this->animationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
$result = $formatter->comments($this);
|
||||
$result .= "@{$this->vendorKeyFrame} {$this->animationName}{$formatter->spaceBeforeOpeningBrace()}{";
|
||||
$result .= $this->renderListContents($outputFormat);
|
||||
$result .= '}';
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function isRootList(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleName(): string
|
||||
{
|
||||
return $this->vendorKeyFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleArgs(): string
|
||||
{
|
||||
return $this->animationName;
|
||||
}
|
||||
}
|
||||
66
vendor/sabberworm/php-css-parser/src/Comment/Comment.php
vendored
Normal file
66
vendor/sabberworm/php-css-parser/src/Comment/Comment.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Comment;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\Renderable;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
class Comment implements Positionable, Renderable
|
||||
{
|
||||
use Position;
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $commentText;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $commentText = '', ?int $lineNumber = null)
|
||||
{
|
||||
$this->commentText = $commentText;
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
public function getComment(): string
|
||||
{
|
||||
return $this->commentText;
|
||||
}
|
||||
|
||||
public function setComment(string $commentText): void
|
||||
{
|
||||
$this->commentText = $commentText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return '/*' . $this->commentText . '*/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
// "contents" is the term used in the W3C specs:
|
||||
// https://www.w3.org/TR/CSS22/syndata.html#comments
|
||||
'contents' => $this->commentText,
|
||||
];
|
||||
}
|
||||
}
|
||||
44
vendor/sabberworm/php-css-parser/src/Comment/CommentContainer.php
vendored
Normal file
44
vendor/sabberworm/php-css-parser/src/Comment/CommentContainer.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Comment;
|
||||
|
||||
/**
|
||||
* Provides a standard reusable implementation of `Commentable`.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @phpstan-require-implements Commentable
|
||||
*/
|
||||
trait CommentContainer
|
||||
{
|
||||
/**
|
||||
* @var list<Comment>
|
||||
*/
|
||||
protected $comments = [];
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*/
|
||||
public function addComments(array $comments): void
|
||||
{
|
||||
$this->comments = \array_merge($this->comments, $comments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Comment>
|
||||
*/
|
||||
public function getComments(): array
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*/
|
||||
public function setComments(array $comments): void
|
||||
{
|
||||
$this->comments = $comments;
|
||||
}
|
||||
}
|
||||
26
vendor/sabberworm/php-css-parser/src/Comment/Commentable.php
vendored
Normal file
26
vendor/sabberworm/php-css-parser/src/Comment/Commentable.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Comment;
|
||||
|
||||
/**
|
||||
* A standard implementation of this interface is available in the `CommentContainer` trait.
|
||||
*/
|
||||
interface Commentable
|
||||
{
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*/
|
||||
public function addComments(array $comments): void;
|
||||
|
||||
/**
|
||||
* @return list<Comment>
|
||||
*/
|
||||
public function getComments(): array;
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*/
|
||||
public function setComments(array $comments): void;
|
||||
}
|
||||
778
vendor/sabberworm/php-css-parser/src/OutputFormat.php
vendored
Normal file
778
vendor/sabberworm/php-css-parser/src/OutputFormat.php
vendored
Normal file
@@ -0,0 +1,778 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS;
|
||||
|
||||
final class OutputFormat
|
||||
{
|
||||
/**
|
||||
* @var '"'|"'"
|
||||
*/
|
||||
private $stringQuotingType = '"';
|
||||
|
||||
/**
|
||||
* Output RGB colors in hash notation if possible
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $usesRgbHashNotation = true;
|
||||
|
||||
/**
|
||||
* Declaration format
|
||||
*
|
||||
* Semicolon after the last rule of a declaration block can be omitted. To do that, set this false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $renderSemicolonAfterLastRule = true;
|
||||
|
||||
/**
|
||||
* Spacing
|
||||
* Note that these strings are not sanity-checked: the value should only consist of whitespace
|
||||
* Any newline character will be indented according to the current level.
|
||||
* The triples (After, Before, Between) can be set using a wildcard
|
||||
* (e.g. `$outputFormat->set('Space*Rules', "\n");`)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $spaceAfterRuleName = ' ';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceBeforeRules = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceAfterRules = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceBetweenRules = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceBeforeBlocks = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceAfterBlocks = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceBetweenBlocks = "\n";
|
||||
|
||||
/**
|
||||
* Content injected in and around at-rule blocks.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $contentBeforeAtRuleBlock = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $contentAfterAtRuleBlock = '';
|
||||
|
||||
/**
|
||||
* This is what’s printed before and after the comma if a declaration block contains multiple selectors.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $spaceBeforeSelectorSeparator = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceAfterSelectorSeparator = ' ';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceAroundSelectorCombinator = ' ';
|
||||
|
||||
/**
|
||||
* This is what’s inserted before the separator in value lists, by default.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $spaceBeforeListArgumentSeparator = '';
|
||||
|
||||
/**
|
||||
* Keys are separators (e.g. `,`). Values are the space sequence to insert, or an empty string.
|
||||
*
|
||||
* @var array<non-empty-string, string>
|
||||
*/
|
||||
private $spaceBeforeListArgumentSeparators = [];
|
||||
|
||||
/**
|
||||
* This is what’s inserted after the separator in value lists, by default.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $spaceAfterListArgumentSeparator = '';
|
||||
|
||||
/**
|
||||
* Keys are separators (e.g. `,`). Values are the space sequence to insert, or an empty string.
|
||||
*
|
||||
* @var array<non-empty-string, string>
|
||||
*/
|
||||
private $spaceAfterListArgumentSeparators = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $spaceBeforeOpeningBrace = ' ';
|
||||
|
||||
/**
|
||||
* Content injected in and around declaration blocks.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $contentBeforeDeclarationBlock = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $contentAfterDeclarationBlockSelectors = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $contentAfterDeclarationBlock = '';
|
||||
|
||||
/**
|
||||
* Indentation character(s) per level. Only applicable if newlines are used in any of the spacing settings.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $indentation = "\t";
|
||||
|
||||
/**
|
||||
* Output exceptions.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $shouldIgnoreExceptions = false;
|
||||
|
||||
/**
|
||||
* Render comments for lists and RuleSets
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $shouldRenderComments = false;
|
||||
|
||||
/**
|
||||
* @var OutputFormatter|null
|
||||
*/
|
||||
private $outputFormatter;
|
||||
|
||||
/**
|
||||
* @var OutputFormat|null
|
||||
*/
|
||||
private $nextLevelFormat;
|
||||
|
||||
/**
|
||||
* @var int<0, max>
|
||||
*/
|
||||
private $indentationLevel = 0;
|
||||
|
||||
/**
|
||||
* @return '"'|"'"
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getStringQuotingType(): string
|
||||
{
|
||||
return $this->stringQuotingType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param '"'|"'" $quotingType
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setStringQuotingType(string $quotingType): self
|
||||
{
|
||||
$this->stringQuotingType = $quotingType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function usesRgbHashNotation(): bool
|
||||
{
|
||||
return $this->usesRgbHashNotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setRGBHashNotation(bool $usesRgbHashNotation): self
|
||||
{
|
||||
$this->usesRgbHashNotation = $usesRgbHashNotation;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function shouldRenderSemicolonAfterLastRule(): bool
|
||||
{
|
||||
return $this->renderSemicolonAfterLastRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSemicolonAfterLastRule(bool $renderSemicolonAfterLastRule): self
|
||||
{
|
||||
$this->renderSemicolonAfterLastRule = $renderSemicolonAfterLastRule;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceAfterRuleName(): string
|
||||
{
|
||||
return $this->spaceAfterRuleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceAfterRuleName(string $whitespace): self
|
||||
{
|
||||
$this->spaceAfterRuleName = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBeforeRules(): string
|
||||
{
|
||||
return $this->spaceBeforeRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBeforeRules(string $whitespace): self
|
||||
{
|
||||
$this->spaceBeforeRules = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceAfterRules(): string
|
||||
{
|
||||
return $this->spaceAfterRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceAfterRules(string $whitespace): self
|
||||
{
|
||||
$this->spaceAfterRules = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBetweenRules(): string
|
||||
{
|
||||
return $this->spaceBetweenRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBetweenRules(string $whitespace): self
|
||||
{
|
||||
$this->spaceBetweenRules = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBeforeBlocks(): string
|
||||
{
|
||||
return $this->spaceBeforeBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBeforeBlocks(string $whitespace): self
|
||||
{
|
||||
$this->spaceBeforeBlocks = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceAfterBlocks(): string
|
||||
{
|
||||
return $this->spaceAfterBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceAfterBlocks(string $whitespace): self
|
||||
{
|
||||
$this->spaceAfterBlocks = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBetweenBlocks(): string
|
||||
{
|
||||
return $this->spaceBetweenBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBetweenBlocks(string $whitespace): self
|
||||
{
|
||||
$this->spaceBetweenBlocks = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getContentBeforeAtRuleBlock(): string
|
||||
{
|
||||
return $this->contentBeforeAtRuleBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setBeforeAtRuleBlock(string $content): self
|
||||
{
|
||||
$this->contentBeforeAtRuleBlock = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getContentAfterAtRuleBlock(): string
|
||||
{
|
||||
return $this->contentAfterAtRuleBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setAfterAtRuleBlock(string $content): self
|
||||
{
|
||||
$this->contentAfterAtRuleBlock = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBeforeSelectorSeparator(): string
|
||||
{
|
||||
return $this->spaceBeforeSelectorSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBeforeSelectorSeparator(string $whitespace): self
|
||||
{
|
||||
$this->spaceBeforeSelectorSeparator = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceAfterSelectorSeparator(): string
|
||||
{
|
||||
return $this->spaceAfterSelectorSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceAfterSelectorSeparator(string $whitespace): self
|
||||
{
|
||||
$this->spaceAfterSelectorSeparator = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceAroundSelectorCombinator(): string
|
||||
{
|
||||
return $this->spaceAroundSelectorCombinator;
|
||||
}
|
||||
|
||||
/**
|
||||
* The spacing set is also used for the descendent combinator, which is whitespace only,
|
||||
* unless an empty string is set, in which case a space will be used.
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceAroundSelectorCombinator(string $whitespace): self
|
||||
{
|
||||
$this->spaceAroundSelectorCombinator = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBeforeListArgumentSeparator(): string
|
||||
{
|
||||
return $this->spaceBeforeListArgumentSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBeforeListArgumentSeparator(string $whitespace): self
|
||||
{
|
||||
$this->spaceBeforeListArgumentSeparator = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, string>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBeforeListArgumentSeparators(): array
|
||||
{
|
||||
return $this->spaceBeforeListArgumentSeparators;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string, string> $separatorSpaces
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBeforeListArgumentSeparators(array $separatorSpaces): self
|
||||
{
|
||||
$this->spaceBeforeListArgumentSeparators = $separatorSpaces;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceAfterListArgumentSeparator(): string
|
||||
{
|
||||
return $this->spaceAfterListArgumentSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceAfterListArgumentSeparator(string $whitespace): self
|
||||
{
|
||||
$this->spaceAfterListArgumentSeparator = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, string>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceAfterListArgumentSeparators(): array
|
||||
{
|
||||
return $this->spaceAfterListArgumentSeparators;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string, string> $separatorSpaces
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceAfterListArgumentSeparators(array $separatorSpaces): self
|
||||
{
|
||||
$this->spaceAfterListArgumentSeparators = $separatorSpaces;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getSpaceBeforeOpeningBrace(): string
|
||||
{
|
||||
return $this->spaceBeforeOpeningBrace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setSpaceBeforeOpeningBrace(string $whitespace): self
|
||||
{
|
||||
$this->spaceBeforeOpeningBrace = $whitespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getContentBeforeDeclarationBlock(): string
|
||||
{
|
||||
return $this->contentBeforeDeclarationBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setBeforeDeclarationBlock(string $content): self
|
||||
{
|
||||
$this->contentBeforeDeclarationBlock = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getContentAfterDeclarationBlockSelectors(): string
|
||||
{
|
||||
return $this->contentAfterDeclarationBlockSelectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setAfterDeclarationBlockSelectors(string $content): self
|
||||
{
|
||||
$this->contentAfterDeclarationBlockSelectors = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getContentAfterDeclarationBlock(): string
|
||||
{
|
||||
return $this->contentAfterDeclarationBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setAfterDeclarationBlock(string $content): self
|
||||
{
|
||||
$this->contentAfterDeclarationBlock = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getIndentation(): string
|
||||
{
|
||||
return $this->indentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setIndentation(string $indentation): self
|
||||
{
|
||||
$this->indentation = $indentation;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function shouldIgnoreExceptions(): bool
|
||||
{
|
||||
return $this->shouldIgnoreExceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setIgnoreExceptions(bool $ignoreExceptions): self
|
||||
{
|
||||
$this->shouldIgnoreExceptions = $ignoreExceptions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function shouldRenderComments(): bool
|
||||
{
|
||||
return $this->shouldRenderComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setRenderComments(bool $renderComments): self
|
||||
{
|
||||
$this->shouldRenderComments = $renderComments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getIndentationLevel(): int
|
||||
{
|
||||
return $this->indentationLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<1, max> $numberOfTabs
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function indentWithTabs(int $numberOfTabs = 1): self
|
||||
{
|
||||
return $this->setIndentation(\str_repeat("\t", $numberOfTabs));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<1, max> $numberOfSpaces
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function indentWithSpaces(int $numberOfSpaces = 2): self
|
||||
{
|
||||
return $this->setIndentation(\str_repeat(' ', $numberOfSpaces));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public function nextLevel(): self
|
||||
{
|
||||
if ($this->nextLevelFormat === null) {
|
||||
$this->nextLevelFormat = clone $this;
|
||||
$this->nextLevelFormat->indentationLevel++;
|
||||
$this->nextLevelFormat->outputFormatter = null;
|
||||
}
|
||||
return $this->nextLevelFormat;
|
||||
}
|
||||
|
||||
public function beLenient(): void
|
||||
{
|
||||
$this->shouldIgnoreExceptions = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
public function getFormatter(): OutputFormatter
|
||||
{
|
||||
if ($this->outputFormatter === null) {
|
||||
$this->outputFormatter = new OutputFormatter($this);
|
||||
}
|
||||
|
||||
return $this->outputFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of this class without any particular formatting settings.
|
||||
*/
|
||||
public static function create(): self
|
||||
{
|
||||
return new OutputFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of this class with a preset for compact formatting.
|
||||
*/
|
||||
public static function createCompact(): self
|
||||
{
|
||||
$format = self::create();
|
||||
$format
|
||||
->setSpaceBeforeRules('')
|
||||
->setSpaceBetweenRules('')
|
||||
->setSpaceAfterRules('')
|
||||
->setSpaceBeforeBlocks('')
|
||||
->setSpaceBetweenBlocks('')
|
||||
->setSpaceAfterBlocks('')
|
||||
->setSpaceAfterRuleName('')
|
||||
->setSpaceBeforeOpeningBrace('')
|
||||
->setSpaceAfterSelectorSeparator('')
|
||||
->setSpaceAroundSelectorCombinator('')
|
||||
->setSemicolonAfterLastRule(false)
|
||||
->setRenderComments(false);
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of this class with a preset for pretty formatting.
|
||||
*/
|
||||
public static function createPretty(): self
|
||||
{
|
||||
$format = self::create();
|
||||
$format
|
||||
->setSpaceBeforeRules("\n")
|
||||
->setSpaceBetweenRules("\n")
|
||||
->setSpaceAfterRules("\n")
|
||||
->setSpaceBeforeBlocks("\n")
|
||||
->setSpaceBetweenBlocks("\n\n")
|
||||
->setSpaceAfterBlocks("\n")
|
||||
->setSpaceAfterListArgumentSeparators([',' => ' '])
|
||||
->setRenderComments(true);
|
||||
|
||||
return $format;
|
||||
}
|
||||
}
|
||||
235
vendor/sabberworm/php-css-parser/src/OutputFormatter.php
vendored
Normal file
235
vendor/sabberworm/php-css-parser/src/OutputFormatter.php
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS;
|
||||
|
||||
use Sabberworm\CSS\Comment\Commentable;
|
||||
use Sabberworm\CSS\Parsing\OutputException;
|
||||
|
||||
/**
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
class OutputFormatter
|
||||
{
|
||||
/**
|
||||
* @var OutputFormat
|
||||
*/
|
||||
private $outputFormat;
|
||||
|
||||
public function __construct(OutputFormat $outputFormat)
|
||||
{
|
||||
$this->outputFormat = $outputFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $name
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function space(string $name): string
|
||||
{
|
||||
switch ($name) {
|
||||
case 'AfterRuleName':
|
||||
$spaceString = $this->outputFormat->getSpaceAfterRuleName();
|
||||
break;
|
||||
case 'BeforeRules':
|
||||
$spaceString = $this->outputFormat->getSpaceBeforeRules();
|
||||
break;
|
||||
case 'AfterRules':
|
||||
$spaceString = $this->outputFormat->getSpaceAfterRules();
|
||||
break;
|
||||
case 'BetweenRules':
|
||||
$spaceString = $this->outputFormat->getSpaceBetweenRules();
|
||||
break;
|
||||
case 'BeforeBlocks':
|
||||
$spaceString = $this->outputFormat->getSpaceBeforeBlocks();
|
||||
break;
|
||||
case 'AfterBlocks':
|
||||
$spaceString = $this->outputFormat->getSpaceAfterBlocks();
|
||||
break;
|
||||
case 'BetweenBlocks':
|
||||
$spaceString = $this->outputFormat->getSpaceBetweenBlocks();
|
||||
break;
|
||||
case 'BeforeSelectorSeparator':
|
||||
$spaceString = $this->outputFormat->getSpaceBeforeSelectorSeparator();
|
||||
break;
|
||||
case 'AfterSelectorSeparator':
|
||||
$spaceString = $this->outputFormat->getSpaceAfterSelectorSeparator();
|
||||
break;
|
||||
case 'BeforeOpeningBrace':
|
||||
$spaceString = $this->outputFormat->getSpaceBeforeOpeningBrace();
|
||||
break;
|
||||
case 'BeforeListArgumentSeparator':
|
||||
$spaceString = $this->outputFormat->getSpaceBeforeListArgumentSeparator();
|
||||
break;
|
||||
case 'AfterListArgumentSeparator':
|
||||
$spaceString = $this->outputFormat->getSpaceAfterListArgumentSeparator();
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown space type: $name", 1740049248);
|
||||
}
|
||||
|
||||
return $this->prepareSpace($spaceString);
|
||||
}
|
||||
|
||||
public function spaceAfterRuleName(): string
|
||||
{
|
||||
return $this->space('AfterRuleName');
|
||||
}
|
||||
|
||||
public function spaceBeforeRules(): string
|
||||
{
|
||||
return $this->space('BeforeRules');
|
||||
}
|
||||
|
||||
public function spaceAfterRules(): string
|
||||
{
|
||||
return $this->space('AfterRules');
|
||||
}
|
||||
|
||||
public function spaceBetweenRules(): string
|
||||
{
|
||||
return $this->space('BetweenRules');
|
||||
}
|
||||
|
||||
public function spaceBeforeBlocks(): string
|
||||
{
|
||||
return $this->space('BeforeBlocks');
|
||||
}
|
||||
|
||||
public function spaceAfterBlocks(): string
|
||||
{
|
||||
return $this->space('AfterBlocks');
|
||||
}
|
||||
|
||||
public function spaceBetweenBlocks(): string
|
||||
{
|
||||
return $this->space('BetweenBlocks');
|
||||
}
|
||||
|
||||
public function spaceBeforeSelectorSeparator(): string
|
||||
{
|
||||
return $this->space('BeforeSelectorSeparator');
|
||||
}
|
||||
|
||||
public function spaceAfterSelectorSeparator(): string
|
||||
{
|
||||
return $this->space('AfterSelectorSeparator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $separator
|
||||
*/
|
||||
public function spaceBeforeListArgumentSeparator(string $separator): string
|
||||
{
|
||||
$spaceForSeparator = $this->outputFormat->getSpaceBeforeListArgumentSeparators();
|
||||
|
||||
return $spaceForSeparator[$separator] ?? $this->space('BeforeListArgumentSeparator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $separator
|
||||
*/
|
||||
public function spaceAfterListArgumentSeparator(string $separator): string
|
||||
{
|
||||
$spaceForSeparator = $this->outputFormat->getSpaceAfterListArgumentSeparators();
|
||||
|
||||
return $spaceForSeparator[$separator] ?? $this->space('AfterListArgumentSeparator');
|
||||
}
|
||||
|
||||
public function spaceBeforeOpeningBrace(): string
|
||||
{
|
||||
return $this->space('BeforeOpeningBrace');
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given code, either swallowing or passing exceptions, depending on the `ignoreExceptions` setting.
|
||||
*/
|
||||
public function safely(callable $callable): ?string
|
||||
{
|
||||
if ($this->outputFormat->shouldIgnoreExceptions()) {
|
||||
// If output exceptions are ignored, run the code with exception guards
|
||||
try {
|
||||
return $callable();
|
||||
} catch (OutputException $e) {
|
||||
return null;
|
||||
} // Do nothing
|
||||
} else {
|
||||
// Run the code as-is
|
||||
return $callable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone of the `implode` function, but calls `render` with the current output format.
|
||||
*
|
||||
* @param array<array-key, Renderable|string> $values
|
||||
*/
|
||||
public function implode(string $separator, array $values, bool $increaseLevel = false): string
|
||||
{
|
||||
$result = '';
|
||||
$outputFormat = $this->outputFormat;
|
||||
if ($increaseLevel) {
|
||||
$outputFormat = $outputFormat->nextLevel();
|
||||
}
|
||||
$isFirst = true;
|
||||
foreach ($values as $value) {
|
||||
if ($isFirst) {
|
||||
$isFirst = false;
|
||||
} else {
|
||||
$result .= $separator;
|
||||
}
|
||||
if ($value instanceof Renderable) {
|
||||
$result .= $value->render($outputFormat);
|
||||
} else {
|
||||
$result .= $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function removeLastSemicolon(string $string): string
|
||||
{
|
||||
if ($this->outputFormat->shouldRenderSemicolonAfterLastRule()) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$parts = \explode(';', $string);
|
||||
if (\count($parts) < 2) {
|
||||
return $parts[0];
|
||||
}
|
||||
$lastPart = \array_pop($parts);
|
||||
$nextToLastPart = \array_pop($parts);
|
||||
\array_push($parts, $nextToLastPart . $lastPart);
|
||||
|
||||
return \implode(';', $parts);
|
||||
}
|
||||
|
||||
public function comments(Commentable $commentable): string
|
||||
{
|
||||
if (!$this->outputFormat->shouldRenderComments()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = '';
|
||||
$comments = $commentable->getComments();
|
||||
$lastCommentIndex = \count($comments) - 1;
|
||||
|
||||
foreach ($comments as $i => $comment) {
|
||||
$result .= $comment->render($this->outputFormat);
|
||||
$result .= $i === $lastCommentIndex ? $this->spaceAfterBlocks() : $this->spaceBetweenBlocks();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function prepareSpace(string $spaceString): string
|
||||
{
|
||||
return \str_replace("\n", "\n" . $this->indent(), $spaceString);
|
||||
}
|
||||
|
||||
private function indent(): string
|
||||
{
|
||||
return \str_repeat($this->outputFormat->getIndentation(), $this->outputFormat->getIndentationLevel());
|
||||
}
|
||||
}
|
||||
42
vendor/sabberworm/php-css-parser/src/Parser.php
vendored
Normal file
42
vendor/sabberworm/php-css-parser/src/Parser.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS;
|
||||
|
||||
use Sabberworm\CSS\CSSList\Document;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
|
||||
/**
|
||||
* This class parses CSS from text into a data structure.
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
/**
|
||||
* @var ParserState
|
||||
*/
|
||||
private $parserState;
|
||||
|
||||
/**
|
||||
* @param string $text the complete CSS as text (i.e., usually the contents of a CSS file)
|
||||
* @param int<1, max> $lineNumber the line number (starting from 1, not from 0)
|
||||
*/
|
||||
public function __construct(string $text, ?Settings $parserSettings = null, int $lineNumber = 1)
|
||||
{
|
||||
if ($parserSettings === null) {
|
||||
$parserSettings = Settings::create();
|
||||
}
|
||||
$this->parserState = new ParserState($text, $parserSettings, $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the CSS provided to the constructor and creates a `Document` from it.
|
||||
*
|
||||
* @throws SourceException
|
||||
*/
|
||||
public function parse(): Document
|
||||
{
|
||||
return Document::parse($this->parserState);
|
||||
}
|
||||
}
|
||||
35
vendor/sabberworm/php-css-parser/src/Parsing/Anchor.php
vendored
Normal file
35
vendor/sabberworm/php-css-parser/src/Parsing/Anchor.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Parsing;
|
||||
|
||||
/**
|
||||
* @internal since 8.7.0
|
||||
*/
|
||||
class Anchor
|
||||
{
|
||||
/**
|
||||
* @var int<0, max>
|
||||
*/
|
||||
private $position;
|
||||
|
||||
/**
|
||||
* @var ParserState
|
||||
*/
|
||||
private $parserState;
|
||||
|
||||
/**
|
||||
* @param int<0, max> $position
|
||||
*/
|
||||
public function __construct(int $position, ParserState $parserState)
|
||||
{
|
||||
$this->position = $position;
|
||||
$this->parserState = $parserState;
|
||||
}
|
||||
|
||||
public function backtrack(): void
|
||||
{
|
||||
$this->parserState->setPosition($this->position);
|
||||
}
|
||||
}
|
||||
10
vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php
vendored
Normal file
10
vendor/sabberworm/php-css-parser/src/Parsing/OutputException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Parsing;
|
||||
|
||||
/**
|
||||
* Thrown if the CSS parser attempts to print something invalid.
|
||||
*/
|
||||
final class OutputException extends SourceException {}
|
||||
502
vendor/sabberworm/php-css-parser/src/Parsing/ParserState.php
vendored
Normal file
502
vendor/sabberworm/php-css-parser/src/Parsing/ParserState.php
vendored
Normal file
@@ -0,0 +1,502 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Parsing;
|
||||
|
||||
use Sabberworm\CSS\Comment\Comment;
|
||||
use Sabberworm\CSS\Settings;
|
||||
|
||||
use function Safe\iconv;
|
||||
use function Safe\preg_match;
|
||||
use function Safe\preg_split;
|
||||
|
||||
/**
|
||||
* @internal since 8.7.0
|
||||
*/
|
||||
class ParserState
|
||||
{
|
||||
public const EOF = null;
|
||||
|
||||
/**
|
||||
* @var Settings
|
||||
*/
|
||||
private $parserSettings;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $text;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private $characters;
|
||||
|
||||
/**
|
||||
* @var int<0, max>
|
||||
*/
|
||||
private $currentPosition = 0;
|
||||
|
||||
/**
|
||||
* will only be used if the CSS does not contain an `@charset` declaration
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $charset;
|
||||
|
||||
/**
|
||||
* @var int<1, max> $lineNumber
|
||||
*/
|
||||
private $lineNumber;
|
||||
|
||||
/**
|
||||
* @param string $text the complete CSS as text (i.e., usually the contents of a CSS file)
|
||||
* @param int<1, max> $lineNumber
|
||||
*/
|
||||
public function __construct(string $text, Settings $parserSettings, int $lineNumber = 1)
|
||||
{
|
||||
$this->parserSettings = $parserSettings;
|
||||
$this->text = $text;
|
||||
$this->lineNumber = $lineNumber;
|
||||
$this->setCharset($this->parserSettings->getDefaultCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the charset to be used if the CSS does not contain an `@charset` declaration.
|
||||
*/
|
||||
public function setCharset(string $charset): void
|
||||
{
|
||||
$this->charset = $charset;
|
||||
$this->characters = $this->strsplit($this->text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<1, max>
|
||||
*/
|
||||
public function currentLine(): int
|
||||
{
|
||||
return $this->lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function currentColumn(): int
|
||||
{
|
||||
return $this->currentPosition;
|
||||
}
|
||||
|
||||
public function getSettings(): Settings
|
||||
{
|
||||
return $this->parserSettings;
|
||||
}
|
||||
|
||||
public function anchor(): Anchor
|
||||
{
|
||||
return new Anchor($this->currentPosition, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<0, max> $position
|
||||
*/
|
||||
public function setPosition(int $position): void
|
||||
{
|
||||
$this->currentPosition = $position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function parseIdentifier(bool $ignoreCase = true): string
|
||||
{
|
||||
if ($this->isEnd()) {
|
||||
throw new UnexpectedEOFException('', '', 'identifier', $this->lineNumber);
|
||||
}
|
||||
$result = $this->parseCharacter(true);
|
||||
if ($result === null) {
|
||||
throw new UnexpectedTokenException('', $this->peek(5), 'identifier', $this->lineNumber);
|
||||
}
|
||||
$character = null;
|
||||
while (!$this->isEnd() && ($character = $this->parseCharacter(true)) !== null) {
|
||||
if (preg_match('/[a-zA-Z0-9\\x{00A0}-\\x{FFFF}_-]/Sux', $character) !== 0) {
|
||||
$result .= $character;
|
||||
} else {
|
||||
$result .= '\\' . $character;
|
||||
}
|
||||
}
|
||||
if ($ignoreCase) {
|
||||
$result = $this->strtolower($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function parseCharacter(bool $isForIdentifier): ?string
|
||||
{
|
||||
if ($this->peek() === '\\') {
|
||||
$this->consume('\\');
|
||||
if ($this->comes('\\n') || $this->comes('\\r')) {
|
||||
return '';
|
||||
}
|
||||
if (preg_match('/[0-9a-fA-F]/Su', $this->peek()) === 0) {
|
||||
return $this->consume(1);
|
||||
}
|
||||
$hexCodePoint = $this->consumeExpression('/^[0-9a-fA-F]{1,6}/u', 6);
|
||||
if ($this->strlen($hexCodePoint) < 6) {
|
||||
// Consume whitespace after incomplete unicode escape
|
||||
if (preg_match('/\\s/isSu', $this->peek()) !== 0) {
|
||||
if ($this->comes('\\r\\n')) {
|
||||
$this->consume(2);
|
||||
} else {
|
||||
$this->consume(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
$codePoint = \intval($hexCodePoint, 16);
|
||||
$utf32EncodedCharacter = '';
|
||||
for ($i = 0; $i < 4; ++$i) {
|
||||
$utf32EncodedCharacter .= \chr($codePoint & 0xff);
|
||||
$codePoint = $codePoint >> 8;
|
||||
}
|
||||
return iconv('utf-32le', $this->charset, $utf32EncodedCharacter);
|
||||
}
|
||||
if ($isForIdentifier) {
|
||||
$peek = \ord($this->peek());
|
||||
// Ranges: a-z A-Z 0-9 - _
|
||||
if (
|
||||
($peek >= 97 && $peek <= 122)
|
||||
|| ($peek >= 65 && $peek <= 90)
|
||||
|| ($peek >= 48 && $peek <= 57)
|
||||
|| ($peek === 45)
|
||||
|| ($peek === 95)
|
||||
|| ($peek > 0xa1)
|
||||
) {
|
||||
return $this->consume(1);
|
||||
}
|
||||
} else {
|
||||
return $this->consume(1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes whitespace and/or comments until the next non-whitespace character that isn't a slash opening a comment.
|
||||
*
|
||||
* @param list<Comment> $comments Any comments consumed will be appended to this array.
|
||||
*
|
||||
* @return string the whitespace consumed, without the comments
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @phpstan-impure
|
||||
* This method may change the state of the object by advancing the internal position;
|
||||
* it does not simply 'get' a value.
|
||||
*/
|
||||
public function consumeWhiteSpace(array &$comments = []): string
|
||||
{
|
||||
$consumed = '';
|
||||
do {
|
||||
while (preg_match('/\\s/isSu', $this->peek()) === 1) {
|
||||
$consumed .= $this->consume(1);
|
||||
}
|
||||
if ($this->parserSettings->usesLenientParsing()) {
|
||||
try {
|
||||
$comment = $this->consumeComment();
|
||||
} catch (UnexpectedEOFException $e) {
|
||||
$this->currentPosition = \count($this->characters);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$comment = $this->consumeComment();
|
||||
}
|
||||
if ($comment instanceof Comment) {
|
||||
$comments[] = $comment;
|
||||
}
|
||||
} while ($comment instanceof Comment);
|
||||
|
||||
return $consumed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $string
|
||||
*/
|
||||
public function comes(string $string, bool $caseInsensitive = false): bool
|
||||
{
|
||||
$peek = $this->peek(\strlen($string));
|
||||
|
||||
return ($peek !== '') && $this->streql($peek, $string, $caseInsensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<1, max> $length
|
||||
* @param int<0, max> $offset
|
||||
*/
|
||||
public function peek(int $length = 1, int $offset = 0): string
|
||||
{
|
||||
$offset += $this->currentPosition;
|
||||
if ($offset >= \count($this->characters)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->substr($offset, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int<1, max> $value
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function consume($value = 1): string
|
||||
{
|
||||
if (\is_string($value)) {
|
||||
$numberOfLines = \substr_count($value, "\n");
|
||||
$length = $this->strlen($value);
|
||||
if (!$this->streql($this->substr($this->currentPosition, $length), $value)) {
|
||||
throw new UnexpectedTokenException(
|
||||
$value,
|
||||
$this->peek(\max($length, 5)),
|
||||
'literal',
|
||||
$this->lineNumber
|
||||
);
|
||||
}
|
||||
|
||||
$this->lineNumber += $numberOfLines;
|
||||
$this->currentPosition += $this->strlen($value);
|
||||
$result = $value;
|
||||
} else {
|
||||
if ($this->currentPosition + $value > \count($this->characters)) {
|
||||
throw new UnexpectedEOFException((string) $value, $this->peek(5), 'count', $this->lineNumber);
|
||||
}
|
||||
|
||||
$result = $this->substr($this->currentPosition, $value);
|
||||
$numberOfLines = \substr_count($result, "\n");
|
||||
$this->lineNumber += $numberOfLines;
|
||||
$this->currentPosition += $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the possibly-expected next content is next, consume it.
|
||||
*
|
||||
* @param non-empty-string $nextContent
|
||||
*
|
||||
* @return bool whether the possibly-expected content was found and consumed
|
||||
*/
|
||||
public function consumeIfComes(string $nextContent): bool
|
||||
{
|
||||
$length = $this->strlen($nextContent);
|
||||
if (!$this->streql($this->substr($this->currentPosition, $length), $nextContent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$numberOfLines = \substr_count($nextContent, "\n");
|
||||
$this->lineNumber += $numberOfLines;
|
||||
$this->currentPosition += $this->strlen($nextContent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $expression
|
||||
* @param int<1, max>|null $maximumLength
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function consumeExpression(string $expression, ?int $maximumLength = null): string
|
||||
{
|
||||
$matches = null;
|
||||
$input = ($maximumLength !== null) ? $this->peek($maximumLength) : $this->inputLeft();
|
||||
if (preg_match($expression, $input, $matches, PREG_OFFSET_CAPTURE) !== 1) {
|
||||
throw new UnexpectedTokenException($expression, $this->peek(5), 'expression', $this->lineNumber);
|
||||
}
|
||||
|
||||
return $this->consume($matches[0][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Comment|false
|
||||
*/
|
||||
public function consumeComment()
|
||||
{
|
||||
$lineNumber = $this->lineNumber;
|
||||
$comment = null;
|
||||
|
||||
if ($this->comes('/*')) {
|
||||
$this->consume(1);
|
||||
$comment = '';
|
||||
while (($char = $this->consume(1)) !== '') {
|
||||
$comment .= $char;
|
||||
if ($this->comes('*/')) {
|
||||
$this->consume(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We skip the * which was included in the comment.
|
||||
return \is_string($comment) ? new Comment(\substr($comment, 1), $lineNumber) : false;
|
||||
}
|
||||
|
||||
public function isEnd(): bool
|
||||
{
|
||||
return $this->currentPosition >= \count($this->characters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string|self::EOF>|string|self::EOF $stopCharacters
|
||||
* @param list<Comment> $comments
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function consumeUntil(
|
||||
$stopCharacters,
|
||||
bool $includeEnd = false,
|
||||
bool $consumeEnd = false,
|
||||
array &$comments = []
|
||||
): string {
|
||||
$stopCharacters = \is_array($stopCharacters) ? $stopCharacters : [$stopCharacters];
|
||||
$consumedCharacters = '';
|
||||
$start = $this->currentPosition;
|
||||
|
||||
$comments = \array_merge($comments, $this->consumeComments());
|
||||
while (!$this->isEnd()) {
|
||||
$character = $this->consume(1);
|
||||
if (\in_array($character, $stopCharacters, true)) {
|
||||
if ($includeEnd) {
|
||||
$consumedCharacters .= $character;
|
||||
} elseif (!$consumeEnd) {
|
||||
$this->currentPosition -= $this->strlen($character);
|
||||
}
|
||||
return $consumedCharacters;
|
||||
}
|
||||
$consumedCharacters .= $character;
|
||||
$comments = \array_merge($comments, $this->consumeComments());
|
||||
}
|
||||
|
||||
if (\in_array(self::EOF, $stopCharacters, true)) {
|
||||
return $consumedCharacters;
|
||||
}
|
||||
|
||||
$this->currentPosition = $start;
|
||||
throw new UnexpectedEOFException(
|
||||
'One of ("' . \implode('","', $stopCharacters) . '")',
|
||||
$this->peek(5),
|
||||
'search',
|
||||
$this->lineNumber
|
||||
);
|
||||
}
|
||||
|
||||
private function inputLeft(): string
|
||||
{
|
||||
return $this->substr($this->currentPosition, -1);
|
||||
}
|
||||
|
||||
public function streql(string $string1, string $string2, bool $caseInsensitive = true): bool
|
||||
{
|
||||
return $caseInsensitive
|
||||
? ($this->strtolower($string1) === $this->strtolower($string2))
|
||||
: ($string1 === $string2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<1, max> $numberOfCharacters
|
||||
*/
|
||||
public function backtrack(int $numberOfCharacters): void
|
||||
{
|
||||
$this->currentPosition -= $numberOfCharacters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function strlen(string $string): int
|
||||
{
|
||||
return $this->parserSettings->hasMultibyteSupport()
|
||||
? \mb_strlen($string, $this->charset)
|
||||
: \strlen($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<0, max> $offset
|
||||
*/
|
||||
private function substr(int $offset, int $length): string
|
||||
{
|
||||
if ($length < 0) {
|
||||
$length = \count($this->characters) - $offset + $length;
|
||||
}
|
||||
if ($offset + $length > \count($this->characters)) {
|
||||
$length = \count($this->characters) - $offset;
|
||||
}
|
||||
$result = '';
|
||||
while ($length > 0) {
|
||||
$result .= $this->characters[$offset];
|
||||
$offset++;
|
||||
$length--;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ($string is non-empty-string ? non-empty-string : string)
|
||||
*/
|
||||
private function strtolower(string $string): string
|
||||
{
|
||||
return $this->parserSettings->hasMultibyteSupport()
|
||||
? \mb_strtolower($string, $this->charset)
|
||||
: \strtolower($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private function strsplit(string $string): array
|
||||
{
|
||||
if ($this->parserSettings->hasMultibyteSupport()) {
|
||||
if ($this->streql($this->charset, 'utf-8')) {
|
||||
$result = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
|
||||
} else {
|
||||
$length = \mb_strlen($string, $this->charset);
|
||||
$result = [];
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$result[] = \mb_substr($string, $i, 1, $this->charset);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = ($string !== '') ? \str_split($string) : [];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Comment>
|
||||
*/
|
||||
private function consumeComments(): array
|
||||
{
|
||||
$comments = [];
|
||||
|
||||
while (true) {
|
||||
$comment = $this->consumeComment();
|
||||
if ($comment instanceof Comment) {
|
||||
$comments[] = $comment;
|
||||
} else {
|
||||
return $comments;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php
vendored
Normal file
25
vendor/sabberworm/php-css-parser/src/Parsing/SourceException.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Parsing;
|
||||
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
|
||||
class SourceException extends \Exception implements Positionable
|
||||
{
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $message, ?int $lineNumber = null)
|
||||
{
|
||||
$this->setPosition($lineNumber);
|
||||
if ($lineNumber !== null) {
|
||||
$message .= " [line no: $lineNumber]";
|
||||
}
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
12
vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php
vendored
Normal file
12
vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedEOFException.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Parsing;
|
||||
|
||||
/**
|
||||
* Thrown if the CSS parser encounters end of file it did not expect.
|
||||
*
|
||||
* Extends `UnexpectedTokenException` in order to preserve backwards compatibility.
|
||||
*/
|
||||
final class UnexpectedEOFException extends UnexpectedTokenException {}
|
||||
31
vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedTokenException.php
vendored
Normal file
31
vendor/sabberworm/php-css-parser/src/Parsing/UnexpectedTokenException.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Parsing;
|
||||
|
||||
/**
|
||||
* Thrown if the CSS parser encounters a token it did not expect.
|
||||
*/
|
||||
class UnexpectedTokenException extends SourceException
|
||||
{
|
||||
/**
|
||||
* @param 'literal'|'identifier'|'count'|'expression'|'search'|'custom' $matchType
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $expected, string $found, string $matchType = 'literal', ?int $lineNumber = null)
|
||||
{
|
||||
$message = "Token “{$expected}” ({$matchType}) not found. Got “{$found}”.";
|
||||
if ($matchType === 'search') {
|
||||
$message = "Search for “{$expected}” returned no results. Context: “{$found}”.";
|
||||
} elseif ($matchType === 'count') {
|
||||
$message = "Next token was expected to have {$expected} chars. Context: “{$found}”.";
|
||||
} elseif ($matchType === 'identifier') {
|
||||
$message = "Identifier expected. Got “{$found}”";
|
||||
} elseif ($matchType === 'custom') {
|
||||
$message = \trim("$expected $found");
|
||||
}
|
||||
|
||||
parent::__construct($message, $lineNumber);
|
||||
}
|
||||
}
|
||||
55
vendor/sabberworm/php-css-parser/src/Position/Position.php
vendored
Normal file
55
vendor/sabberworm/php-css-parser/src/Position/Position.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Position;
|
||||
|
||||
/**
|
||||
* Provides a standard reusable implementation of `Positionable`.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @phpstan-require-implements Positionable
|
||||
*/
|
||||
trait Position
|
||||
{
|
||||
/**
|
||||
* @var int<1, max>|null
|
||||
*/
|
||||
protected $lineNumber;
|
||||
|
||||
/**
|
||||
* @var int<0, max>|null
|
||||
*/
|
||||
protected $columnNumber;
|
||||
|
||||
/**
|
||||
* @return int<1, max>|null
|
||||
*/
|
||||
public function getLineNumber(): ?int
|
||||
{
|
||||
return $this->lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getColumnNumber(): ?int
|
||||
{
|
||||
return $this->columnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
* @param int<0, max>|null $columnNumber
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setPosition(?int $lineNumber, ?int $columnNumber = null): Positionable
|
||||
{
|
||||
$this->lineNumber = $lineNumber;
|
||||
$this->columnNumber = $columnNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
31
vendor/sabberworm/php-css-parser/src/Position/Positionable.php
vendored
Normal file
31
vendor/sabberworm/php-css-parser/src/Position/Positionable.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Position;
|
||||
|
||||
/**
|
||||
* Represents a CSS item that may have a position in the source CSS document (line number and possibly column number).
|
||||
*
|
||||
* A standard implementation of this interface is available in the `Position` trait.
|
||||
*/
|
||||
interface Positionable
|
||||
{
|
||||
/**
|
||||
* @return int<1, max>|null
|
||||
*/
|
||||
public function getLineNumber(): ?int;
|
||||
|
||||
/**
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getColumnNumber(): ?int;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
* @param int<0, max>|null $columnNumber
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function setPosition(?int $lineNumber, ?int $columnNumber = null): Positionable;
|
||||
}
|
||||
27
vendor/sabberworm/php-css-parser/src/Property/AtRule.php
vendored
Normal file
27
vendor/sabberworm/php-css-parser/src/Property/AtRule.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property;
|
||||
|
||||
use Sabberworm\CSS\CSSList\CSSListItem;
|
||||
|
||||
/**
|
||||
* Note that `CSSListItem` extends both `Commentable` and `Renderable`,
|
||||
* so concrete classes implementing this interface must also implement those.
|
||||
*/
|
||||
interface AtRule extends CSSListItem
|
||||
{
|
||||
/**
|
||||
* Since there are more set rules than block rules,
|
||||
* we’re whitelisting the block rules and have anything else be treated as a set rule.
|
||||
*
|
||||
* @internal since 8.5.2
|
||||
*/
|
||||
public const BLOCK_RULES = 'media/document/supports/region-style/font-feature-values/container';
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleName(): string;
|
||||
}
|
||||
107
vendor/sabberworm/php-css-parser/src/Property/CSSNamespace.php
vendored
Normal file
107
vendor/sabberworm/php-css-parser/src/Property/CSSNamespace.php
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property;
|
||||
|
||||
use Sabberworm\CSS\Comment\CommentContainer;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\Value\CSSString;
|
||||
use Sabberworm\CSS\Value\URL;
|
||||
|
||||
/**
|
||||
* `CSSNamespace` represents an `@namespace` rule.
|
||||
*/
|
||||
class CSSNamespace implements AtRule, Positionable
|
||||
{
|
||||
use CommentContainer;
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* @var CSSString|URL
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $prefix;
|
||||
|
||||
/**
|
||||
* @param CSSString|URL $url
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct($url, ?string $prefix = null, ?int $lineNumber = null)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->prefix = $prefix;
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return '@namespace ' . ($this->prefix === null ? '' : $this->prefix . ' ')
|
||||
. $this->url->render($outputFormat) . ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSSString|URL
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function getPrefix(): ?string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CSSString|URL $url
|
||||
*/
|
||||
public function setUrl($url): void
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function setPrefix(string $prefix): void
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleName(): string
|
||||
{
|
||||
return 'namespace';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: CSSString|URL|non-empty-string, 1?: CSSString|URL}
|
||||
*/
|
||||
public function atRuleArgs(): array
|
||||
{
|
||||
$result = [$this->url];
|
||||
if (\is_string($this->prefix) && $this->prefix !== '') {
|
||||
\array_unshift($result, $this->prefix);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
}
|
||||
89
vendor/sabberworm/php-css-parser/src/Property/Charset.php
vendored
Normal file
89
vendor/sabberworm/php-css-parser/src/Property/Charset.php
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property;
|
||||
|
||||
use Sabberworm\CSS\Comment\CommentContainer;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
use Sabberworm\CSS\Value\CSSString;
|
||||
|
||||
/**
|
||||
* Class representing an `@charset` rule.
|
||||
*
|
||||
* The following restrictions apply:
|
||||
* - May not be found in any CSSList other than the Document.
|
||||
* - May only appear at the very top of a Document’s contents.
|
||||
* - Must not appear more than once.
|
||||
*/
|
||||
class Charset implements AtRule, Positionable
|
||||
{
|
||||
use CommentContainer;
|
||||
use Position;
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @var CSSString
|
||||
*/
|
||||
private $charset;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(CSSString $charset, ?int $lineNumber = null)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|CSSString $charset
|
||||
*/
|
||||
public function setCharset($charset): void
|
||||
{
|
||||
$charset = $charset instanceof CSSString ? $charset : new CSSString($charset);
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
public function getCharset(): string
|
||||
{
|
||||
return $this->charset->getString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return "{$outputFormat->getFormatter()->comments($this)}@charset {$this->charset->render($outputFormat)};";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleName(): string
|
||||
{
|
||||
return 'charset';
|
||||
}
|
||||
|
||||
public function atRuleArgs(): CSSString
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
'charset' => $this->charset->getArrayRepresentation(),
|
||||
];
|
||||
}
|
||||
}
|
||||
231
vendor/sabberworm/php-css-parser/src/Property/Declaration.php
vendored
Normal file
231
vendor/sabberworm/php-css-parser/src/Property/Declaration.php
vendored
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property;
|
||||
|
||||
use Sabberworm\CSS\Comment\Comment;
|
||||
use Sabberworm\CSS\Comment\Commentable;
|
||||
use Sabberworm\CSS\Comment\CommentContainer;
|
||||
use Sabberworm\CSS\CSSElement;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\Value\RuleValueList;
|
||||
use Sabberworm\CSS\Value\Value;
|
||||
|
||||
use function Safe\preg_match;
|
||||
|
||||
/**
|
||||
* `Declaration`s just have a string key (the property name) and a 'Value'.
|
||||
*
|
||||
* In CSS, `Declaration`s are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];”
|
||||
*/
|
||||
class Declaration implements Commentable, CSSElement, Positionable
|
||||
{
|
||||
use CommentContainer;
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private $propertyName;
|
||||
|
||||
/**
|
||||
* @var RuleValueList|string|null
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isImportant = false;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $propertyName
|
||||
* @param int<1, max>|null $lineNumber
|
||||
* @param int<0, max>|null $columnNumber
|
||||
*/
|
||||
public function __construct(string $propertyName, ?int $lineNumber = null, ?int $columnNumber = null)
|
||||
{
|
||||
$this->propertyName = $propertyName;
|
||||
$this->setPosition($lineNumber, $columnNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Comment> $commentsBefore
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, array $commentsBefore = []): self
|
||||
{
|
||||
$comments = $commentsBefore;
|
||||
$parserState->consumeWhiteSpace($comments);
|
||||
$declaration = new self(
|
||||
$parserState->parseIdentifier(!$parserState->comes('--')),
|
||||
$parserState->currentLine(),
|
||||
$parserState->currentColumn()
|
||||
);
|
||||
$parserState->consumeWhiteSpace($comments);
|
||||
$declaration->setComments($comments);
|
||||
$parserState->consume(':');
|
||||
$value = Value::parseValue($parserState, self::getDelimitersForPropertyValue($declaration->getPropertyName()));
|
||||
$declaration->setValue($value);
|
||||
$parserState->consumeWhiteSpace();
|
||||
if ($parserState->comes('!')) {
|
||||
$parserState->consume('!');
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consume('important');
|
||||
$declaration->setIsImportant(true);
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
while ($parserState->comes(';')) {
|
||||
$parserState->consume(';');
|
||||
}
|
||||
|
||||
return $declaration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of delimiters (or separators).
|
||||
* The first item is the innermost separator (or, put another way, the highest-precedence operator).
|
||||
* The sequence continues to the outermost separator (or lowest-precedence operator).
|
||||
*
|
||||
* @param non-empty-string $propertyName
|
||||
*
|
||||
* @return list<non-empty-string>
|
||||
*/
|
||||
private static function getDelimitersForPropertyValue(string $propertyName): array
|
||||
{
|
||||
if (preg_match('/^font($|-)/', $propertyName) === 1) {
|
||||
return [',', '/', ' '];
|
||||
}
|
||||
|
||||
switch ($propertyName) {
|
||||
case 'src':
|
||||
return [' ', ','];
|
||||
default:
|
||||
return [',', ' ', '/'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $propertyName
|
||||
*/
|
||||
public function setPropertyName(string $propertyName): void
|
||||
{
|
||||
$this->propertyName = $propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getPropertyName(): string
|
||||
{
|
||||
return $this->propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $propertyName
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `setPropertyName()` instead.
|
||||
*/
|
||||
public function setRule(string $propertyName): void
|
||||
{
|
||||
$this->propertyName = $propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `getPropertyName()` instead.
|
||||
*/
|
||||
public function getRule(): string
|
||||
{
|
||||
return $this->propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RuleValueList|string|null
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RuleValueList|string|null $value
|
||||
*/
|
||||
public function setValue($value): void
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to the existing value. Value will be appended if a `RuleValueList` exists of the given type.
|
||||
* Otherwise, the existing value will be wrapped by one.
|
||||
*
|
||||
* @param RuleValueList|array<int, RuleValueList> $value
|
||||
*/
|
||||
public function addValue($value, string $type = ' '): void
|
||||
{
|
||||
if (!\is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
if (!($this->value instanceof RuleValueList) || $this->value->getListSeparator() !== $type) {
|
||||
$currentValue = $this->value;
|
||||
$this->value = new RuleValueList($type, $this->getLineNumber());
|
||||
if ($currentValue !== null && $currentValue !== '') {
|
||||
$this->value->addListComponent($currentValue);
|
||||
}
|
||||
}
|
||||
foreach ($value as $valueItem) {
|
||||
$this->value->addListComponent($valueItem);
|
||||
}
|
||||
}
|
||||
|
||||
public function setIsImportant(bool $isImportant): void
|
||||
{
|
||||
$this->isImportant = $isImportant;
|
||||
}
|
||||
|
||||
public function getIsImportant(): bool
|
||||
{
|
||||
return $this->isImportant;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
$result = "{$formatter->comments($this)}{$this->propertyName}:{$formatter->spaceAfterRuleName()}";
|
||||
if ($this->value instanceof Value) { // Can also be a ValueList
|
||||
$result .= $this->value->render($outputFormat);
|
||||
} else {
|
||||
$result .= $this->value;
|
||||
}
|
||||
if ($this->isImportant) {
|
||||
$result .= ' !important';
|
||||
}
|
||||
$result .= ';';
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
}
|
||||
95
vendor/sabberworm/php-css-parser/src/Property/Import.php
vendored
Normal file
95
vendor/sabberworm/php-css-parser/src/Property/Import.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property;
|
||||
|
||||
use Sabberworm\CSS\Comment\CommentContainer;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\Value\URL;
|
||||
|
||||
/**
|
||||
* Class representing an `@import` rule.
|
||||
*/
|
||||
class Import implements AtRule, Positionable
|
||||
{
|
||||
use CommentContainer;
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* @var URL
|
||||
*/
|
||||
private $location;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $mediaQuery;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(URL $location, ?string $mediaQuery, ?int $lineNumber = null)
|
||||
{
|
||||
$this->location = $location;
|
||||
$this->mediaQuery = $mediaQuery;
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
public function setLocation(URL $location): void
|
||||
{
|
||||
$this->location = $location;
|
||||
}
|
||||
|
||||
public function getLocation(): URL
|
||||
{
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return $outputFormat->getFormatter()->comments($this) . '@import ' . $this->location->render($outputFormat)
|
||||
. ($this->mediaQuery === null ? '' : ' ' . $this->mediaQuery) . ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleName(): string
|
||||
{
|
||||
return 'import';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: URL, 1?: non-empty-string}
|
||||
*/
|
||||
public function atRuleArgs(): array
|
||||
{
|
||||
$result = [$this->location];
|
||||
if (\is_string($this->mediaQuery) && $this->mediaQuery !== '') {
|
||||
$result[] = $this->mediaQuery;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getMediaQuery(): ?string
|
||||
{
|
||||
return $this->mediaQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
}
|
||||
45
vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php
vendored
Normal file
45
vendor/sabberworm/php-css-parser/src/Property/KeyframeSelector.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property;
|
||||
|
||||
class KeyframeSelector extends Selector
|
||||
{
|
||||
/**
|
||||
* This differs from the parent class:
|
||||
* - comma is not allowed unless escaped or quoted;
|
||||
* - percentage value is allowed by itself.
|
||||
*
|
||||
* @internal since 8.5.2
|
||||
*/
|
||||
public const SELECTOR_VALIDATION_RX = '/
|
||||
^(
|
||||
(?:
|
||||
# any sequence of valid unescaped characters, except quotes
|
||||
[a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*=~\\[\\]()\\-\\s\\.:#+>]++
|
||||
|
|
||||
# one or more escaped characters
|
||||
(?:\\\\.)++
|
||||
|
|
||||
# quoted text, like in `[id="example"]`
|
||||
(?:
|
||||
# opening quote
|
||||
([\'"])
|
||||
(?:
|
||||
# sequence of characters except closing quote or backslash
|
||||
(?:(?!\\g{-1}|\\\\).)++
|
||||
|
|
||||
# one or more escaped characters
|
||||
(?:\\\\.)++
|
||||
)*+ # zero or more times
|
||||
# closing quote or end (unmatched quote is currently allowed)
|
||||
(?:\\g{-1}|$)
|
||||
)
|
||||
)*+ # zero or more times
|
||||
|
|
||||
# keyframe animation progress percentage (e.g. 50%), untrimmed
|
||||
\\s*+(\\d++%)\\s*+
|
||||
)$
|
||||
/ux';
|
||||
}
|
||||
249
vendor/sabberworm/php-css-parser/src/Property/Selector.php
vendored
Normal file
249
vendor/sabberworm/php-css-parser/src/Property/Selector.php
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property;
|
||||
|
||||
use Sabberworm\CSS\Comment\Comment;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\Property\Selector\Combinator;
|
||||
use Sabberworm\CSS\Property\Selector\Component;
|
||||
use Sabberworm\CSS\Property\Selector\CompoundSelector;
|
||||
use Sabberworm\CSS\Renderable;
|
||||
use Sabberworm\CSS\Settings;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
use function Safe\preg_match;
|
||||
|
||||
/**
|
||||
* Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this
|
||||
* class.
|
||||
*/
|
||||
class Selector implements Renderable
|
||||
{
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @internal since 8.5.2
|
||||
*/
|
||||
public const SELECTOR_VALIDATION_RX = '/
|
||||
^(
|
||||
# not whitespace only
|
||||
(?!\\s*+$)
|
||||
(?:
|
||||
# any sequence of valid unescaped characters, except quotes
|
||||
[a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*=~\\[\\]()\\-\\s\\.:#+>,]++
|
||||
|
|
||||
# one or more escaped characters
|
||||
(?:\\\\.)++
|
||||
|
|
||||
# quoted text, like in `[id="example"]`
|
||||
(?:
|
||||
# opening quote
|
||||
([\'"])
|
||||
(?:
|
||||
# sequence of characters except closing quote or backslash
|
||||
(?:(?!\\g{-1}|\\\\).)++
|
||||
|
|
||||
# one or more escaped characters
|
||||
(?:\\\\.)++
|
||||
)*+ # zero or more times
|
||||
# closing quote or end (unmatched quote is currently allowed)
|
||||
(?:\\g{-1}|$)
|
||||
)
|
||||
)*+ # zero or more times
|
||||
)$
|
||||
/ux';
|
||||
|
||||
/**
|
||||
* @var non-empty-list<Component>
|
||||
*/
|
||||
private $components;
|
||||
|
||||
/**
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function isValid(string $selector): bool
|
||||
{
|
||||
// Note: We need to use `static::` here as the constant is overridden in the `KeyframeSelector` class.
|
||||
$numberOfMatches = preg_match(static::SELECTOR_VALIDATION_RX, $selector);
|
||||
|
||||
return $numberOfMatches === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string|non-empty-list<Component> $selector
|
||||
* Providing a string is deprecated in version 9.2 and will not work from v10.0
|
||||
*
|
||||
* @throws UnexpectedTokenException if the selector is not valid
|
||||
*/
|
||||
final public function __construct($selector)
|
||||
{
|
||||
if (\is_string($selector)) {
|
||||
$this->setSelector($selector);
|
||||
} else {
|
||||
$this->setComponents($selector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*
|
||||
* @return non-empty-list<Component>
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseComponents(ParserState $parserState, array &$comments = []): array
|
||||
{
|
||||
// Whitespace is a descendent combinator, not allowed around a compound selector.
|
||||
// (It is allowed within, e.g. as part of a string or within a function like `:not()`.)
|
||||
// Gobble any up now to get a clean start.
|
||||
$parserState->consumeWhiteSpace($comments);
|
||||
|
||||
$selectorParts = [];
|
||||
while (true) {
|
||||
try {
|
||||
$selectorParts[] = CompoundSelector::parse($parserState, $comments);
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
if ($selectorParts !== [] && \end($selectorParts)->getValue() === ' ') {
|
||||
// The whitespace was not a descendent combinator, and was, in fact, arbitrary,
|
||||
// after the end of the selector. Discard it.
|
||||
\array_pop($selectorParts);
|
||||
break;
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$selectorParts[] = Combinator::parse($parserState, $comments);
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
// End of selector has been reached.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $selectorParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function parse(ParserState $parserState, array &$comments = []): self
|
||||
{
|
||||
$selectorParts = self::parseComponents($parserState, $comments);
|
||||
|
||||
// Check that the selector has been fully parsed:
|
||||
if (!\in_array($parserState->peek(), ['{', '}', ',', ''], true)) {
|
||||
throw new UnexpectedTokenException(
|
||||
'`,`, `{`, `}` or EOF',
|
||||
$parserState->peek(5),
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
|
||||
return new static($selectorParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-list<Component>
|
||||
*/
|
||||
public function getComponents(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-list<Component> $components
|
||||
* This should be an alternating sequence of `CompoundSelector` and `Combinator`, starting and ending with a
|
||||
* `CompoundSelector`, and may be a single `CompoundSelector`.
|
||||
*/
|
||||
public function setComponents(array $components): self
|
||||
{
|
||||
$this->components = $components;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*
|
||||
* @deprecated in version 9.2, will be removed in v10.0. Use either `getComponents()` or `render()` instead.
|
||||
*/
|
||||
public function getSelector(): string
|
||||
{
|
||||
return $this->render(new OutputFormat());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $selector
|
||||
*
|
||||
* @throws UnexpectedTokenException if the selector is not valid
|
||||
*
|
||||
* @deprecated in version 9.2, will be removed in v10.0. Use `setComponents()` instead.
|
||||
*/
|
||||
public function setSelector(string $selector): void
|
||||
{
|
||||
$parserState = new ParserState($selector, Settings::create());
|
||||
|
||||
$components = self::parseComponents($parserState);
|
||||
|
||||
// Check that the selector has been fully parsed:
|
||||
if (!$parserState->isEnd()) {
|
||||
throw new UnexpectedTokenException(
|
||||
'EOF',
|
||||
$parserState->peek(5),
|
||||
'literal'
|
||||
);
|
||||
}
|
||||
|
||||
$this->components = $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getSpecificity(): int
|
||||
{
|
||||
return \array_sum(\array_map(
|
||||
static function (Component $component): int {
|
||||
return $component->getSpecificity();
|
||||
},
|
||||
$this->components
|
||||
));
|
||||
}
|
||||
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return \implode('', \array_map(
|
||||
static function (Component $component) use ($outputFormat): string {
|
||||
return $component->render($outputFormat);
|
||||
},
|
||||
$this->components
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
'components' => \array_map(
|
||||
static function (Component $component): array {
|
||||
return $component->getArrayRepresentation();
|
||||
},
|
||||
$this->components
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
119
vendor/sabberworm/php-css-parser/src/Property/Selector/Combinator.php
vendored
Normal file
119
vendor/sabberworm/php-css-parser/src/Property/Selector/Combinator.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property\Selector;
|
||||
|
||||
use Sabberworm\CSS\Comment\Comment;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* Class representing a CSS selector combinator (space, `>`, `+`, or `~`).
|
||||
*
|
||||
* @phpstan-type ValidCombinatorValue ' '|'>'|'+'|'~'
|
||||
*/
|
||||
class Combinator implements Component
|
||||
{
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @var ValidCombinatorValue
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param ValidCombinatorValue $value
|
||||
*/
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function parse(ParserState $parserState, array &$comments = []): self
|
||||
{
|
||||
$consumedWhitespace = $parserState->consumeWhiteSpace($comments);
|
||||
|
||||
$nextToken = $parserState->peek();
|
||||
if (\in_array($nextToken, ['>', '+', '~'], true)) {
|
||||
$value = $nextToken;
|
||||
$parserState->consume(1);
|
||||
$parserState->consumeWhiteSpace($comments);
|
||||
} elseif ($consumedWhitespace !== '') {
|
||||
$value = ' ';
|
||||
} else {
|
||||
throw new UnexpectedTokenException(
|
||||
'combinator',
|
||||
$nextToken,
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ValidCombinatorValue
|
||||
*/
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $value
|
||||
*
|
||||
* @throws \UnexpectedValueException if `$value` is not either space, '>', '+' or '~'
|
||||
*/
|
||||
public function setValue(string $value): void
|
||||
{
|
||||
if (!\in_array($value, [' ', '>', '+', '~'], true)) {
|
||||
throw new \UnexpectedValueException('`' . $value . '` is not a valid selector combinator.');
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getSpecificity(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$spacing = $outputFormat->getSpaceAroundSelectorCombinator();
|
||||
if ($this->value === ' ') {
|
||||
$rendering = $spacing !== '' ? $spacing : ' ';
|
||||
} else {
|
||||
$rendering = $spacing . $this->value . $spacing;
|
||||
}
|
||||
|
||||
return $rendering;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
'value' => $this->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
41
vendor/sabberworm/php-css-parser/src/Property/Selector/Component.php
vendored
Normal file
41
vendor/sabberworm/php-css-parser/src/Property/Selector/Component.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property\Selector;
|
||||
|
||||
use Sabberworm\CSS\Renderable;
|
||||
|
||||
/**
|
||||
* This interface is for a class that represents a part of a selector which is either a compound selector (or a simple
|
||||
* selector, which is effectively a compound selector without any compounding) or a selector combinator.
|
||||
*
|
||||
* It allows a selector to be represented as an array of objects that implement this interface.
|
||||
* This is the formal definition:
|
||||
* selector = compound-selector [combinator, compound-selector]*
|
||||
*
|
||||
* The selector is comprised of an array of alternating types that can't be easily represented in a type-safe manner
|
||||
* without this.
|
||||
*
|
||||
* 'Selector component' is not a known grammar in the spec, but a convenience for the implementation.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Selectors/Selector_structure
|
||||
* @see https://www.w3.org/TR/selectors-4/#structure
|
||||
*/
|
||||
interface Component extends Renderable
|
||||
{
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getValue(): string;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $value
|
||||
*/
|
||||
public function setValue(string $value): void;
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getSpecificity(): int;
|
||||
}
|
||||
284
vendor/sabberworm/php-css-parser/src/Property/Selector/CompoundSelector.php
vendored
Normal file
284
vendor/sabberworm/php-css-parser/src/Property/Selector/CompoundSelector.php
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property\Selector;
|
||||
|
||||
use Sabberworm\CSS\Comment\Comment;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
use function Safe\preg_match;
|
||||
|
||||
/**
|
||||
* Class representing a CSS compound selector.
|
||||
* Selectors have to be split at combinators (space, `>`, `+`, `~`) before being passed to this class.
|
||||
*/
|
||||
class CompoundSelector implements Component
|
||||
{
|
||||
use ShortClassNameProvider;
|
||||
|
||||
private const PARSER_STOP_CHARACTERS = [
|
||||
'{',
|
||||
'}',
|
||||
'\'',
|
||||
'"',
|
||||
'(',
|
||||
')',
|
||||
'[',
|
||||
']',
|
||||
',',
|
||||
' ',
|
||||
"\t",
|
||||
"\n",
|
||||
"\r",
|
||||
'>',
|
||||
'+',
|
||||
'~',
|
||||
ParserState::EOF,
|
||||
'', // `ParserState::peek()` returns empty string rather than `ParserState::EOF` when end of string is reached
|
||||
];
|
||||
|
||||
private const SELECTOR_VALIDATION_RX = '/
|
||||
^
|
||||
# not starting with whitespace
|
||||
(?!\\s)
|
||||
(?:
|
||||
(?:
|
||||
# any sequence of valid unescaped characters, except quotes
|
||||
[a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*=~\\[\\]()\\-\\s\\.:#+>,]++
|
||||
|
|
||||
# one or more escaped characters
|
||||
(?:\\\\.)++
|
||||
|
|
||||
# quoted text, like in `[id="example"]`
|
||||
(?:
|
||||
# opening quote
|
||||
([\'"])
|
||||
(?:
|
||||
# sequence of characters except closing quote or backslash
|
||||
(?:(?!\\g{-1}|\\\\).)++
|
||||
|
|
||||
# one or more escaped characters
|
||||
(?:\\\\.)++
|
||||
)*+ # zero or more times
|
||||
# closing quote or end (unmatched quote is currently allowed)
|
||||
(?:\\g{-1}|$)
|
||||
)
|
||||
)++ # one or more times
|
||||
|
|
||||
# keyframe animation progress percentage (e.g. 50%)
|
||||
(?:\\d++%)
|
||||
)
|
||||
# not ending with whitespace
|
||||
(?<!\\s)
|
||||
$
|
||||
/ux';
|
||||
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $value
|
||||
*/
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function parse(ParserState $parserState, array &$comments = []): self
|
||||
{
|
||||
$selectorParts = [];
|
||||
$stringWrapperCharacter = null;
|
||||
$functionNestingLevel = 0;
|
||||
$isWithinAttribute = false;
|
||||
|
||||
while (true) {
|
||||
$selectorParts[] = $parserState->consumeUntil(self::PARSER_STOP_CHARACTERS, false, false, $comments);
|
||||
$nextCharacter = $parserState->peek();
|
||||
switch ($nextCharacter) {
|
||||
case '':
|
||||
// EOF
|
||||
break 2;
|
||||
case '\'':
|
||||
// The fallthrough is intentional.
|
||||
case '"':
|
||||
$lastPart = \end($selectorParts);
|
||||
$backslashCount = \strspn(\strrev($lastPart), '\\');
|
||||
$quoteIsEscaped = ($backslashCount % 2 === 1);
|
||||
if (!$quoteIsEscaped) {
|
||||
if (!\is_string($stringWrapperCharacter)) {
|
||||
$stringWrapperCharacter = $nextCharacter;
|
||||
} elseif ($stringWrapperCharacter === $nextCharacter) {
|
||||
$stringWrapperCharacter = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '(':
|
||||
if (!\is_string($stringWrapperCharacter)) {
|
||||
++$functionNestingLevel;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
if (!\is_string($stringWrapperCharacter)) {
|
||||
if ($functionNestingLevel <= 0) {
|
||||
throw new UnexpectedTokenException(
|
||||
'anything but',
|
||||
')',
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
--$functionNestingLevel;
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
if (!\is_string($stringWrapperCharacter)) {
|
||||
if ($isWithinAttribute) {
|
||||
throw new UnexpectedTokenException(
|
||||
'anything but',
|
||||
'[',
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
$isWithinAttribute = true;
|
||||
}
|
||||
break;
|
||||
case ']':
|
||||
if (!\is_string($stringWrapperCharacter)) {
|
||||
if (!$isWithinAttribute) {
|
||||
throw new UnexpectedTokenException(
|
||||
'anything but',
|
||||
']',
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
$isWithinAttribute = false;
|
||||
}
|
||||
break;
|
||||
case '{':
|
||||
// The fallthrough is intentional.
|
||||
case '}':
|
||||
if (!\is_string($stringWrapperCharacter)) {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
// The fallthrough is intentional.
|
||||
case ' ':
|
||||
// The fallthrough is intentional.
|
||||
case "\t":
|
||||
// The fallthrough is intentional.
|
||||
case "\n":
|
||||
// The fallthrough is intentional.
|
||||
case "\r":
|
||||
// The fallthrough is intentional.
|
||||
case '>':
|
||||
// The fallthrough is intentional.
|
||||
case '+':
|
||||
// The fallthrough is intentional.
|
||||
case '~':
|
||||
if (!\is_string($stringWrapperCharacter) && $functionNestingLevel === 0 && !$isWithinAttribute) {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$selectorParts[] = $parserState->consume(1);
|
||||
}
|
||||
|
||||
if ($functionNestingLevel !== 0) {
|
||||
throw new UnexpectedTokenException(')', $nextCharacter, 'literal', $parserState->currentLine());
|
||||
}
|
||||
if (\is_string($stringWrapperCharacter)) {
|
||||
throw new UnexpectedTokenException(
|
||||
$stringWrapperCharacter,
|
||||
$nextCharacter,
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
|
||||
$value = \implode('', $selectorParts);
|
||||
if ($value === '') {
|
||||
throw new UnexpectedTokenException('selector', $nextCharacter, 'literal', $parserState->currentLine());
|
||||
}
|
||||
if (!self::isValid($value)) {
|
||||
throw new UnexpectedTokenException(
|
||||
'Selector component is not valid:',
|
||||
'`' . $value . '`',
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $value
|
||||
*
|
||||
* @throws \UnexpectedValueException if `$value` contains invalid characters or has surrounding whitespce
|
||||
*/
|
||||
public function setValue(string $value): void
|
||||
{
|
||||
if (!self::isValid($value)) {
|
||||
throw new \UnexpectedValueException('`' . $value . '` is not a valid compound selector.');
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getSpecificity(): int
|
||||
{
|
||||
return SpecificityCalculator::calculate($this->value);
|
||||
}
|
||||
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
'value' => $this->value,
|
||||
];
|
||||
}
|
||||
|
||||
private static function isValid(string $value): bool
|
||||
{
|
||||
$numberOfMatches = preg_match(self::SELECTOR_VALIDATION_RX, $value);
|
||||
|
||||
return $numberOfMatches === 1;
|
||||
}
|
||||
}
|
||||
83
vendor/sabberworm/php-css-parser/src/Property/Selector/SpecificityCalculator.php
vendored
Normal file
83
vendor/sabberworm/php-css-parser/src/Property/Selector/SpecificityCalculator.php
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Property\Selector;
|
||||
|
||||
use function Safe\preg_match_all;
|
||||
|
||||
/**
|
||||
* Utility class to calculate the specificity of a CSS selector.
|
||||
*
|
||||
* The results are cached to avoid recalculating the specificity of the same selector multiple times.
|
||||
*/
|
||||
final class SpecificityCalculator
|
||||
{
|
||||
/**
|
||||
* regexp for specificity calculations
|
||||
*/
|
||||
private const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
|
||||
(\\.[\\w]+) # classes
|
||||
|
|
||||
\\[(\\w+) # attributes
|
||||
|
|
||||
(\\:( # pseudo classes
|
||||
link|visited|active
|
||||
|hover|focus
|
||||
|lang
|
||||
|target
|
||||
|enabled|disabled|checked|indeterminate
|
||||
|root
|
||||
|nth-child|nth-last-child|nth-of-type|nth-last-of-type
|
||||
|first-child|last-child|first-of-type|last-of-type
|
||||
|only-child|only-of-type
|
||||
|empty|contains
|
||||
))
|
||||
/ix';
|
||||
|
||||
/**
|
||||
* regexp for specificity calculations
|
||||
*/
|
||||
private const ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
|
||||
((^|[\\s\\+\\>\\~]+)[\\w]+ # elements
|
||||
|
|
||||
\\:{1,2}( # pseudo-elements
|
||||
after|before|first-letter|first-line|selection
|
||||
))
|
||||
/ix';
|
||||
|
||||
/**
|
||||
* @var array<string, int<0, max>>
|
||||
*/
|
||||
private static $cache = [];
|
||||
|
||||
/**
|
||||
* Calculates the specificity of the given CSS selector.
|
||||
*
|
||||
* @return int<0, max>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function calculate(string $selector): int
|
||||
{
|
||||
if (!isset(self::$cache[$selector])) {
|
||||
$a = 0;
|
||||
/// @todo should exclude \# as well as "#"
|
||||
$matches = null;
|
||||
$b = \substr_count($selector, '#');
|
||||
$c = preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $selector, $matches);
|
||||
$d = preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $selector, $matches);
|
||||
self::$cache[$selector] = ($a * 1000) + ($b * 100) + ($c * 10) + $d;
|
||||
}
|
||||
|
||||
return self::$cache[$selector];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cache in order to lower memory usage.
|
||||
*/
|
||||
public static function clearCache(): void
|
||||
{
|
||||
self::$cache = [];
|
||||
}
|
||||
}
|
||||
17
vendor/sabberworm/php-css-parser/src/Renderable.php
vendored
Normal file
17
vendor/sabberworm/php-css-parser/src/Renderable.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS;
|
||||
|
||||
interface Renderable
|
||||
{
|
||||
public function render(OutputFormat $outputFormat): string;
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array;
|
||||
}
|
||||
14
vendor/sabberworm/php-css-parser/src/Rule/Rule.php
vendored
Normal file
14
vendor/sabberworm/php-css-parser/src/Rule/Rule.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Rule;
|
||||
|
||||
use Sabberworm\CSS\Property\Declaration;
|
||||
|
||||
use function Safe\class_alias;
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0. Use `Property\Declaration` instead, which is a direct replacement.
|
||||
*/
|
||||
class_alias(Declaration::class, Rule::class);
|
||||
68
vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php
vendored
Normal file
68
vendor/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\RuleSet;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Property\AtRule;
|
||||
|
||||
/**
|
||||
* This class represents rule sets 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`.
|
||||
*/
|
||||
class AtRuleSet extends RuleSet implements AtRule
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $arguments;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $type
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $type, string $arguments = '', ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
$this->type = $type;
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function atRuleName(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function atRuleArgs(): string
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
$result = $formatter->comments($this);
|
||||
$arguments = $this->arguments;
|
||||
if ($arguments !== '') {
|
||||
$arguments = ' ' . $arguments;
|
||||
}
|
||||
$result .= "@{$this->type}$arguments{$formatter->spaceBeforeOpeningBrace()}{";
|
||||
$result .= $this->renderDeclarations($outputFormat);
|
||||
$result .= '}';
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
306
vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php
vendored
Normal file
306
vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\RuleSet;
|
||||
|
||||
use Sabberworm\CSS\Comment\Comment;
|
||||
use Sabberworm\CSS\Comment\CommentContainer;
|
||||
use Sabberworm\CSS\CSSElement;
|
||||
use Sabberworm\CSS\CSSList\CSSList;
|
||||
use Sabberworm\CSS\CSSList\CSSListItem;
|
||||
use Sabberworm\CSS\CSSList\KeyFrame;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\OutputException;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\Property\Declaration;
|
||||
use Sabberworm\CSS\Property\KeyframeSelector;
|
||||
use Sabberworm\CSS\Property\Selector;
|
||||
use Sabberworm\CSS\Settings;
|
||||
|
||||
/**
|
||||
* This class represents a `RuleSet` constrained by a `Selector`.
|
||||
*
|
||||
* It contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the
|
||||
* matching elements.
|
||||
*
|
||||
* Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
|
||||
*
|
||||
* Note that `CSSListItem` extends both `Commentable` and `Renderable`, so those interfaces must also be implemented.
|
||||
*/
|
||||
class DeclarationBlock implements CSSElement, CSSListItem, Positionable, DeclarationList
|
||||
{
|
||||
use CommentContainer;
|
||||
use LegacyDeclarationListMethods;
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* @var list<Selector>
|
||||
*/
|
||||
private $selectors = [];
|
||||
|
||||
/**
|
||||
* @var RuleSet
|
||||
*/
|
||||
private $ruleSet;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(?int $lineNumber = null)
|
||||
{
|
||||
$this->ruleSet = new RuleSet($lineNumber);
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, ?CSSList $list = null): ?DeclarationBlock
|
||||
{
|
||||
$comments = [];
|
||||
$result = new DeclarationBlock($parserState->currentLine());
|
||||
try {
|
||||
$selectors = self::parseSelectors($parserState, $list, $comments);
|
||||
$result->setSelectors($selectors, $list);
|
||||
if ($parserState->comes('{')) {
|
||||
$parserState->consume(1);
|
||||
}
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
if ($parserState->getSettings()->usesLenientParsing()) {
|
||||
if (!$parserState->consumeIfComes('}')) {
|
||||
$parserState->consumeUntil(['}', ParserState::EOF], false, true);
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
$result->setComments($comments);
|
||||
|
||||
RuleSet::parseRuleSet($parserState, $result->getRuleSet());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Selector|string>|string $selectors
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
public function setSelectors($selectors, ?CSSList $list = null): void
|
||||
{
|
||||
if (\is_array($selectors)) {
|
||||
$selectorsToSet = $selectors;
|
||||
} else {
|
||||
// A string of comma-separated selectors requires parsing.
|
||||
try {
|
||||
$parserState = new ParserState($selectors, Settings::create());
|
||||
$selectorsToSet = self::parseSelectors($parserState, $list);
|
||||
if (!$parserState->isEnd()) {
|
||||
throw new UnexpectedTokenException('EOF', 'more');
|
||||
}
|
||||
} catch (UnexpectedTokenException $exception) {
|
||||
// The exception message from parsing may refer to the faux `{` block start token,
|
||||
// which would be confusing.
|
||||
// Rethrow with a more useful message, that also includes the selector(s) string that was passed.
|
||||
throw new UnexpectedTokenException(
|
||||
'Selector(s) string is not valid.',
|
||||
$selectors,
|
||||
'custom'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert all items to a `Selector` if not already
|
||||
foreach ($selectorsToSet as $key => $selector) {
|
||||
if (!($selector instanceof Selector)) {
|
||||
if ($list === null || !($list instanceof KeyFrame)) {
|
||||
if (!Selector::isValid($selector)) {
|
||||
throw new UnexpectedTokenException(
|
||||
"Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.",
|
||||
$selector,
|
||||
'custom'
|
||||
);
|
||||
}
|
||||
$selectorsToSet[$key] = new Selector($selector);
|
||||
} else {
|
||||
if (!KeyframeSelector::isValid($selector)) {
|
||||
throw new UnexpectedTokenException(
|
||||
"Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.",
|
||||
$selector,
|
||||
'custom'
|
||||
);
|
||||
}
|
||||
$selectorsToSet[$key] = new KeyframeSelector($selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Discard the keys and reindex the array
|
||||
$this->selectors = \array_values($selectorsToSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one of the selectors of the block.
|
||||
*
|
||||
* @param Selector|string $selectorToRemove
|
||||
*/
|
||||
public function removeSelector($selectorToRemove): bool
|
||||
{
|
||||
if ($selectorToRemove instanceof Selector) {
|
||||
$selectorToRemove = $selectorToRemove->getSelector();
|
||||
}
|
||||
foreach ($this->selectors as $key => $selector) {
|
||||
if ($selector->getSelector() === $selectorToRemove) {
|
||||
unset($this->selectors[$key]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Selector>
|
||||
*/
|
||||
public function getSelectors(): array
|
||||
{
|
||||
return $this->selectors;
|
||||
}
|
||||
|
||||
public function getRuleSet(): RuleSet
|
||||
{
|
||||
return $this->ruleSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RuleSet::addDeclaration()
|
||||
*/
|
||||
public function addDeclaration(Declaration $declarationToAdd, ?Declaration $sibling = null): void
|
||||
{
|
||||
$this->ruleSet->addDeclaration($declarationToAdd, $sibling);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int<0, max>, Declaration>
|
||||
*
|
||||
* @see RuleSet::getDeclarations()
|
||||
*/
|
||||
public function getDeclarations(?string $searchPattern = null): array
|
||||
{
|
||||
return $this->ruleSet->getDeclarations($searchPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Declaration> $declarations
|
||||
*
|
||||
* @see RuleSet::setDeclarations()
|
||||
*/
|
||||
public function setDeclarations(array $declarations): void
|
||||
{
|
||||
$this->ruleSet->setDeclarations($declarations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Declaration>
|
||||
*
|
||||
* @see RuleSet::getDeclarationsAssociative()
|
||||
*/
|
||||
public function getDeclarationsAssociative(?string $searchPattern = null): array
|
||||
{
|
||||
return $this->ruleSet->getDeclarationsAssociative($searchPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RuleSet::removeDeclaration()
|
||||
*/
|
||||
public function removeDeclaration(Declaration $declarationToRemove): void
|
||||
{
|
||||
$this->ruleSet->removeDeclaration($declarationToRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RuleSet::removeMatchingDeclarations()
|
||||
*/
|
||||
public function removeMatchingDeclarations(string $searchPattern): void
|
||||
{
|
||||
$this->ruleSet->removeMatchingDeclarations($searchPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RuleSet::removeAllDeclarations()
|
||||
*/
|
||||
public function removeAllDeclarations(): void
|
||||
{
|
||||
$this->ruleSet->removeAllDeclarations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*
|
||||
* @throws OutputException
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
$result = $formatter->comments($this);
|
||||
if (\count($this->selectors) === 0) {
|
||||
// If all the selectors have been removed, this declaration block becomes invalid
|
||||
throw new OutputException(
|
||||
'Attempt to print declaration block with missing selector',
|
||||
$this->getLineNumber()
|
||||
);
|
||||
}
|
||||
$result .= $outputFormat->getContentBeforeDeclarationBlock();
|
||||
$result .= $formatter->implode(
|
||||
$formatter->spaceBeforeSelectorSeparator() . ',' . $formatter->spaceAfterSelectorSeparator(),
|
||||
$this->selectors
|
||||
);
|
||||
$result .= $outputFormat->getContentAfterDeclarationBlockSelectors();
|
||||
$result .= $formatter->spaceBeforeOpeningBrace() . '{';
|
||||
$result .= $this->ruleSet->render($outputFormat);
|
||||
$result .= '}';
|
||||
$result .= $outputFormat->getContentAfterDeclarationBlock();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Comment> $comments
|
||||
*
|
||||
* @return list<Selector>
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseSelectors(ParserState $parserState, ?CSSList $list, array &$comments = []): array
|
||||
{
|
||||
$selectorClass = $list instanceof KeyFrame ? KeyFrameSelector::class : Selector::class;
|
||||
$selectors = [];
|
||||
|
||||
while (true) {
|
||||
$selectors[] = $selectorClass::parse($parserState, $comments);
|
||||
if (!$parserState->consumeIfComes(',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $selectors;
|
||||
}
|
||||
}
|
||||
77
vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationList.php
vendored
Normal file
77
vendor/sabberworm/php-css-parser/src/RuleSet/DeclarationList.php
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\RuleSet;
|
||||
|
||||
use Sabberworm\CSS\Property\Declaration;
|
||||
|
||||
/**
|
||||
* Represents a CSS item that contains `Declaration`s, defining the methods to manipulate them.
|
||||
*/
|
||||
interface DeclarationList
|
||||
{
|
||||
public function addDeclaration(Declaration $declarationToAdd, ?Declaration $sibling = null): void;
|
||||
|
||||
public function removeDeclaration(Declaration $declarationToRemove): void;
|
||||
|
||||
public function removeMatchingDeclarations(string $searchPattern): void;
|
||||
|
||||
public function removeAllDeclarations(): void;
|
||||
|
||||
/**
|
||||
* @param array<Declaration> $declarations
|
||||
*/
|
||||
public function setDeclarations(array $declarations): void;
|
||||
|
||||
/**
|
||||
* @return array<int<0, max>, Declaration>
|
||||
*/
|
||||
public function getDeclarations(?string $searchPattern = null): array;
|
||||
|
||||
/**
|
||||
* @return array<string, Declaration>
|
||||
*/
|
||||
public function getDeclarationsAssociative(?string $searchPattern = null): array;
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `addDeclaration()` instead.
|
||||
*/
|
||||
public function addRule(Declaration $declarationToAdd, ?Declaration $sibling = null): void;
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `removeDeclaration()` instead.
|
||||
*/
|
||||
public function removeRule(Declaration $declarationToRemove): void;
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `removeMatchingDeclarations()` instead.
|
||||
*/
|
||||
public function removeMatchingRules(string $searchPattern): void;
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `removeAllDeclarations()` instead.
|
||||
*/
|
||||
public function removeAllRules(): void;
|
||||
|
||||
/**
|
||||
* @param array<Declaration> $declarations
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `setDeclarations()` instead.
|
||||
*/
|
||||
public function setRules(array $declarations): void;
|
||||
|
||||
/**
|
||||
* @return array<int<0, max>, Declaration>
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `getDeclarations()` instead.
|
||||
*/
|
||||
public function getRules(?string $searchPattern = null): array;
|
||||
|
||||
/**
|
||||
* @return array<string, Declaration>
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `getDeclarationsAssociative()` instead.
|
||||
*/
|
||||
public function getRulesAssoc(?string $searchPattern = null): array;
|
||||
}
|
||||
75
vendor/sabberworm/php-css-parser/src/RuleSet/LegacyDeclarationListMethods.php
vendored
Normal file
75
vendor/sabberworm/php-css-parser/src/RuleSet/LegacyDeclarationListMethods.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\RuleSet;
|
||||
|
||||
use Sabberworm\CSS\Property\Declaration;
|
||||
|
||||
/**
|
||||
* Provides a mapping of the deprecated methods in a `DeclarationList` to their renamed replacements.
|
||||
*/
|
||||
trait LegacyDeclarationListMethods
|
||||
{
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `addDeclaration()` instead.
|
||||
*/
|
||||
public function addRule(Declaration $declarationToAdd, ?Declaration $sibling = null): void
|
||||
{
|
||||
$this->addDeclaration($declarationToAdd, $sibling);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `removeDeclaration()` instead.
|
||||
*/
|
||||
public function removeRule(Declaration $declarationToRemove): void
|
||||
{
|
||||
$this->removeDeclaration($declarationToRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `removeMatchingDeclarations()` instead.
|
||||
*/
|
||||
public function removeMatchingRules(string $searchPattern): void
|
||||
{
|
||||
$this->removeMatchingDeclarations($searchPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `removeAllDeclarations()` instead.
|
||||
*/
|
||||
public function removeAllRules(): void
|
||||
{
|
||||
$this->removeAllDeclarations();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Declaration> $declarations
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `setDeclarations()` instead.
|
||||
*/
|
||||
public function setRules(array $declarations): void
|
||||
{
|
||||
$this->setDeclarations($declarations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int<0, max>, Declaration>
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `getDeclarations()` instead.
|
||||
*/
|
||||
public function getRules(?string $searchPattern = null): array
|
||||
{
|
||||
return $this->getDeclarations($searchPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Declaration>
|
||||
*
|
||||
* @deprecated in v9.2, will be removed in v10.0; use `getDeclarationsAssociative()` instead.
|
||||
*/
|
||||
public function getRulesAssoc(?string $searchPattern = null): array
|
||||
{
|
||||
return $this->getDeclarationsAssociative($searchPattern);
|
||||
}
|
||||
}
|
||||
12
vendor/sabberworm/php-css-parser/src/RuleSet/RuleContainer.php
vendored
Normal file
12
vendor/sabberworm/php-css-parser/src/RuleSet/RuleContainer.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\RuleSet;
|
||||
|
||||
use function Safe\class_alias;
|
||||
|
||||
/**
|
||||
* @deprecated in v9.2, will be removed in v10.0. Use `DeclarationList` instead, which is a direct replacement.
|
||||
*/
|
||||
class_alias(DeclarationList::class, RuleContainer::class);
|
||||
392
vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php
vendored
Normal file
392
vendor/sabberworm/php-css-parser/src/RuleSet/RuleSet.php
vendored
Normal file
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\RuleSet;
|
||||
|
||||
use Sabberworm\CSS\Comment\CommentContainer;
|
||||
use Sabberworm\CSS\CSSElement;
|
||||
use Sabberworm\CSS\CSSList\CSSListItem;
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\Property\Declaration;
|
||||
|
||||
/**
|
||||
* This class is a container for individual `Declaration`s.
|
||||
*
|
||||
* The most common form of a rule set is one constrained by a selector, i.e., a `DeclarationBlock`.
|
||||
* However, unknown `AtRule`s (like `@font-face`) are rule sets as well.
|
||||
*
|
||||
* If you want to manipulate a `RuleSet`,
|
||||
* use the methods `addDeclaration()`, `getDeclarations()`, `removeDeclaration()`, `removeMatchingDeclarations()`, etc.
|
||||
*
|
||||
* Note that `CSSListItem` extends both `Commentable` and `Renderable`, so those interfaces must also be implemented.
|
||||
*/
|
||||
class RuleSet implements CSSElement, CSSListItem, Positionable, DeclarationList
|
||||
{
|
||||
use CommentContainer;
|
||||
use LegacyDeclarationListMethods;
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* the declarations in this rule set, using the property name as the key,
|
||||
* with potentially multiple declarations per property name.
|
||||
*
|
||||
* @var array<string, array<int<0, max>, Declaration>>
|
||||
*/
|
||||
private $declarations = [];
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(?int $lineNumber = null)
|
||||
{
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parseRuleSet(ParserState $parserState, RuleSet $ruleSet): void
|
||||
{
|
||||
while ($parserState->comes(';')) {
|
||||
$parserState->consume(';');
|
||||
}
|
||||
while (true) {
|
||||
$commentsBeforeDeclaration = [];
|
||||
$parserState->consumeWhiteSpace($commentsBeforeDeclaration);
|
||||
if ($parserState->comes('}')) {
|
||||
break;
|
||||
}
|
||||
$declaration = null;
|
||||
if ($parserState->getSettings()->usesLenientParsing()) {
|
||||
try {
|
||||
$declaration = Declaration::parse($parserState, $commentsBeforeDeclaration);
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
try {
|
||||
$consumedText = $parserState->consumeUntil(["\n", ';', '}'], true);
|
||||
// We need to “unfind” the matches to the end of the ruleSet as this will be matched later
|
||||
if ($parserState->streql(\substr($consumedText, -1), '}')) {
|
||||
$parserState->backtrack(1);
|
||||
} else {
|
||||
while ($parserState->comes(';')) {
|
||||
$parserState->consume(';');
|
||||
}
|
||||
}
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
// We’ve reached the end of the document. Just close the RuleSet.
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$declaration = Declaration::parse($parserState, $commentsBeforeDeclaration);
|
||||
}
|
||||
if ($declaration instanceof Declaration) {
|
||||
$ruleSet->addDeclaration($declaration);
|
||||
}
|
||||
}
|
||||
$parserState->consume('}');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \UnexpectedValueException
|
||||
* if the last `Declaration` is needed as a basis for setting position, but does not have a valid position,
|
||||
* which should never happen
|
||||
*/
|
||||
public function addDeclaration(Declaration $declarationToAdd, ?Declaration $sibling = null): void
|
||||
{
|
||||
$propertyName = $declarationToAdd->getPropertyName();
|
||||
if (!isset($this->declarations[$propertyName])) {
|
||||
$this->declarations[$propertyName] = [];
|
||||
}
|
||||
|
||||
$position = \count($this->declarations[$propertyName]);
|
||||
|
||||
if ($sibling !== null) {
|
||||
$siblingIsInSet = false;
|
||||
$siblingPosition = \array_search($sibling, $this->declarations[$propertyName], true);
|
||||
if ($siblingPosition !== false) {
|
||||
$siblingIsInSet = true;
|
||||
$position = $siblingPosition;
|
||||
} else {
|
||||
$siblingIsInSet = $this->hasDeclaration($sibling);
|
||||
if ($siblingIsInSet) {
|
||||
// Maintain ordering within `$this->declarations[$propertyName]`
|
||||
// by inserting before first `Declaration` with a same-or-later position than the sibling.
|
||||
foreach ($this->declarations[$propertyName] as $index => $declaration) {
|
||||
if (self::comparePositionable($declaration, $sibling) >= 0) {
|
||||
$position = $index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($siblingIsInSet) {
|
||||
// Increment column number of all existing declarations on same line, starting at sibling
|
||||
$siblingLineNumber = $sibling->getLineNumber();
|
||||
$siblingColumnNumber = $sibling->getColumnNumber();
|
||||
foreach ($this->declarations as $declarationsForAProperty) {
|
||||
foreach ($declarationsForAProperty as $declaration) {
|
||||
if (
|
||||
$declaration->getLineNumber() === $siblingLineNumber &&
|
||||
$declaration->getColumnNumber() >= $siblingColumnNumber
|
||||
) {
|
||||
$declaration->setPosition($siblingLineNumber, $declaration->getColumnNumber() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
$declarationToAdd->setPosition($siblingLineNumber, $siblingColumnNumber);
|
||||
}
|
||||
}
|
||||
|
||||
if ($declarationToAdd->getLineNumber() === null) {
|
||||
//this node is added manually, give it the next best line
|
||||
$columnNumber = $declarationToAdd->getColumnNumber() ?? 0;
|
||||
$declarations = $this->getDeclarations();
|
||||
$declarationsCount = \count($declarations);
|
||||
if ($declarationsCount > 0) {
|
||||
$last = $declarations[$declarationsCount - 1];
|
||||
$lastsLineNumber = $last->getLineNumber();
|
||||
if (!\is_int($lastsLineNumber)) {
|
||||
throw new \UnexpectedValueException(
|
||||
'A Declaration without a line number was found during addDeclaration',
|
||||
1750718399
|
||||
);
|
||||
}
|
||||
$declarationToAdd->setPosition($lastsLineNumber + 1, $columnNumber);
|
||||
} else {
|
||||
$declarationToAdd->setPosition(1, $columnNumber);
|
||||
}
|
||||
} elseif ($declarationToAdd->getColumnNumber() === null) {
|
||||
$declarationToAdd->setPosition($declarationToAdd->getLineNumber(), 0);
|
||||
}
|
||||
|
||||
\array_splice($this->declarations[$propertyName], $position, 0, [$declarationToAdd]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all declarations matching the given property name
|
||||
*
|
||||
* @example $ruleSet->getDeclarations('font') // returns array(0 => $declaration, …) or array().
|
||||
*
|
||||
* @example $ruleSet->getDeclarations('font-')
|
||||
* //returns an array of all declarations either beginning with font- or matching font.
|
||||
*
|
||||
* @param string|null $searchPattern
|
||||
* Pattern to search for. If null, returns all declarations.
|
||||
* If the pattern ends with a dash, all declarations starting with the pattern are returned
|
||||
* as well as one matching the pattern with the dash excluded.
|
||||
*
|
||||
* @return array<int<0, max>, Declaration>
|
||||
*/
|
||||
public function getDeclarations(?string $searchPattern = null): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->declarations as $propertyName => $declarations) {
|
||||
// Either no search pattern was given
|
||||
// or the search pattern matches the found declaration's property name exactly
|
||||
// or the search pattern ends in “-”
|
||||
// ... and the found declaration's property name starts with the search pattern
|
||||
if (
|
||||
$searchPattern === null || $propertyName === $searchPattern
|
||||
|| (
|
||||
\strrpos($searchPattern, '-') === \strlen($searchPattern) - \strlen('-')
|
||||
&& (\strpos($propertyName, $searchPattern) === 0
|
||||
|| $propertyName === \substr($searchPattern, 0, -1))
|
||||
)
|
||||
) {
|
||||
$result = \array_merge($result, $declarations);
|
||||
}
|
||||
}
|
||||
\usort($result, [self::class, 'comparePositionable']);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides all the declarations of this set.
|
||||
*
|
||||
* @param array<Declaration> $declarations
|
||||
*/
|
||||
public function setDeclarations(array $declarations): void
|
||||
{
|
||||
$this->declarations = [];
|
||||
foreach ($declarations as $declaration) {
|
||||
$this->addDeclaration($declaration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all declarations with property names matching the given pattern and returns them in an associative array
|
||||
* with the property names as keys.
|
||||
* This method exists mainly for backwards-compatibility and is really only partially useful.
|
||||
*
|
||||
* Note: This method loses some information: Calling this (with an argument of `background-`) on a declaration block
|
||||
* like `{ background-color: green; background-color; rgba(0, 127, 0, 0.7); }` will only yield an associative array
|
||||
* containing the rgba-valued declaration while `getDeclarations()` would yield an indexed array containing both.
|
||||
*
|
||||
* @param string|null $searchPattern
|
||||
* Pattern to search for. If null, returns all declarations. If the pattern ends with a dash,
|
||||
* all declarations starting with the pattern are returned as well as one matching the pattern with the dash
|
||||
* excluded.
|
||||
*
|
||||
* @return array<string, Declaration>
|
||||
*/
|
||||
public function getDeclarationsAssociative(?string $searchPattern = null): array
|
||||
{
|
||||
/** @var array<string, Declaration> $result */
|
||||
$result = [];
|
||||
foreach ($this->getDeclarations($searchPattern) as $declaration) {
|
||||
$result[$declaration->getPropertyName()] = $declaration;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a `Declaration` from this `RuleSet` by identity.
|
||||
*/
|
||||
public function removeDeclaration(Declaration $declarationToRemove): void
|
||||
{
|
||||
$nameOfPropertyToRemove = $declarationToRemove->getPropertyName();
|
||||
if (!isset($this->declarations[$nameOfPropertyToRemove])) {
|
||||
return;
|
||||
}
|
||||
foreach ($this->declarations[$nameOfPropertyToRemove] as $key => $declaration) {
|
||||
if ($declaration === $declarationToRemove) {
|
||||
unset($this->declarations[$nameOfPropertyToRemove][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes declarations by property name or search pattern.
|
||||
*
|
||||
* @param string $searchPattern
|
||||
* pattern to remove.
|
||||
* If the pattern ends in a dash,
|
||||
* all declarations starting with the pattern are removed as well as one matching the pattern with the dash
|
||||
* excluded.
|
||||
*/
|
||||
public function removeMatchingDeclarations(string $searchPattern): void
|
||||
{
|
||||
foreach ($this->declarations as $propertyName => $declarations) {
|
||||
// Either the search pattern matches the found declaration's property name exactly
|
||||
// or the search pattern ends in “-” and the found declaration's property name starts with the search
|
||||
// pattern or equals it (without the trailing dash).
|
||||
if (
|
||||
$propertyName === $searchPattern
|
||||
|| (\strrpos($searchPattern, '-') === \strlen($searchPattern) - \strlen('-')
|
||||
&& (\strpos($propertyName, $searchPattern) === 0
|
||||
|| $propertyName === \substr($searchPattern, 0, -1)))
|
||||
) {
|
||||
unset($this->declarations[$propertyName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function removeAllDeclarations(): void
|
||||
{
|
||||
$this->declarations = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return $this->renderDeclarations($outputFormat);
|
||||
}
|
||||
|
||||
protected function renderDeclarations(OutputFormat $outputFormat): string
|
||||
{
|
||||
$result = '';
|
||||
$isFirst = true;
|
||||
$nextLevelFormat = $outputFormat->nextLevel();
|
||||
foreach ($this->getDeclarations() as $declaration) {
|
||||
$nextLevelFormatter = $nextLevelFormat->getFormatter();
|
||||
$renderedDeclaration = $nextLevelFormatter->safely(
|
||||
static function () use ($declaration, $nextLevelFormat): string {
|
||||
return $declaration->render($nextLevelFormat);
|
||||
}
|
||||
);
|
||||
if ($renderedDeclaration === null) {
|
||||
continue;
|
||||
}
|
||||
if ($isFirst) {
|
||||
$isFirst = false;
|
||||
$result .= $nextLevelFormatter->spaceBeforeRules();
|
||||
} else {
|
||||
$result .= $nextLevelFormatter->spaceBetweenRules();
|
||||
}
|
||||
$result .= $renderedDeclaration;
|
||||
}
|
||||
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
if (!$isFirst) {
|
||||
// Had some output
|
||||
$result .= $formatter->spaceAfterRules();
|
||||
}
|
||||
|
||||
return $formatter->removeLastSemicolon($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int negative if `$first` is before `$second`; zero if they have the same position; positive otherwise
|
||||
*
|
||||
* @throws \UnexpectedValueException if either argument does not have a valid position, which should never happen
|
||||
*/
|
||||
private static function comparePositionable(Positionable $first, Positionable $second): int
|
||||
{
|
||||
$firstsLineNumber = $first->getLineNumber();
|
||||
$secondsLineNumber = $second->getLineNumber();
|
||||
if (!\is_int($firstsLineNumber) || !\is_int($secondsLineNumber)) {
|
||||
throw new \UnexpectedValueException(
|
||||
'A Declaration without a line number was passed to comparePositionable',
|
||||
1750637683
|
||||
);
|
||||
}
|
||||
|
||||
if ($firstsLineNumber === $secondsLineNumber) {
|
||||
$firstsColumnNumber = $first->getColumnNumber();
|
||||
$secondsColumnNumber = $second->getColumnNumber();
|
||||
if (!\is_int($firstsColumnNumber) || !\is_int($secondsColumnNumber)) {
|
||||
throw new \UnexpectedValueException(
|
||||
'A Declaration without a column number was passed to comparePositionable',
|
||||
1750637761
|
||||
);
|
||||
}
|
||||
return $firstsColumnNumber - $secondsColumnNumber;
|
||||
}
|
||||
|
||||
return $firstsLineNumber - $secondsLineNumber;
|
||||
}
|
||||
|
||||
private function hasDeclaration(Declaration $declaration): bool
|
||||
{
|
||||
foreach ($this->declarations as $declarationsForAProperty) {
|
||||
if (\in_array($declaration, $declarationsForAProperty, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
124
vendor/sabberworm/php-css-parser/src/Settings.php
vendored
Normal file
124
vendor/sabberworm/php-css-parser/src/Settings.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS;
|
||||
|
||||
/**
|
||||
* Parser settings class.
|
||||
*
|
||||
* Configure parser behaviour here.
|
||||
*/
|
||||
class Settings
|
||||
{
|
||||
/**
|
||||
* Multi-byte string support.
|
||||
*
|
||||
* If `true` (`mbstring` extension must be enabled), will use (slower) `mb_strlen`, `mb_convert_case`, `mb_substr`
|
||||
* and `mb_strpos` functions. Otherwise, the normal (ASCII-Only) functions will be used.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $multibyteSupport;
|
||||
|
||||
/**
|
||||
* The default charset for the CSS if no `@charset` declaration is found. Defaults to utf-8.
|
||||
*
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private $defaultCharset = 'utf-8';
|
||||
|
||||
/**
|
||||
* Whether the parser silently ignore invalid rules instead of choking on them.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $lenientParsing = true;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
$this->multibyteSupport = \extension_loaded('mbstring');
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new Settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables multi-byte string support.
|
||||
*
|
||||
* If `true` (`mbstring` extension must be enabled), will use (slower) `mb_strlen`, `mb_convert_case`, `mb_substr`
|
||||
* and `mb_strpos` functions. Otherwise, the normal (ASCII-Only) functions will be used.
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function withMultibyteSupport(bool $multibyteSupport = true): self
|
||||
{
|
||||
$this->multibyteSupport = $multibyteSupport;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the charset to be used if the CSS does not contain an `@charset` declaration.
|
||||
*
|
||||
* @param non-empty-string $defaultCharset
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function withDefaultCharset(string $defaultCharset): self
|
||||
{
|
||||
$this->defaultCharset = $defaultCharset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether the parser should silently ignore invalid rules.
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function withLenientParsing(bool $usesLenientParsing = true): self
|
||||
{
|
||||
$this->lenientParsing = $usesLenientParsing;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the parser to choke on invalid rules.
|
||||
*
|
||||
* @return $this fluent interface
|
||||
*/
|
||||
public function beStrict(): self
|
||||
{
|
||||
return $this->withLenientParsing(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function hasMultibyteSupport(): bool
|
||||
{
|
||||
return $this->multibyteSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getDefaultCharset(): string
|
||||
{
|
||||
return $this->defaultCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function usesLenientParsing(): bool
|
||||
{
|
||||
return $this->lenientParsing;
|
||||
}
|
||||
}
|
||||
21
vendor/sabberworm/php-css-parser/src/ShortClassNameProvider.php
vendored
Normal file
21
vendor/sabberworm/php-css-parser/src/ShortClassNameProvider.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS;
|
||||
|
||||
/**
|
||||
* Provides a method to obtain the short name of the instantiated class (i.e. without namespace prefix).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait ShortClassNameProvider
|
||||
{
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function getShortClassName(): string
|
||||
{
|
||||
return (new \ReflectionClass($this))->getShortName();
|
||||
}
|
||||
}
|
||||
132
vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php
vendored
Normal file
132
vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* A `CSSFunction` represents a special kind of value that also contains a function name and where the values are the
|
||||
* function’s arguments. It also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`.
|
||||
*/
|
||||
class CSSFunction extends ValueList
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $name
|
||||
* @param RuleValueList|array<Value|string> $arguments
|
||||
* @param non-empty-string $separator
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $name, $arguments, string $separator = ',', ?int $lineNumber = null)
|
||||
{
|
||||
if ($arguments instanceof RuleValueList) {
|
||||
$separator = $arguments->getListSeparator();
|
||||
$arguments = $arguments->getListComponents();
|
||||
}
|
||||
$this->name = $name;
|
||||
$this->setPosition($lineNumber); // TODO: redundant?
|
||||
parent::__construct($arguments, $separator, $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $ignoreCase = false): CSSFunction
|
||||
{
|
||||
$name = self::parseName($parserState, $ignoreCase);
|
||||
$parserState->consume('(');
|
||||
$arguments = self::parseArguments($parserState);
|
||||
|
||||
$result = new CSSFunction($name, $arguments, ',', $parserState->currentLine());
|
||||
$parserState->consume(')');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseName(ParserState $parserState, bool $ignoreCase = false): string
|
||||
{
|
||||
return $parserState->parseIdentifier($ignoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value|string
|
||||
*
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseArguments(ParserState $parserState)
|
||||
{
|
||||
return Value::parseValue($parserState, ['=', ' ', ',']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $name
|
||||
*/
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Value|string>
|
||||
*/
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$arguments = parent::render($outputFormat);
|
||||
return "{$this->name}({$arguments})";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return \array_merge(
|
||||
[
|
||||
'class' => 'placeholder',
|
||||
'name' => $this->name,
|
||||
],
|
||||
parent::getArrayRepresentation()
|
||||
);
|
||||
}
|
||||
}
|
||||
125
vendor/sabberworm/php-css-parser/src/Value/CSSString.php
vendored
Normal file
125
vendor/sabberworm/php-css-parser/src/Value/CSSString.php
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
use function Safe\preg_match;
|
||||
|
||||
/**
|
||||
* This class is a wrapper for quoted strings to distinguish them from keywords.
|
||||
*
|
||||
* `CSSString`s always output with double quotes.
|
||||
*/
|
||||
class CSSString extends PrimitiveValue
|
||||
{
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $string;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $string, ?int $lineNumber = null)
|
||||
{
|
||||
$this->string = $string;
|
||||
parent::__construct($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState): CSSString
|
||||
{
|
||||
$begin = $parserState->peek();
|
||||
$quote = null;
|
||||
if ($begin === "'") {
|
||||
$quote = "'";
|
||||
} elseif ($begin === '"') {
|
||||
$quote = '"';
|
||||
}
|
||||
if ($quote !== null) {
|
||||
$parserState->consume($quote);
|
||||
}
|
||||
$result = '';
|
||||
$content = null;
|
||||
if ($quote === null) {
|
||||
// Unquoted strings end in whitespace or with braces, brackets, parentheses
|
||||
while (preg_match('/[\\s{}()<>\\[\\]]/isu', $parserState->peek()) === 0) {
|
||||
$result .= $parserState->parseCharacter(false);
|
||||
}
|
||||
} else {
|
||||
while (!$parserState->comes($quote)) {
|
||||
$content = $parserState->parseCharacter(false);
|
||||
if ($content === null) {
|
||||
throw new SourceException(
|
||||
"Non-well-formed quoted string {$parserState->peek(3)}",
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
$result .= $content;
|
||||
}
|
||||
$parserState->consume($quote);
|
||||
}
|
||||
return new CSSString($result, $parserState->currentLine());
|
||||
}
|
||||
|
||||
public function setString(string $string): void
|
||||
{
|
||||
$this->string = $string;
|
||||
}
|
||||
|
||||
public function getString(): string
|
||||
{
|
||||
return $this->string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return $outputFormat->getStringQuotingType()
|
||||
. $this->escape($this->string, $outputFormat)
|
||||
. $outputFormat->getStringQuotingType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
// We're using the term "contents" here to make the difference to the class more clear.
|
||||
'contents' => $this->string,
|
||||
];
|
||||
}
|
||||
|
||||
private function escape(string $string, OutputFormat $outputFormat): string
|
||||
{
|
||||
$charactersToEscape = '\\';
|
||||
$charactersToEscape .= ($outputFormat->getStringQuotingType() === '"' ? '"' : "'");
|
||||
$withEscapedQuotes = \addcslashes($string, $charactersToEscape);
|
||||
|
||||
$withNewlineEncoded = \str_replace("\n", '\\A', $withEscapedQuotes);
|
||||
|
||||
return $withNewlineEncoded;
|
||||
}
|
||||
}
|
||||
109
vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php
vendored
Normal file
109
vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
use function Safe\preg_match;
|
||||
|
||||
class CalcFunction extends CSSFunction
|
||||
{
|
||||
private const T_OPERAND = 1;
|
||||
private const T_OPERATOR = 2;
|
||||
|
||||
/**
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $ignoreCase = false): CSSFunction
|
||||
{
|
||||
$operators = ['+', '-', '*', '/'];
|
||||
$function = $parserState->parseIdentifier();
|
||||
if ($parserState->peek() !== '(') {
|
||||
// Found ; or end of line before an opening bracket
|
||||
throw new UnexpectedTokenException('(', $parserState->peek(), 'literal', $parserState->currentLine());
|
||||
} elseif ($function !== 'calc') {
|
||||
// Found invalid calc definition. Example calc (...
|
||||
throw new UnexpectedTokenException('calc', $function, 'literal', $parserState->currentLine());
|
||||
}
|
||||
$parserState->consume('(');
|
||||
$calcRuleValueList = new CalcRuleValueList($parserState->currentLine());
|
||||
$list = new RuleValueList(',', $parserState->currentLine());
|
||||
$nestingLevel = 0;
|
||||
$lastComponentType = null;
|
||||
while (!$parserState->comes(')') || $nestingLevel > 0) {
|
||||
if ($parserState->isEnd() && $nestingLevel === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$parserState->consumeWhiteSpace();
|
||||
if ($parserState->comes('(')) {
|
||||
$nestingLevel++;
|
||||
$calcRuleValueList->addListComponent($parserState->consume(1));
|
||||
$parserState->consumeWhiteSpace();
|
||||
continue;
|
||||
} elseif ($parserState->comes(')')) {
|
||||
$nestingLevel--;
|
||||
$calcRuleValueList->addListComponent($parserState->consume(1));
|
||||
$parserState->consumeWhiteSpace();
|
||||
continue;
|
||||
}
|
||||
if ($lastComponentType !== CalcFunction::T_OPERAND) {
|
||||
$value = Value::parsePrimitiveValue($parserState);
|
||||
$calcRuleValueList->addListComponent($value);
|
||||
$lastComponentType = CalcFunction::T_OPERAND;
|
||||
} else {
|
||||
if (\in_array($parserState->peek(), $operators, true)) {
|
||||
if (($parserState->comes('-') || $parserState->comes('+'))) {
|
||||
if (
|
||||
preg_match('/\\s/', $parserState->peek(1, -1)) !== 1
|
||||
|| preg_match('/\\s/', $parserState->peek(1, 1)) !== 1
|
||||
) {
|
||||
throw new UnexpectedTokenException(
|
||||
" {$parserState->peek()} ",
|
||||
$parserState->peek(1, -1) . $parserState->peek(2),
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
}
|
||||
$calcRuleValueList->addListComponent($parserState->consume(1));
|
||||
$lastComponentType = CalcFunction::T_OPERATOR;
|
||||
} else {
|
||||
throw new UnexpectedTokenException(
|
||||
\sprintf(
|
||||
'Next token was expected to be an operand of type %s. Instead "%s" was found.',
|
||||
\implode(', ', $operators),
|
||||
$parserState->peek()
|
||||
),
|
||||
'',
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
}
|
||||
$list->addListComponent($calcRuleValueList);
|
||||
if (!$parserState->isEnd()) {
|
||||
$parserState->consume(')');
|
||||
}
|
||||
return new CalcFunction($function, $list, ',', $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
}
|
||||
33
vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php
vendored
Normal file
33
vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
|
||||
class CalcRuleValueList extends RuleValueList
|
||||
{
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct(',', $lineNumber);
|
||||
}
|
||||
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return $outputFormat->getFormatter()->implode(' ', $this->components);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
}
|
||||
400
vendor/sabberworm/php-css-parser/src/Value/Color.php
vendored
Normal file
400
vendor/sabberworm/php-css-parser/src/Value/Color.php
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* `Color's 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.
|
||||
*/
|
||||
class Color extends CSSFunction
|
||||
{
|
||||
/**
|
||||
* @param array<non-empty-string, Value|string> $colorValues
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(array $colorValues, ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct(\implode('', \array_keys($colorValues)), $colorValues, ',', $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $ignoreCase = false): CSSFunction
|
||||
{
|
||||
return $parserState->comes('#')
|
||||
? self::parseHexColor($parserState)
|
||||
: self::parseColorFunction($parserState);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseHexColor(ParserState $parserState): Color
|
||||
{
|
||||
$parserState->consume('#');
|
||||
$hexValue = $parserState->parseIdentifier(false);
|
||||
if ($parserState->strlen($hexValue) === 3) {
|
||||
$hexValue = $hexValue[0] . $hexValue[0] . $hexValue[1] . $hexValue[1] . $hexValue[2] . $hexValue[2];
|
||||
} elseif ($parserState->strlen($hexValue) === 4) {
|
||||
$hexValue = $hexValue[0] . $hexValue[0] . $hexValue[1] . $hexValue[1] . $hexValue[2] . $hexValue[2]
|
||||
. $hexValue[3] . $hexValue[3];
|
||||
}
|
||||
|
||||
if ($parserState->strlen($hexValue) === 8) {
|
||||
$colorValues = [
|
||||
'r' => new Size(\intval($hexValue[0] . $hexValue[1], 16), null, true, $parserState->currentLine()),
|
||||
'g' => new Size(\intval($hexValue[2] . $hexValue[3], 16), null, true, $parserState->currentLine()),
|
||||
'b' => new Size(\intval($hexValue[4] . $hexValue[5], 16), null, true, $parserState->currentLine()),
|
||||
'a' => new Size(
|
||||
\round(self::mapRange(\intval($hexValue[6] . $hexValue[7], 16), 0, 255, 0, 1), 2),
|
||||
null,
|
||||
true,
|
||||
$parserState->currentLine()
|
||||
),
|
||||
];
|
||||
} elseif ($parserState->strlen($hexValue) === 6) {
|
||||
$colorValues = [
|
||||
'r' => new Size(\intval($hexValue[0] . $hexValue[1], 16), null, true, $parserState->currentLine()),
|
||||
'g' => new Size(\intval($hexValue[2] . $hexValue[3], 16), null, true, $parserState->currentLine()),
|
||||
'b' => new Size(\intval($hexValue[4] . $hexValue[5], 16), null, true, $parserState->currentLine()),
|
||||
];
|
||||
} else {
|
||||
throw new UnexpectedTokenException(
|
||||
'Invalid hex color value',
|
||||
$hexValue,
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
|
||||
return new Color($colorValues, $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseColorFunction(ParserState $parserState): CSSFunction
|
||||
{
|
||||
$colorValues = [];
|
||||
|
||||
$colorMode = $parserState->parseIdentifier(true);
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consume('(');
|
||||
|
||||
// CSS Color Module Level 4 says that `rgb` and `rgba` are now aliases; likewise `hsl` and `hsla`.
|
||||
// So, attempt to parse with the `a`, and allow for it not being there.
|
||||
switch ($colorMode) {
|
||||
case 'rgb':
|
||||
$colorModeForParsing = 'rgba';
|
||||
$mayHaveOptionalAlpha = true;
|
||||
break;
|
||||
case 'hsl':
|
||||
$colorModeForParsing = 'hsla';
|
||||
$mayHaveOptionalAlpha = true;
|
||||
break;
|
||||
case 'rgba':
|
||||
// This is handled identically to the following case.
|
||||
case 'hsla':
|
||||
$colorModeForParsing = $colorMode;
|
||||
$mayHaveOptionalAlpha = true;
|
||||
break;
|
||||
default:
|
||||
$colorModeForParsing = $colorMode;
|
||||
$mayHaveOptionalAlpha = false;
|
||||
}
|
||||
|
||||
$containsVar = false;
|
||||
$containsNone = false;
|
||||
$isLegacySyntax = false;
|
||||
$expectedArgumentCount = $parserState->strlen($colorModeForParsing);
|
||||
for ($argumentIndex = 0; $argumentIndex < $expectedArgumentCount; ++$argumentIndex) {
|
||||
$parserState->consumeWhiteSpace();
|
||||
$valueKey = $colorModeForParsing[$argumentIndex];
|
||||
if ($parserState->comes('var')) {
|
||||
$colorValues[$valueKey] = CSSFunction::parseIdentifierOrFunction($parserState);
|
||||
$containsVar = true;
|
||||
} elseif (!$isLegacySyntax && $parserState->comes('none')) {
|
||||
$colorValues[$valueKey] = $parserState->parseIdentifier();
|
||||
$containsNone = true;
|
||||
} else {
|
||||
$colorValues[$valueKey] = Size::parse($parserState, true);
|
||||
}
|
||||
|
||||
// This must be done first, to consume comments as well, so that the `comes` test will work.
|
||||
$parserState->consumeWhiteSpace();
|
||||
|
||||
// With a `var` argument, the function can have fewer arguments.
|
||||
// And as of CSS Color Module Level 4, the alpha argument is optional.
|
||||
$canCloseNow =
|
||||
$containsVar
|
||||
|| ($mayHaveOptionalAlpha && $argumentIndex >= $expectedArgumentCount - 2);
|
||||
if ($canCloseNow && $parserState->comes(')')) {
|
||||
break;
|
||||
}
|
||||
|
||||
// "Legacy" syntax is comma-delimited, and does not allow the `none` keyword.
|
||||
// "Modern" syntax is space-delimited, with `/` as alpha delimiter.
|
||||
// They cannot be mixed.
|
||||
if ($argumentIndex === 0 && !$containsNone) {
|
||||
// An immediate closing parenthesis is not valid.
|
||||
if ($parserState->comes(')')) {
|
||||
throw new UnexpectedTokenException(
|
||||
'Color function with no arguments',
|
||||
'',
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
$isLegacySyntax = $parserState->comes(',');
|
||||
}
|
||||
|
||||
if ($isLegacySyntax && $argumentIndex < ($expectedArgumentCount - 1)) {
|
||||
$parserState->consume(',');
|
||||
}
|
||||
|
||||
// In the "modern" syntax, the alpha value must be delimited with `/`.
|
||||
if (!$isLegacySyntax) {
|
||||
if ($containsVar) {
|
||||
// If the `var` substitution encompasses more than one argument,
|
||||
// the alpha deliminator may come at any time.
|
||||
if ($parserState->comes('/')) {
|
||||
$parserState->consume('/');
|
||||
}
|
||||
} elseif (($colorModeForParsing[$argumentIndex + 1] ?? '') === 'a') {
|
||||
// Alpha value is the next expected argument.
|
||||
// Since a closing parenthesis was not found, a `/` separator is now required.
|
||||
$parserState->consume('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
$parserState->consume(')');
|
||||
|
||||
return $containsVar
|
||||
? new CSSFunction($colorMode, \array_values($colorValues), ',', $parserState->currentLine())
|
||||
: new Color($colorValues, $parserState->currentLine());
|
||||
}
|
||||
|
||||
private static function mapRange(float $value, float $fromMin, float $fromMax, float $toMin, float $toMax): float
|
||||
{
|
||||
$fromRange = $fromMax - $fromMin;
|
||||
$toRange = $toMax - $toMin;
|
||||
$multiplier = $toRange / $fromRange;
|
||||
$newValue = $value - $fromMin;
|
||||
$newValue *= $multiplier;
|
||||
|
||||
return $newValue + $toMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, Value|string>
|
||||
*/
|
||||
public function getColor(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string, Value|string> $colorValues
|
||||
*/
|
||||
public function setColor(array $colorValues): void
|
||||
{
|
||||
$this->setName(\implode('', \array_keys($colorValues)));
|
||||
$this->components = $colorValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getColorDescription(): string
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
if ($this->shouldRenderAsHex($outputFormat)) {
|
||||
return $this->renderAsHex();
|
||||
}
|
||||
|
||||
if ($this->shouldRenderInModernSyntax()) {
|
||||
return $this->renderInModernSyntax($outputFormat);
|
||||
}
|
||||
|
||||
return parent::render($outputFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
|
||||
private function shouldRenderAsHex(OutputFormat $outputFormat): bool
|
||||
{
|
||||
return
|
||||
$outputFormat->usesRgbHashNotation()
|
||||
&& $this->getRealName() === 'rgb'
|
||||
&& $this->allComponentsAreNumbers();
|
||||
}
|
||||
|
||||
/**
|
||||
* The function name is a concatenation of the array keys of the components, which is passed to the constructor.
|
||||
* However, this can be changed by calling {@see CSSFunction::setName},
|
||||
* so is not reliable in situations where it's necessary to determine the function name based on the components.
|
||||
*/
|
||||
private function getRealName(): string
|
||||
{
|
||||
return \implode('', \array_keys($this->components));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether all color components are absolute numbers (CSS type `number`), not percentages or anything else.
|
||||
* If any component is not an instance of `Size`, the method will also return `false`.
|
||||
*/
|
||||
private function allComponentsAreNumbers(): bool
|
||||
{
|
||||
foreach ($this->components as $component) {
|
||||
if (!($component instanceof Size) || $component->getUnit() !== null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that this method assumes the following:
|
||||
* - The `components` array has keys for `r`, `g` and `b`;
|
||||
* - The values in the array are all instances of `Size`.
|
||||
*
|
||||
* Errors will be triggered or thrown if this is not the case.
|
||||
*
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function renderAsHex(): string
|
||||
{
|
||||
$result = \sprintf(
|
||||
'%02x%02x%02x',
|
||||
$this->components['r']->getSize(),
|
||||
$this->components['g']->getSize(),
|
||||
$this->components['b']->getSize()
|
||||
);
|
||||
$canUseShortVariant = ($result[0] === $result[1]) && ($result[2] === $result[3]) && ($result[4] === $result[5]);
|
||||
|
||||
return '#' . ($canUseShortVariant ? $result[0] . $result[2] . $result[4] : $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "legacy" syntax does not allow RGB colors to have a mixture of `percentage`s and `number`s,
|
||||
* and does not allow `none` as any component value.
|
||||
*
|
||||
* The "legacy" and "modern" monikers are part of the formal W3C syntax.
|
||||
* See the following for more information:
|
||||
* - {@link
|
||||
* https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb#formal_syntax
|
||||
* Description of the formal syntax for `rgb()` on MDN
|
||||
* };
|
||||
* - {@link
|
||||
* https://www.w3.org/TR/css-color-4/#rgb-functions
|
||||
* The same in the CSS Color Module Level 4 W3C Candidate Recommendation Draft
|
||||
* } (as of 13 February 2024, at time of writing).
|
||||
*/
|
||||
private function shouldRenderInModernSyntax(): bool
|
||||
{
|
||||
if ($this->hasNoneAsComponentValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->colorFunctionMayHaveMixedValueTypes($this->getRealName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasPercentage = false;
|
||||
$hasNumber = false;
|
||||
foreach ($this->components as $key => $value) {
|
||||
if ($key === 'a') {
|
||||
// Alpha can have units that don't match those of the RGB components in the "legacy" syntax.
|
||||
// So it is not necessary to check it. It's also always last, hence `break` rather than `continue`.
|
||||
break;
|
||||
}
|
||||
if (!($value instanceof Size)) {
|
||||
// Unexpected, unknown, or modified via the API
|
||||
return false;
|
||||
}
|
||||
$unit = $value->getUnit();
|
||||
// `switch` only does loose comparison
|
||||
if ($unit === null) {
|
||||
$hasNumber = true;
|
||||
} elseif ($unit === '%') {
|
||||
$hasPercentage = true;
|
||||
} else {
|
||||
// Invalid unit
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $hasPercentage && $hasNumber;
|
||||
}
|
||||
|
||||
private function hasNoneAsComponentValue(): bool
|
||||
{
|
||||
return \in_array('none', $this->components, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some color functions, such as `rgb`,
|
||||
* may have a mixture of `percentage`, `number`, or possibly other types in their arguments.
|
||||
*
|
||||
* Note that this excludes the alpha component, which is treated separately.
|
||||
*/
|
||||
private function colorFunctionMayHaveMixedValueTypes(string $function): bool
|
||||
{
|
||||
$functionsThatMayHaveMixedValueTypes = ['rgb', 'rgba'];
|
||||
|
||||
return \in_array($function, $functionsThatMayHaveMixedValueTypes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function renderInModernSyntax(OutputFormat $outputFormat): string
|
||||
{
|
||||
// Maybe not yet without alpha, but will be...
|
||||
$componentsWithoutAlpha = $this->components;
|
||||
\end($componentsWithoutAlpha);
|
||||
if (\key($componentsWithoutAlpha) === 'a') {
|
||||
$alpha = $this->components['a'];
|
||||
unset($componentsWithoutAlpha['a']);
|
||||
}
|
||||
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
$arguments = $formatter->implode(' ', $componentsWithoutAlpha);
|
||||
if (isset($alpha)) {
|
||||
$separator = $formatter->spaceBeforeListArgumentSeparator('/')
|
||||
. '/' . $formatter->spaceAfterListArgumentSeparator('/');
|
||||
$arguments = $formatter->implode($separator, [$arguments, $alpha]);
|
||||
}
|
||||
|
||||
return $this->getName() . '(' . $arguments . ')';
|
||||
}
|
||||
}
|
||||
69
vendor/sabberworm/php-css-parser/src/Value/LineName.php
vendored
Normal file
69
vendor/sabberworm/php-css-parser/src/Value/LineName.php
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
class LineName extends ValueList
|
||||
{
|
||||
/**
|
||||
* @param array<Value|string> $components
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(array $components = [], ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($components, ' ', $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState): LineName
|
||||
{
|
||||
$parserState->consume('[');
|
||||
$parserState->consumeWhiteSpace();
|
||||
$names = [];
|
||||
do {
|
||||
if ($parserState->getSettings()->usesLenientParsing()) {
|
||||
try {
|
||||
$names[] = $parserState->parseIdentifier();
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
if (!$parserState->comes(']')) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$names[] = $parserState->parseIdentifier();
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
} while (!$parserState->comes(']'));
|
||||
$parserState->consume(']');
|
||||
return new LineName($names, $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return '[' . parent::render(OutputFormat::createCompact()) . ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
throw new \BadMethodCallException('`getArrayRepresentation` is not yet implemented for `' . self::class . '`');
|
||||
}
|
||||
}
|
||||
7
vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php
vendored
Normal file
7
vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
abstract class PrimitiveValue extends Value {}
|
||||
22
vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php
vendored
Normal file
22
vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
/**
|
||||
* This class is 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).
|
||||
*/
|
||||
class RuleValueList extends ValueList
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $separator
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $separator = ',', ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct([], $separator, $lineNumber);
|
||||
}
|
||||
}
|
||||
223
vendor/sabberworm/php-css-parser/src/Value/Size.php
vendored
Normal file
223
vendor/sabberworm/php-css-parser/src/Value/Size.php
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
use function Safe\preg_match;
|
||||
use function Safe\preg_replace;
|
||||
|
||||
/**
|
||||
* A `Size` consists of a numeric `size` value and a unit.
|
||||
*/
|
||||
class Size extends PrimitiveValue
|
||||
{
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* vh/vw/vm(ax)/vmin/rem are absolute insofar as they don’t scale to the immediate parent (only the viewport)
|
||||
*/
|
||||
private const ABSOLUTE_SIZE_UNITS = [
|
||||
'px',
|
||||
'pt',
|
||||
'pc',
|
||||
'cm',
|
||||
'mm',
|
||||
'mozmm',
|
||||
'in',
|
||||
'vh',
|
||||
'dvh',
|
||||
'svh',
|
||||
'lvh',
|
||||
'vw',
|
||||
'vmin',
|
||||
'vmax',
|
||||
'rem',
|
||||
];
|
||||
|
||||
private const RELATIVE_SIZE_UNITS = ['%', 'em', 'ex', 'ch', 'fr'];
|
||||
|
||||
private const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turn', 'Hz', 'kHz'];
|
||||
|
||||
/**
|
||||
* @var array<int<1, max>, array<lowercase-string, non-empty-string>>|null
|
||||
*/
|
||||
private static $SIZE_UNITS = null;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $size;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $unit;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isColorComponent;
|
||||
|
||||
/**
|
||||
* @param float|int|string $size
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct($size, ?string $unit = null, bool $isColorComponent = false, ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
$this->size = (float) $size;
|
||||
$this->unit = $unit;
|
||||
$this->isColorComponent = $isColorComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $isColorComponent = false): Size
|
||||
{
|
||||
$size = '';
|
||||
if ($parserState->comes('-')) {
|
||||
$size .= $parserState->consume('-');
|
||||
}
|
||||
while (\is_numeric($parserState->peek()) || $parserState->comes('.') || $parserState->comes('e', true)) {
|
||||
if ($parserState->comes('.')) {
|
||||
$size .= $parserState->consume('.');
|
||||
} elseif ($parserState->comes('e', true)) {
|
||||
$lookahead = $parserState->peek(1, 1);
|
||||
if (\is_numeric($lookahead) || $lookahead === '+' || $lookahead === '-') {
|
||||
$size .= $parserState->consume(2);
|
||||
} else {
|
||||
break; // Reached the unit part of the number like "em" or "ex"
|
||||
}
|
||||
} else {
|
||||
$size .= $parserState->consume(1);
|
||||
}
|
||||
}
|
||||
|
||||
$unit = null;
|
||||
$sizeUnits = self::getSizeUnits();
|
||||
foreach ($sizeUnits as $length => &$values) {
|
||||
$key = \strtolower($parserState->peek($length));
|
||||
if (\array_key_exists($key, $values)) {
|
||||
if (($unit = $values[$key]) !== null) {
|
||||
$parserState->consume($length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Size((float) $size, $unit, $isColorComponent, $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int<1, max>, array<lowercase-string, non-empty-string>>
|
||||
*/
|
||||
private static function getSizeUnits(): array
|
||||
{
|
||||
if (!\is_array(self::$SIZE_UNITS)) {
|
||||
self::$SIZE_UNITS = [];
|
||||
$sizeUnits = \array_merge(self::ABSOLUTE_SIZE_UNITS, self::RELATIVE_SIZE_UNITS, self::NON_SIZE_UNITS);
|
||||
foreach ($sizeUnits as $sizeUnit) {
|
||||
$tokenLength = \strlen($sizeUnit);
|
||||
if (!isset(self::$SIZE_UNITS[$tokenLength])) {
|
||||
self::$SIZE_UNITS[$tokenLength] = [];
|
||||
}
|
||||
self::$SIZE_UNITS[$tokenLength][\strtolower($sizeUnit)] = $sizeUnit;
|
||||
}
|
||||
|
||||
\krsort(self::$SIZE_UNITS, SORT_NUMERIC);
|
||||
}
|
||||
|
||||
return self::$SIZE_UNITS;
|
||||
}
|
||||
|
||||
public function setUnit(string $unit): void
|
||||
{
|
||||
$this->unit = $unit;
|
||||
}
|
||||
|
||||
public function getUnit(): ?string
|
||||
{
|
||||
return $this->unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float|int|string $size
|
||||
*/
|
||||
public function setSize($size): void
|
||||
{
|
||||
$this->size = (float) $size;
|
||||
}
|
||||
|
||||
public function getSize(): float
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function isColorComponent(): bool
|
||||
{
|
||||
return $this->isColorComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the number stored in this Size really represents a size (as in a length of something on screen).
|
||||
*
|
||||
* Returns `false` if the unit is an angle, a duration, a frequency, or the number is a component in a `Color`
|
||||
* object.
|
||||
*/
|
||||
public function isSize(): bool
|
||||
{
|
||||
if (\in_array($this->unit, self::NON_SIZE_UNITS, true)) {
|
||||
return false;
|
||||
}
|
||||
return !$this->isColorComponent();
|
||||
}
|
||||
|
||||
public function isRelative(): bool
|
||||
{
|
||||
if (\in_array($this->unit, self::RELATIVE_SIZE_UNITS, true)) {
|
||||
return true;
|
||||
}
|
||||
if ($this->unit === null && $this->size !== 0.0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$locale = \localeconv();
|
||||
$decimalPoint = \preg_quote($locale['decimal_point'], '/');
|
||||
$size = preg_match('/[\\d\\.]+e[+-]?\\d+/i', (string) $this->size) === 1
|
||||
? preg_replace("/$decimalPoint?0+$/", '', \sprintf('%f', $this->size)) : (string) $this->size;
|
||||
|
||||
return preg_replace(["/$decimalPoint/", '/^(-?)0\\./'], ['.', '$1.'], $size) . ($this->unit ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
// 'number' is the official W3C terminology (not 'size')
|
||||
'number' => $this->size,
|
||||
'unit' => $this->unit,
|
||||
];
|
||||
}
|
||||
}
|
||||
101
vendor/sabberworm/php-css-parser/src/Value/URL.php
vendored
Normal file
101
vendor/sabberworm/php-css-parser/src/Value/URL.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* This class represents URLs in CSS. `URL`s always output in `URL("")` notation.
|
||||
*/
|
||||
class URL extends PrimitiveValue
|
||||
{
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @var CSSString
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(CSSString $url, ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState): URL
|
||||
{
|
||||
$anchor = $parserState->anchor();
|
||||
$identifier = '';
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$character = $parserState->parseCharacter(true);
|
||||
if ($character === null) {
|
||||
break;
|
||||
}
|
||||
$identifier .= $character;
|
||||
}
|
||||
$useUrl = $parserState->streql($identifier, 'url');
|
||||
if ($useUrl) {
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consume('(');
|
||||
} else {
|
||||
$anchor->backtrack();
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
$result = new URL(CSSString::parse($parserState), $parserState->currentLine());
|
||||
if ($useUrl) {
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consume(')');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setURL(CSSString $url): void
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function getURL(): CSSString
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return "url({$this->url->render($outputFormat)})";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
// We're using the term "uri" here to match the wording used in the specs:
|
||||
// https://www.w3.org/TR/CSS22/syndata.html#uri
|
||||
'uri' => $this->url->getArrayRepresentation(),
|
||||
];
|
||||
}
|
||||
}
|
||||
229
vendor/sabberworm/php-css-parser/src/Value/Value.php
vendored
Normal file
229
vendor/sabberworm/php-css-parser/src/Value/Value.php
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\CSSElement;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
use function Safe\preg_match;
|
||||
|
||||
/**
|
||||
* Abstract base class for specific classes of CSS values: `Size`, `Color`, `CSSString` and `URL`, and another
|
||||
* abstract subclass `ValueList`.
|
||||
*/
|
||||
abstract class Value implements CSSElement, Positionable
|
||||
{
|
||||
use Position;
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(?int $lineNumber = null)
|
||||
{
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string> $listDelimiters
|
||||
*
|
||||
* @return Value|string
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parseValue(ParserState $parserState, array $listDelimiters = [])
|
||||
{
|
||||
/** @var list<Value|string> $stack */
|
||||
$stack = [];
|
||||
$parserState->consumeWhiteSpace();
|
||||
//Build a list of delimiters and parsed values
|
||||
while (
|
||||
!($parserState->comes('}') || $parserState->comes(';') || $parserState->comes('!')
|
||||
|| $parserState->comes(')')
|
||||
|| $parserState->isEnd())
|
||||
) {
|
||||
if (\count($stack) > 0) {
|
||||
$foundDelimiter = false;
|
||||
foreach ($listDelimiters as $delimiter) {
|
||||
if ($parserState->comes($delimiter)) {
|
||||
\array_push($stack, $parserState->consume($delimiter));
|
||||
$parserState->consumeWhiteSpace();
|
||||
$foundDelimiter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$foundDelimiter) {
|
||||
//Whitespace was the list delimiter
|
||||
\array_push($stack, ' ');
|
||||
}
|
||||
}
|
||||
\array_push($stack, self::parsePrimitiveValue($parserState));
|
||||
$parserState->consumeWhiteSpace();
|
||||
}
|
||||
// Convert the list to list objects
|
||||
foreach ($listDelimiters as $delimiter) {
|
||||
$stackSize = \count($stack);
|
||||
if ($stackSize === 1) {
|
||||
return $stack[0];
|
||||
}
|
||||
$newStack = [];
|
||||
for ($offset = 0; $offset < $stackSize; ++$offset) {
|
||||
if ($offset === ($stackSize - 1) || $delimiter !== $stack[$offset + 1]) {
|
||||
$newStack[] = $stack[$offset];
|
||||
continue;
|
||||
}
|
||||
$length = 2; //Number of elements to be joined
|
||||
for ($i = $offset + 3; $i < $stackSize; $i += 2, ++$length) {
|
||||
if ($delimiter !== $stack[$i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$list = new RuleValueList($delimiter, $parserState->currentLine());
|
||||
for ($i = $offset; $i - $offset < $length * 2; $i += 2) {
|
||||
$list->addListComponent($stack[$i]);
|
||||
}
|
||||
$newStack[] = $list;
|
||||
$offset += $length * 2 - 2;
|
||||
}
|
||||
$stack = $newStack;
|
||||
}
|
||||
if (!isset($stack[0])) {
|
||||
throw new UnexpectedTokenException(
|
||||
" {$parserState->peek()} ",
|
||||
$parserState->peek(1, -1) . $parserState->peek(2),
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
return $stack[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSSFunction|string
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parseIdentifierOrFunction(ParserState $parserState, bool $ignoreCase = false)
|
||||
{
|
||||
$anchor = $parserState->anchor();
|
||||
$result = $parserState->parseIdentifier($ignoreCase);
|
||||
|
||||
if ($parserState->comes('(')) {
|
||||
$anchor->backtrack();
|
||||
if ($parserState->streql('url', $result)) {
|
||||
$result = URL::parse($parserState);
|
||||
} elseif ($parserState->streql('calc', $result)) {
|
||||
$result = CalcFunction::parse($parserState);
|
||||
} else {
|
||||
$result = CSSFunction::parse($parserState, $ignoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSSFunction|CSSString|LineName|Size|URL|string
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws SourceException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parsePrimitiveValue(ParserState $parserState)
|
||||
{
|
||||
$value = null;
|
||||
$parserState->consumeWhiteSpace();
|
||||
if (
|
||||
\is_numeric($parserState->peek())
|
||||
|| ($parserState->comes('-.')
|
||||
&& \is_numeric($parserState->peek(1, 2)))
|
||||
|| (($parserState->comes('-') || $parserState->comes('.')) && \is_numeric($parserState->peek(1, 1)))
|
||||
) {
|
||||
$value = Size::parse($parserState);
|
||||
} elseif ($parserState->comes('#') || $parserState->comes('rgb', true) || $parserState->comes('hsl', true)) {
|
||||
$value = Color::parse($parserState);
|
||||
} elseif ($parserState->comes("'") || $parserState->comes('"')) {
|
||||
$value = CSSString::parse($parserState);
|
||||
} elseif ($parserState->comes('progid:') && $parserState->getSettings()->usesLenientParsing()) {
|
||||
$value = self::parseMicrosoftFilter($parserState);
|
||||
} elseif ($parserState->comes('[')) {
|
||||
$value = LineName::parse($parserState);
|
||||
} elseif ($parserState->comes('U+')) {
|
||||
$value = self::parseUnicodeRangeValue($parserState);
|
||||
} else {
|
||||
$nextCharacter = $parserState->peek(1);
|
||||
try {
|
||||
$value = self::parseIdentifierOrFunction($parserState);
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
if (\in_array($nextCharacter, ['+', '-', '*', '/'], true)) {
|
||||
$value = $parserState->consume(1);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseMicrosoftFilter(ParserState $parserState): CSSFunction
|
||||
{
|
||||
$function = $parserState->consumeUntil('(', false, true);
|
||||
$arguments = Value::parseValue($parserState, [',', '=']);
|
||||
return new CSSFunction($function, $arguments, ',', $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseUnicodeRangeValue(ParserState $parserState): string
|
||||
{
|
||||
$codepointMaxLength = 6; // Code points outside BMP can use up to six digits
|
||||
$range = '';
|
||||
$parserState->consume('U+');
|
||||
do {
|
||||
if ($parserState->comes('-')) {
|
||||
$codepointMaxLength = 13; // Max length is 2 six-digit code points + the dash(-) between them
|
||||
}
|
||||
$range .= $parserState->consume(1);
|
||||
} while (
|
||||
(\strlen($range) < $codepointMaxLength) && (preg_match('/[A-Fa-f0-9\\?-]/', $parserState->peek()) === 1)
|
||||
);
|
||||
|
||||
return "U+{$range}";
|
||||
}
|
||||
}
|
||||
124
vendor/sabberworm/php-css-parser/src/Value/ValueList.php
vendored
Normal file
124
vendor/sabberworm/php-css-parser/src/Value/ValueList.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* A `ValueList` represents a lists of `Value`s, separated by some separation character
|
||||
* (mostly `,`, whitespace, or `/`).
|
||||
*
|
||||
* There are two types of `ValueList`s: `RuleValueList` and `CSSFunction`
|
||||
*/
|
||||
abstract class ValueList extends Value
|
||||
{
|
||||
use ShortClassNameProvider;
|
||||
|
||||
/**
|
||||
* @var array<Value|string>
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $components;
|
||||
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $separator;
|
||||
|
||||
/**
|
||||
* @param array<Value|string>|Value|string $components
|
||||
* @param non-empty-string $separator
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct($components = [], $separator = ',', ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
if (!\is_array($components)) {
|
||||
$components = [$components];
|
||||
}
|
||||
$this->components = $components;
|
||||
$this->separator = $separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Value|string $component
|
||||
*/
|
||||
public function addListComponent($component): void
|
||||
{
|
||||
$this->components[] = $component;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Value|string>
|
||||
*/
|
||||
public function getListComponents(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Value|string> $components
|
||||
*/
|
||||
public function setListComponents(array $components): void
|
||||
{
|
||||
$this->components = $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getListSeparator(): string
|
||||
{
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $separator
|
||||
*/
|
||||
public function setListSeparator(string $separator): void
|
||||
{
|
||||
$this->separator = $separator;
|
||||
}
|
||||
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
|
||||
return $formatter->implode(
|
||||
$formatter->spaceBeforeListArgumentSeparator($this->separator) . $this->separator
|
||||
. $formatter->spaceAfterListArgumentSeparator($this->separator),
|
||||
$this->components
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, bool|int|float|string|array<mixed>|null>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getArrayRepresentation(): array
|
||||
{
|
||||
return [
|
||||
'class' => $this->getShortClassName(),
|
||||
'components' => \array_map(
|
||||
/**
|
||||
* @parm Value|string $component
|
||||
*/
|
||||
function ($component): array {
|
||||
if (\is_string($component)) {
|
||||
return ['class' => 'string', 'value' => $component];
|
||||
}
|
||||
return $component->getArrayRepresentation();
|
||||
},
|
||||
$this->components
|
||||
),
|
||||
'separator' => $this->separator,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user