prestashop / php-css-parser
用PHP编写的CSS文件解析器
Requires
- php: >=5.3.2
Requires (Dev)
- phpunit/phpunit: ~4.8
README
PHP编写的CSS文件解析器。允许将CSS文件提取到数据结构中,操作此结构并输出为(优化后的)CSS。
用法
使用Composer安装
将php-css-parser添加到composer.json中
{ "require": { "sabberworm/php-css-parser": "*" } }
提取
要使用CSS解析器,创建一个新的实例。构造函数采用以下形式
new Sabberworm\CSS\Parser($sText);
例如,要读取文件,可以这样做
$oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css')); $oCssDocument = $oCssParser->parse();
在输出之前,可以操作生成的CSS文档结构。
选项
字符集
只有在CSS文件中没有找到@charset声明时,才使用charset选项。UTF-8是默认值,所以如果您不打算更改该值,则无需创建设置对象。
$oSettings = Sabberworm\CSS\Settings::create()->withDefaultCharset('windows-1252'); new Sabberworm\CSS\Parser($sText, $oSettings);
严格解析
要使解析器对无效规则产生错误,请提供以下配置的Sabberworm\CSS\Settings对象
$oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css'), Sabberworm\CSS\Settings::create()->beStrict());
禁用多字节函数
为了获得更快的解析速度,您可以选择让PHP-CSS-Parser使用常规字符串函数而不是mb_*
函数。在大多数情况下,这应该没问题,即使是UTF-8文件,因为所有多字节字符都在字符串字面量中。但是,不建议您使用您无法控制的输入,因为它们没有经过充分的测试案例覆盖。
$oSettings = Sabberworm\CSS\Settings::create()->withMultibyteSupport(false); new Sabberworm\CSS\Parser($sText, $oSettings);
操作
生成的数据结构主要由五种基本类型组成:CSSList
、RuleSet
、Rule
、Selector
和Value
。还有两种额外的类型被使用:Import
和Charset
,您不太经常使用。
CSSList
CSSList
代表一个通用的CSS容器,最可能包含声明块(具有选择器的规则集),但它也可能包含@规则、字符集声明等。CSSList
有以下具体子类型
Document
– 代表CSS文件的根。MediaQuery
– 代表CSSList的一个子部分,仅适用于匹配包含媒体查询的输出设备。
要访问存储在CSSList
中的项(例如,在调用$oCssParser->parse()
时返回的文档),请使用getContents()
,然后遍历该集合,并使用instanceof检查您是否正在处理另一个CSSList
、RuleSet
、Import
或Charset
。
要将新项(选择器、媒体查询等)追加到现有的CSSList
中,请使用该类的构造函数构建它,并使用append($oItem)
方法。
规则集
规则集
是单个规则的容器。规则集最常见的形式是由选择器约束的。以下是一些具体的子类型:
AtRuleSet
– 用于通用的 at 规则,不匹配特定提到的规则,如 @import、@charset 或 @media。一个常见的例子是 @font-face。DeclarationBlock
– 一个由选择器
约束的规则集;包含一个选择器对象数组(在 CSS 中以逗号分隔)以及应用于匹配元素的规则。
注意:一个 CSSList
可以包含其他 CSSList
(以及 Import
和 Charset
),而一个 RuleSet
只能包含 Rule
。
如果您想操作一个 RuleSet
,请使用方法 addRule(Rule $oRule)
、getRules()
和 removeRule($mRule)
(它接受一个 Rule 实例或规则名;可选地以连字符结尾以删除所有相关规则)。
规则
规则
只有一个键(规则)和一个值。这些值都是 Value
的实例。
值
值
是一个抽象类,仅定义了 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()
– 在规则中查找所有Value
对象。
待办事项
- 更多的便利方法[如
selectorsWithElement($sId/Class/TagName)
、attributesOfType($sType)
、removeAttributesOfType($sType)
] - 真正的多字节支持。目前只支持那些前 255 个代码点只占用一个字节且与 ASCII 相同的多字节字符集(是的,UTF-8 符合这个描述)。
- 命名颜色支持(使用
Color
而不是匿名字符串字面量)
用例
使用 Parser
将 id 预先添加到所有选择器中
$sMyId = "#my_id"; $oParser = new Sabberworm\CSS\Parser($sText); $oCss = $oParser->parse(); foreach($oCss->getAllDeclarationBlocks() as $oBlock) { foreach($oBlock->getSelectors() as $oSelector) { //Loop over all selector parts (the comma-separated strings in a selector) and prepend the id $oSelector->setSelector($sMyId.' '.$oSelector->getSelector()); } }
将所有绝对大小缩小一半
$oParser = new Sabberworm\CSS\Parser($sText); $oCss = $oParser->parse(); foreach($oCss->getAllValues() as $mValue) { if($mValue instanceof CSSSize && !$mValue->isRelative()) { $mValue->setSize($mValue->getSize()/2); } }
删除不需要的规则
$oParser = new Sabberworm\CSS\Parser($sText); $oCss = $oParser->parse(); foreach($oCss->getAllRuleSets() as $oRuleSet) { $oRuleSet->removeRule('font-'); //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('cursor'); }
输出
要将整个 CSS 文档输出到变量中,只需使用 ->render()
$oCssParser = new Sabberworm\CSS\Parser(file_get_contents('somefile.css')); $oCssDocument = $oCssParser->parse(); print $oCssDocument->render();
如果您想格式化输出,请传递一个 Sabberworm\CSS\OutputFormat
类型的实例
$oFormat = Sabberworm\CSS\OutputFormat::create()->indentWithSpaces(4)->setSpaceBetweenRules("\n"); print $oCssDocument->render($oFormat);
或者使用预定义的格式之一
print $oCssDocument->render(Sabberworm\CSS\OutputFormat::createPretty()); print $oCssDocument->render(Sabberworm\CSS\OutputFormat::createCompact());
要查看输出格式化的使用方法,请查看 tests/Sabberworm/CSS/OutputFormatTest.php
中的测试。
示例
示例 1(At-Rules)
输入
@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;}
贡献者/感谢
- FMCorz 对许多补丁和建议,能够以宽松模式解析注释和 IE 修复。
- Lullabot 提供了一个补丁,可以知道每个解析标记的行号。
- ju1ius 提供了特定性解析代码和扩展/压缩缩写属性的能力。
- ossinkine 提供了 150 倍的性能提升。
- GaryJones 提供了大量输入和 http://css-specificity.info/。
- docteurklein 提供了输出格式化和
CSSList->remove()
启发。 - nicolopignatelli 提供了 PSR-0 兼容性。
- diegoembarcadero 提供了关键帧 at 规则解析。
- goetas 提供了对 @namespace at 规则的支持。
- 查看完整列表
杂项
- 遗留支持:此项目的最新预 PSR-0 版本可以通过
0.9.0
标签进行检查。 - 运行测试:要运行此项目的所有单元测试,请运行
composer install
安装 phpunit,然后使用./vendor/phpunit/phpunit/phpunit
。
许可证
PHP-CSS-Parser 在 MIT 风格许可证的条款下免费分发。
版权所有(C)2011 Raphael Schweikert,http://sabberworm.com/
特此授予任何获得本软件及其相关文档副本(以下简称“软件”)的人士,在不受限制的情况下使用软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,并允许软件提供者进行此类操作,前提是遵守以下条件:
上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。
软件按“原样”提供,不提供任何形式的保证,无论是明示的还是暗示的,包括但不限于适销性、特定用途的适用性和非侵权性保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任负责,无论此类索赔、损害或其他责任是基于合同、侵权或其他方式,以及与软件或软件的使用或其他操作有关。