sabberworm / php-css-parser
PHP编写的CSS文件解析器
Requires
- php: >=5.6.20
- ext-iconv: *
Requires (Dev)
- phpunit/phpunit: ^5.7.27
Suggests
- ext-mbstring: for parsing UTF-8 CSS
- dev-main / 9.0.x-dev
- v8.x-dev
- v8.6.0
- v8.5.x-dev
- v8.5.2
- v8.5.1
- 8.5.0
- 8.4.0
- 8.3.1
- 8.3.0
- 8.2.1
- 8.2.0
- 8.1.1
- 8.1.0
- 8.0.1
- 8.0.0
- 7.0.4
- 7.0.3
- 7.0.2
- 7.0.1
- 7.0.0
- 6.0.2
- 6.0.1
- 6.0.0
- 5.2.1
- 5.2.0
- 5.1.3
- 5.1.2
- 5.1.1
- 5.1.0
- 5.0.9
- 5.0.8
- 5.0.7
- 5.0.6
- 5.0.5
- 5.0.4
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 4.0.1
- 4.0.0
- 3.0.1
- 3.0.0
- 2.0.1
- 2.0.0
- 1.0.1
- 1.0.0
- dev-task/fixed-dev-dep-versions
- dev-task/no-hungarian-6
- dev-backport/update-phpunit
- dev-dependabot/composer/phpstan/phpstan-1.12.4
- dev-backport/docs/version-numbers
- dev-docs/version-numbers
- dev-docs/api-deprecation-policy
- dev-feature/automatic-mermaid
- dev-task/phpstan-level-2
- dev-cleanup/declaration-block-test
- dev-bugfix/style-value-strings
- dev-fix-set-get-charset
- dev-selector-parsing
- dev-codacy
- dev-refactor-parsing-logic
This package is auto-updated.
Last update: 2024-09-20 16:56:04 UTC
README
PHP编写的CSS文件解析器。可以将CSS文件提取到数据结构中,操作该结构,并以(优化后)CSS的形式输出。
使用方法
使用Composer安装
composer require sabberworm/php-css-parser
提取
要使用CSS解析器,创建一个新的实例。构造函数具有以下形式
new \Sabberworm\CSS\Parser($css);
例如,要读取一个文件,你可以这样做
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); $cssDocument = $parser->parse();
在输出之前,可以操作生成的CSS文档结构。
选项
字符集
如果CSS文件不包含@charset
声明,将使用字符集选项。默认为UTF-8,因此如果你不打算更改,根本不需要创建设置对象。
$settings = \Sabberworm\CSS\Settings::create() ->withDefaultCharset('windows-1252'); $parser = new \Sabberworm\CSS\Parser($css, $settings);
严格解析
要让解析器在遇到无效/未知构造时抛出异常(而不是尝试忽略它们并继续解析),提供如下配置的\Sabberworm\CSS\Settings
对象
$parser = new \Sabberworm\CSS\Parser( file_get_contents('somefile.css'), \Sabberworm\CSS\Settings::create()->beStrict() );
请注意,这也会禁用解析旧版IE特定filter
规则的未引号变体的工作区。
禁用多字节函数
为了实现更快的解析,你可以选择让PHP-CSS-Parser使用常规字符串函数而不是mb_*
函数。在大多数情况下,这应该可以正常工作,即使是UTF-8文件,因为所有多字节字符都在字符串字面量中。仍然不建议使用此功能处理你无法控制的输入,因为它没有经过充分的测试。
$settings = \Sabberworm\CSS\Settings::create()->withMultibyteSupport(false); $parser = new \Sabberworm\CSS\Parser($css, $settings);
操作
生成的数据结构主要由五种基本类型组成:CSSList
、RuleSet
、Rule
、Selector
和Value
。还有两种额外的类型:Import
和Charset
,你很少会用到。
CSSList
CSSList
代表一个通用的CSS容器,很可能包含声明块(具有选择器的规则集),但它也可能包含at规则、字符集声明等。
要访问存储在CSSList
中的项目(例如,当你调用$parser->parse()
时返回的文档),使用getContents()
,然后遍历该集合,并使用instanceof
检查你是否正在处理另一个CSSList
、RuleSet
、Import
或Charset
。
要将新项目(选择器、媒体查询等)附加到现有的CSSList
中,使用该类的构造函数构建它,并使用append($oItem)
方法。
RuleSet
RuleSet
是单个规则的容器。规则集最常见的形式是由选择器约束的。以下是一些具体的子类型
AtRuleSet
- 用于通用的at规则,这些规则未由特定类覆盖,即不是@import
、@charset
或@media
。一个常见的例子是@font-face
。DeclarationBlock
- 由Selector
约束的RuleSet
;包含一个选择器对象数组(在CSS中以逗号分隔)以及应用于匹配元素的规则。
注意:一个CSSList
可以包含其他CSSList
(以及Import
和Charset
),而一个RuleSet
只能包含Rule
。
如果你要操作RuleSet
,请使用方法addRule(Rule $rule)
、getRules()
和removeRule($rule)
(它接受一个Rule
或规则名;可选地以短横线结尾以删除所有相关规则)。
Rule
规则
只有一个字符串键(规则)和一个值
。
值
值
是一个抽象类,它只定义了render
方法。原子值类型的具体子类包括:
Size
– 包含一个数值size
和一个单位。Color
– 颜色可以以#rrggbb、#rgb或schema(val1, val2, …)的形式输入,但始终以('s' => val1, 'c' => val2, 'h' => val3, …)的数组形式存储,并以第二种形式输出。CSSString
– 这只是一个用于区分关键字的引号字符串的包装器;始终以双引号输出。URL
– CSS中的URL;始终以URL("")
表示法输出。
还有一个Value
的抽象子类,ValueList
:一个ValueList
表示一系列Value
,由某些分隔符(通常是,
、空白或/
)分隔。
存在两种类型的ValueList
:
RuleValueList
– 默认类型,用于表示所有多值规则,如font: bold 12px/3 Helvetica, Verdana, sans-serif;
(其中值将是一个空格分隔的基本值bold
的列表,一个斜杠分隔的列表和一个逗号分隔的列表)。CSSFunction
– 一种包含函数名和值是函数参数的特殊类型值。还处理像filter: alpha(opacity=90);
这样的等号分隔的参数列表。
便利方法
在Document
上有一些便利方法,可以简化查找、操作和删除规则
getAllDeclarationBlocks()
– 如其名所示;无论选择器嵌套有多深。别名getAllSelectors()
。getAllRuleSets()
– 如其名所示;无论规则集嵌套有多深。getAllValues()
– 在Rule
中查找所有Value
对象。
待办事项
- 更多便利方法(如
selectorsWithElement($sId/Class/TagName)
、attributesOfType($type)
、removeAttributesOfType($type)
) - 真正的多字节支持。目前,只有首255个代码点只占用一个字节且与ASCII相同的单字节多字节字符集才受支持(是的,UTF-8符合这个描述)。
- 命名颜色支持(使用
Color
而不是匿名字符串字面量)
用例
使用Parser
将ID预置于所有选择器中
$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()); } }
将所有绝对大小缩小一半
$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); } }
删除不需要的规则
$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'); }
输出
要将整个CSS文档输出到变量中,只需使用->render()
$parser = new \Sabberworm\CSS\Parser(file_get_contents('somefile.css')); $cssDocument = $parser->parse(); print $cssDocument->render();
如果您想格式化输出,请传递一个类型为\Sabberworm\CSS\OutputFormat
的实例
$format = \Sabberworm\CSS\OutputFormat::create() ->indentWithSpaces(4)->setSpaceBetweenRules("\n"); print $cssDocument->render($format);
或使用预定义的格式之一
print $cssDocument->render(Sabberworm\CSS\OutputFormat::createPretty()); print $cssDocument->render(Sabberworm\CSS\OutputFormat::createCompact());
要了解输出格式化可以做什么,请查看tests/OutputFormatTest.php
中的测试。
示例
示例1(At规则)
输入
@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; } }
结构(《var_dump()》)
class Sabberworm\CSS\CSSList\Document#4 (2) { protected $aContents => array(4) { [0] => class Sabberworm\CSS\Property\Charset#6 (2) { private $sCharset => class Sabberworm\CSS\Value\CSSString#5 (2) { private $sString => string(5) "utf-8" protected $iLineNo => int(1) } protected $iLineNo => int(1) } [1] => class Sabberworm\CSS\RuleSet\AtRuleSet#7 (4) { private $sType => string(9) "font-face" private $sArgs => string(0) "" private $aRules => array(2) { 'font-family' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#8 (4) { private $sRule => string(11) "font-family" private $mValue => class Sabberworm\CSS\Value\CSSString#9 (2) { private $sString => string(10) "CrassRoots" protected $iLineNo => int(4) } private $bIsImportant => bool(false) protected $iLineNo => int(4) } } 'src' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#10 (4) { private $sRule => string(3) "src" private $mValue => class Sabberworm\CSS\Value\URL#11 (2) { private $oURL => class Sabberworm\CSS\Value\CSSString#12 (2) { private $sString => string(15) "../media/cr.ttf" protected $iLineNo => int(5) } protected $iLineNo => int(5) } private $bIsImportant => bool(false) protected $iLineNo => int(5) } } } protected $iLineNo => int(3) } [2] => class Sabberworm\CSS\RuleSet\DeclarationBlock#13 (3) { private $aSelectors => array(2) { [0] => class Sabberworm\CSS\Property\Selector#14 (2) { private $sSelector => string(4) "html" private $iSpecificity => NULL } [1] => class Sabberworm\CSS\Property\Selector#15 (2) { private $sSelector => string(4) "body" private $iSpecificity => NULL } } private $aRules => array(1) { 'font-size' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#16 (4) { private $sRule => string(9) "font-size" private $mValue => class Sabberworm\CSS\Value\Size#17 (4) { private $fSize => double(1.6) private $sUnit => string(2) "em" private $bIsColorComponent => bool(false) protected $iLineNo => int(9) } private $bIsImportant => bool(false) protected $iLineNo => int(9) } } } protected $iLineNo => int(8) } [3] => class Sabberworm\CSS\CSSList\KeyFrame#18 (4) { private $vendorKeyFrame => string(9) "keyframes" private $animationName => string(6) "mymove" protected $aContents => array(2) { [0] => class Sabberworm\CSS\RuleSet\DeclarationBlock#19 (3) { private $aSelectors => array(1) { [0] => class Sabberworm\CSS\Property\Selector#20 (2) { private $sSelector => string(4) "from" private $iSpecificity => NULL } } private $aRules => array(1) { 'top' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#21 (4) { private $sRule => string(3) "top" private $mValue => class Sabberworm\CSS\Value\Size#22 (4) { private $fSize => double(0) private $sUnit => string(2) "px" private $bIsColorComponent => bool(false) protected $iLineNo => int(13) } private $bIsImportant => bool(false) protected $iLineNo => int(13) } } } protected $iLineNo => int(13) } [1] => class Sabberworm\CSS\RuleSet\DeclarationBlock#23 (3) { private $aSelectors => array(1) { [0] => class Sabberworm\CSS\Property\Selector#24 (2) { private $sSelector => string(2) "to" private $iSpecificity => NULL } } private $aRules => array(1) { 'top' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#25 (4) { private $sRule => string(3) "top" private $mValue => class Sabberworm\CSS\Value\Size#26 (4) { private $fSize => double(200) private $sUnit => string(2) "px" private $bIsColorComponent => bool(false) protected $iLineNo => int(14) } private $bIsImportant => bool(false) protected $iLineNo => int(14) } } } protected $iLineNo => int(14) } } protected $iLineNo => int(12) } } protected $iLineNo => int(1) }
输出(《render()》)
@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;}}
示例2(值)
输入
#header { margin: 10px 2em 1cm 2%; font-family: Verdana, Helvetica, "Gill Sans", sans-serif; color: red !important; }
结构(《var_dump()》)
class Sabberworm\CSS\CSSList\Document#4 (2) { protected $aContents => array(1) { [0] => class Sabberworm\CSS\RuleSet\DeclarationBlock#5 (3) { private $aSelectors => array(1) { [0] => class Sabberworm\CSS\Property\Selector#6 (2) { private $sSelector => string(7) "#header" private $iSpecificity => NULL } } private $aRules => array(3) { 'margin' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#7 (4) { private $sRule => string(6) "margin" private $mValue => class Sabberworm\CSS\Value\RuleValueList#12 (3) { protected $aComponents => array(4) { [0] => class Sabberworm\CSS\Value\Size#8 (4) { private $fSize => double(10) private $sUnit => string(2) "px" private $bIsColorComponent => bool(false) protected $iLineNo => int(2) } [1] => class Sabberworm\CSS\Value\Size#9 (4) { private $fSize => double(2) private $sUnit => string(2) "em" private $bIsColorComponent => bool(false) protected $iLineNo => int(2) } [2] => class Sabberworm\CSS\Value\Size#10 (4) { private $fSize => double(1) private $sUnit => string(2) "cm" private $bIsColorComponent => bool(false) protected $iLineNo => int(2) } [3] => class Sabberworm\CSS\Value\Size#11 (4) { private $fSize => double(2) private $sUnit => string(1) "%" private $bIsColorComponent => bool(false) protected $iLineNo => int(2) } } protected $sSeparator => string(1) " " protected $iLineNo => int(2) } private $bIsImportant => bool(false) protected $iLineNo => int(2) } } 'font-family' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#13 (4) { private $sRule => string(11) "font-family" private $mValue => class Sabberworm\CSS\Value\RuleValueList#15 (3) { protected $aComponents => array(4) { [0] => string(7) "Verdana" [1] => string(9) "Helvetica" [2] => class Sabberworm\CSS\Value\CSSString#14 (2) { private $sString => string(9) "Gill Sans" protected $iLineNo => int(3) } [3] => string(10) "sans-serif" } protected $sSeparator => string(1) "," protected $iLineNo => int(3) } private $bIsImportant => bool(false) protected $iLineNo => int(3) } } 'color' => array(1) { [0] => class Sabberworm\CSS\Rule\Rule#16 (4) { private $sRule => string(5) "color" private $mValue => string(3) "red" private $bIsImportant => bool(true) protected $iLineNo => int(4) } } } protected $iLineNo => int(1) } } protected $iLineNo => int(1) }
输出(《render()》)
#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;color: red !important;}
类图
API和弃用策略
请参阅我们的API和弃用策略。
贡献
以错误报告、功能请求或拉取请求形式进行的贡献非常欢迎。🙏请参阅我们的贡献指南,了解如何为PHP-CSS-Parser做出贡献。
贡献者/感谢
- oliverklee,因大量重构、代码现代化和CI集成
- raxbg,为解析
calc
、网格线和各种错误修复做出了贡献。 - westonruter,为错误修复和改进做出了贡献。
- FMCorz,为许多补丁和建议做出了贡献,使得能够解析注释和IE技巧(在宽松模式下)。
- Lullabot 提供了一个补丁,允许知道每个解析标记的行号。
- ju1ius 提供了特定性解析代码和展开/压缩缩写属性的能力。
- ossinkine 提供了150倍的性能提升。
- GaryJones 提供了大量输入和 https://css-specificity.info/。
- docteurklein 提供了输出格式化和
CSSList->remove()
的灵感。 - nicolopignatelli 提供了PSR-0兼容性。
- diegoembarcadero 提供了关键帧at规则解析。
- goetas 提供了对
@namespace
at规则的支持。 - ziegenberg 提供了一般维护和清理。
- 查看完整列表
杂项
遗留支持
此项目的最新预PSR-0版本可以通过 0.9.0
标签检查。
运行测试
要为此项目运行所有持续集成(CI)检查(包括单元测试),
- 运行
composer install
以安装由Composer管理的开发依赖项; - 运行
phive install
以安装由PHIVE管理的开发依赖项; - 运行
composer ci
以运行所有静态和动态CI检查。
有关其他Composer脚本的详细信息(例如,运行特定的CI检查)可通过 composer list
提供。