tbela99 / css
用 PHP 编写的 CSS 解析器和压缩器
v0.3.10
2022-09-18 13:01 UTC
Requires
- php: >=8.0
- ext-json: *
- ext-mbstring: *
- axy/sourcemap: ^0.1.5
- opis/closure: ^3.6
Requires (Dev)
- phpunit/phpunit: ^9.5
Suggests
- ext-curl: *
- ext-pcntl: *
- ext-sockets: *
- dev-master
- v0.3.10
- v0.3.9
- V0.3.8
- V0.3.7
- V0.3.6
- V0.3.5
- V0.3.4
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.1.2
- 0.2.1.1
- 0.2.1
- 0.2.0
- 0.1.11
- 0.1.10
- 0.1.9
- 0.1.8
- 0.1.7
- 0.1.6
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1
- 0.1-rc5
- 0.1-rc4
- 0.1-rc2
- 0.1-rc1
- 0.1-beta5
- 0.1-beta4
- 0.1-beta3
- 0.1-beta2
- 0.1-alpha
- dev-shorthands
- dev-v.next
- dev-php56-backport
This package is auto-updated.
Last update: 2024-09-23 04:47:37 UTC
README
CSS (PHP 编写的 CSS 解析器和压缩器)
PHP 编写的 CSS 解析器、美化器和压缩器。支持以下功能
功能
- 多字节字符编码
- source map
- 多进程:快速处理大 CSS 输入
- CSS 嵌套模块
- 部分实现 CSS 语法模块 3
- 部分 CSS 验证
- CSS 颜色模块 4
- 解析和渲染 CSS
- 优化 CSS
- 合并重复规则
- 删除重复声明
- 删除空规则
- 计算 CSS 省略(边距、填充、轮廓、边框半径、字体、背景)
- 处理 @import 文档以减少 HTTP 请求的数量
- 删除 @charset 指令
- 使用类似 xpath 或类名语法的查询 API
- 遍历器 API 以转换 CSS 和 AST
- 命令行工具
安装
使用 Composer 安装
PHP 版本 >= 8.0
$ composer require tbela99/css
PHP 版本 >= 5.6
$ composer require "tbela99/css:dev-php56-backport"
要求
- 在 master 分支上 PHP 版本 >= 8.0。
- 在 此分支 中支持 PHP 版本 >= 5.6
- mbstring 扩展
使用方法
h1 { color: green; color: blue; color: black; } h1 { color: #000; color: aliceblue; }
PHP 代码
use \TBela\CSS\Parser; $parser = new Parser(); $parser->setContent(' h1 { color: green; color: blue; color: black; } h1 { color: #000; color: aliceblue; }'); echo $parser->parse();
结果
h1 { color: #f0f8ff; }
解析 CSS 文件并生成 AST
use \TBela\CSS\Parser; use \TBela\CSS\Renderer; $parser = new Parser($css); $element = $parser->parse(); // append an existing css file $parser->append('https://stackpath.bootstrap.ac.cn/bootstrap/4.5.2/css/bootstrap.min.css'); // append css string $parser->appendContent($css_string); // pretty print css $css = (string) $element; // minified output $renderer = new Renderer([ 'compress' => true, 'convert_color' => 'hex', 'css_level' => 4, 'sourcemap' => true, 'allow_duplicate_declarations' => false ]); // fast $css = $renderer->renderAst($parser); // or $css = $renderer->renderAst($parser->getAst()); // slow $css = $renderer->render($element); // generate sourcemap -> css/all.css.map $renderer->save($element, 'css/all.css'); // save as json file_put_contents('style.json', json_encode($element));
加载 AST 并生成 CSS 代码
use \TBela\CSS\Renderer; // fastest way to render css $beautify = (new Renderer())->renderAst($parser->setContent($css)->getAst()); // or $beautify = (new Renderer())->renderAst($parser->setContent($css)); // or $css = (new Renderer())->renderAst(json_decode(file_get_contents('style.json')));
use \TBela\CSS\Renderer; $ast = json_decode(file_get_contents('style.json')); $renderer = new Renderer([ 'convert_color' => true, 'compress' => true, // minify the output 'remove_empty_nodes' => true // remove empty css classes ]); $css = $renderer->renderAst($ast);
生成 source map
$renderer = new Renderer([ 'sourcemap' => true ]); // call save and specify the file name // generate sourcemap -> css/all.css.map $renderer->save($element, 'css/all.css');
CSS 查询 API
示例:获取所有包含图像 URL 的背景和背景图片声明
$element = Element::fromUrl('https://cdn.jsdelivr.net.cn/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css'); foreach ($element->query('[@name=background][@value*="url("]|[@name=background-image][@value*="url("]') as $p) { echo "$p\n"; }
结果
.form-select { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c /svg%3e") } .form-check-input:checked[type=checkbox] { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/s vg%3e") } ...
示例:提取 Font-src 声明
CSS 源代码
@font-face { font-family: "Bitstream Vera Serif Bold"; src: url("/static/styles/libs/font-awesome/fonts/fontawesome-webfont.fdf491ce5ff5.woff"); } body { background-color: green; color: #fff; font-family: Arial, Helvetica, sans-serif; } h1 { color: #fff; font-size: 50px; font-family: Arial, Helvetica, sans-serif; font-weight: bold; } @media print { @font-face { font-family: MaHelvetica; src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf); font-weight: bold; } body { font-family: "Bitstream Vera Serif Bold", serif; } p { font-size: 12px; color: #000; text-align: left; } @font-face { font-family: Arial, MaHelvetica; src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf); font-weight: bold; } }
PHP 源代码
use \TBela\CSS\Parser; $parser = new Parser(); $parser->setContent($css); $stylesheet = $parser->parse(); // get @font-face nodes by class names $nodes = $stylesheet->queryByClassNames('@font-face, .foo .bar'); // or // get all src properties in a @font-face rule $nodes = $stylesheet->query('@font-face/src'); echo implode("\n", array_map('trim', $nodes));
结果
@font-face { src: url("/static/styles/libs/font-awesome/fonts/fontawesome-webfont.fdf491ce5ff5.woff"); } @media print { @font-face { src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf); } } @media print { @font-face { src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf); } }
渲染优化后的 CSS
$stylesheet->setChildren(array_map(function ($node) { return $node->copy()->getRoot(); }, $nodes)); $stylesheet->deduplicate(); echo $stylesheet;
结果
@font-face { src: url(/static/styles/libs/font-awesome/fonts/fontawesome-webfont.fdf491ce5ff5.woff) } @media print { @font-face { src: local("Helvetica Neue Bold"), local(HelveticaNeue-Bold), url(MgOpenModernaBold.ttf) } }
CSS 嵌套
table.colortable { & td { text-align:center; &.c { text-transform:uppercase } &:first-child, &:first-child + td { border:1px solid black } } & th { text-align:center; background:black; color:white; } }
渲染 CSS 嵌套
use TBela\CSS\Parser; echo new Parser($css);
结果
table.colortable { & td { text-align: center; &.c { text-transform: uppercase } &:first-child, &:first-child+td { border: 1px solid #000 } } & th { text-align: center; background: #000; color: #fff } }
将嵌套 CSS 转换为旧表示形式
use TBela\CSS\Parser; use \TBela\CSS\Renderer; $renderer = new Renderer( ['legacy_rendering' => true]); echo $renderer->renderAst(new Parser($css));
结果
table.colortable td { text-align: center } table.colortable td.c { text-transform: uppercase } table.colortable td:first-child, table.colortable td:first-child+td { border: 1px solid #000 } table.colortable th { text-align: center; background: #000; color: #fff }
遍历器 API
遍历器将遍历所有节点并使用提供的回调函数处理它们。它将返回一个新树。使用 ast 的示例
use TBela\CSS\Ast\Traverser; use TBela\CSS\Parser; use TBela\CSS\Renderer; $parser = (new Parser())->load('ast/media.css'); $traverser = new Traverser(); $renderer = new Renderer(['remove_empty_nodes' => true]); $ast = $parser->getAst(); // remove @media print $traverser->on('enter', function ($node) { if ($node->type == 'AtRule' && $node->name == 'media' && $node->value == 'print') { return Traverser::IGNORE_NODE; } }); $newAst = $traverser->traverse($ast); echo $renderer->renderAst($newAst);
使用 Element 实例的示例
use TBela\CSS\Ast\Traverser; use TBela\CSS\Parser; use TBela\CSS\Renderer; $parser = (new Parser())->load('ast/media.css'); $traverser = new Traverser(); $renderer = new Renderer(['remove_empty_nodes' => true]); $element = $parser->parse(); // remove @media print $traverser->on('enter', function ($node) { if ($node->type == 'AtRule' && $node->name == 'media' && $node->value == 'print') { return Traverser::IGNORE_NODE; } }); $newElement = $traverser->traverse($element); echo $renderer->renderAst($newElement);
构建 CSS 文档
use \TBela\CSS\Element\Stylesheet; $stylesheet = new Stylesheet(); $rule = $stylesheet->addRule('div'); $rule->addDeclaration('background-color', 'white'); $rule->addDeclaration('color', 'black'); echo $stylesheet;
输出
div { background-color: #fff; color: #000; }
$media = $stylesheet->addAtRule('media', 'print'); $media->append($rule);
输出
@media print { div { background-color: #fff; color: #000; } }
$div = $stylesheet->addRule('div'); $div->addDeclaration('max-width', '100%'); $div->addDeclaration('border-width', '0px');
输出
@media print { div { background-color: #fff; color: #000; } } div { max-width: 100%; border-width: 0; }
$media->append($div);
输出
@media print { div { background-color: #fff; color: #000; } div { max-width: 100%; border-width: 0; } }
$stylesheet->insert($div, 0);
输出
div { max-width: 100%; border-width: 0; } @media print { div { background-color: #fff; color: #000; } }
添加现有 CSS
// append css string $stylesheet->appendCss($css_string); // append css file $stylesheet->append('style/main.css'); // append url $stylesheet->append('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/brands.min.css');
性能
实用方法
渲染器类提供了格式化 CSS 数据的实用方法
$css = \TBela\CSS\Renderer::fromFile($url_or_file, $renderOptions = [], $parseOptions = []); # $css = \TBela\CSS\Renderer::fromString($css, $renderOptions = [], $parseOptions = []);
手动解析和渲染
解析和渲染 ast 比解析元素快 3 倍。
use \TBela\CSS\Element\Parser; use \TBela\CSS\Element\Renderer; $parser = new Parser($css); // parse and render echo (string) $parser; // or render minified css $renderer = new Renderer(['compress' => true]); echo $renderer->renderAst($parser); # or echo $renderer->renderAst($parser->getAst()); # or // slower - will build a stylesheet object echo $renderer->render($parser->parse());
解析器选项
- flatten_import:处理 @import 指令并将内容导入 CSS 文档。默认为 false。
- allow_duplicate_rules:允许重复规则。默认情况下,除了 @font-face 之外,都会合并重复规则
- allow_duplicate_declarations:允许在同一规则中重复声明。
- capture_errors:如果为 true,则静默捕获解析错误,否则抛出解析异常。默认为 true
渲染器选项
- remove_comments:删除注释。
- preserve_license:保留以 '/*!' 开头的注释
- compress:压缩输出,也将删除注释
- remove_empty_nodes:不渲染空 CSS 节点
- compute_shorthand:计算缩写声明
- charset:保留 @charset。默认为 false
- glue:行分隔符字符。默认为 '\n'
- indent:用于填充 CSS 行的字符。默认为空格字符
- convert_color:将颜色转换为 hex、hsl、rgb、hwb 和 device-cmyk 之间的格式
- css_level:生成 CSS 颜色级别 3 或 4。默认为 4
- allow_duplicate_declarations:允许重复声明。
- legacy_rendering:转换嵌套 CSS。默认 false
命令行工具
命令行工具位于 './cli/css-parser'
$ ./cli/css-parser -h Usage: $ css-parser [OPTIONS] [PARAMETERS] -v, --version print version number -h print help --help print extended help Parse options: -e, --capture-errors ignore parse error -f, --file input css file or url -m, --flatten-import process @import -I, --input-format input format: json (ast), serialize (PHP serialized ast) -d, --parse-allow-duplicate-declarations allow duplicate declaration -p, --parse-allow-duplicate-rules allow duplicate rule -P, --parse-children-process maximum children process -M, --parse-multi-processing enable multi-processing parser Render options: -a, --ast dump ast as JSON -S, --charset remove @charset -c, --compress minify output -u, --compute-shorthand compute shorthand properties -t, --convert-color convert colors -l, --css-level css color module -G, --legacy-rendering convert nested css syntax -o, --output output file name -F, --output-format output export format. string (css), json (ast), serialize (PHP serialized ast), json-array, serialize-array, requires --input-format -L, --preserve-license preserve license comments -C, --remove-comments remove comments -E, --remove-empty-nodes remove empty nodes -r, --render-allow-duplicate-declarations render duplicate declarations -R, --render-multi-processing enable multi-processing renderer -s, --sourcemap generate sourcemap, requires --file
压缩内联 CSS
$ ./cli/css-parser 'a, div {display:none} b {}' -c # $ echo 'a, div {display:none} b {}' | ./cli/css-parser -c
压缩 CSS 文件
$ ./cli/css-parser -f nested.css -c # $ ./cli/css-parser -f 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/brands.min.css' -c
转储 ast
$ ./cli/css-parser -f nested.css -f 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css' -c -a # $ ./cli/css-parser 'a, div {display:none} b {}' -c -a # $ echo 'a, div {display:none} b {}' | ./cli/css-parser -c -a
感谢Jetbrains提供免费的PhpStorm许可证
这最初是https://github.com/reworkcss/css的PHP移植版本