wikimedia / zest-css
PHP 的快速、轻量级、可扩展的 CSS 选择器引擎
Requires
- php: >=7.4
- ext-mbstring: *
- ext-xml: *
Requires (Dev)
- mediawiki/mediawiki-codesniffer: 41.0.0
- mediawiki/mediawiki-phan-config: 0.14.0
- mediawiki/minus-x: 1.1.1
- ockcyp/covers-validator: 1.6.0
- php-parallel-lint/php-console-highlighter: 1.0.0
- php-parallel-lint/php-parallel-lint: 1.3.2
- phpunit/phpunit: 9.6.16
- wikimedia/remex-html: ^4.0.0
- wikimedia/testing-access-wrapper: ^3.0
- wikimedia/update-history: ^1.0.1
Suggests
- ext-intl: *
README
zest.php 是一个针对 PHP 的快速、轻量级、可扩展的 CSS 选择器引擎。
Zest 设计得非常简洁,同时支持 CSS3/CSS4 选择器,并保持快速。
这是将 zest.js 选择器库移植到 PHP。由于该项目已经有一段时间没有更新,因此从 domino DOM 库中的 zest 复制中获取了错误修复。
在 Phabricator 上报告问题。
使用方法
use Wikimedia\Zest\Zest; $els = Zest::find('section! > div[title="hello" i] > :local-link /href/ h1', $doc);
安装
此包可在 Packagist 上获取
$ composer require wikimedia/zest-css
API
以下函数接受一个 opts
数组,可以通过传递额外的选项来影响匹配结果。这些选项可以在自定义选择器中使用(见下文)。目前,标准选择器支持以下选项
standardsMode
(bool
):如果存在且为 true,将禁用各种 PHP 工具,以调用在 Web 标准中定义的方法。getElementsById
(true|callable(DOMNode,string):array<DOMElement>
):如果设置为true
,则将禁用一种优化以确保 Zest 可以在 ID 在文档中不唯一时返回多个元素。如果设置为接受上下文节点和 ID 字符串并返回元素数组的callable
,则第三方 DOM 实现可以支持一个高效索引,允许多个元素共享相同的 ID。
所有方法都会在选择器解析失败时抛出由 ZestInst::newBadSelectorException()
返回的异常(默认为新的 InvalidArgumentException
)。
Zest::find( string $selector, $context, array $opts = [] ): array
这等价于标准 DOM 方法 ParentNode#querySelectorAll()
。
Zest::matches( $element, string $selector, array $opts = [] ): bool
这等价于标准 DOM 方法 Element#matches()
。
由于 PHP 的 DOMDocument::getElementById
和 DOMDocument#getElementsByTagName
实现存在一些性能和规范兼容性问题,Zest 还导出了一些有用且性能良好的版本
Zest::getElementsById( $contextNode, string $id, array $opts = [] ): array
这等价于标准 DOM 方法 Document#getElementById()
(尽管您可以使用任何上下文节点,而不仅仅是顶级文档)。此外,如果 DOM 实现提供了适当的支持,则此方法可以返回多个匹配元素。
Zest::getElementsByTagName( $contextNode, string $tagName, array $opts = [] ): array
这等价于标准 DOM 方法 Element#getElementsByTagName()
,尽管您可以使用 DocumentFragment
作为 $contextNode
。
扩展
您可以选择添加自己的选择器、运算符或组合器。这些组合器添加到ZestInst
的一个实例中,因此它们不会影响其他Zest实例或静态的Zest::find
/Zest::matches
方法。ZestInst
类提供了在Zest
上可用的所有静态方法的非静态版本。
添加简单选择器
添加简单选择器相当直接。只能添加伪类和属性运算符。(添加您自己的选择器风格需要修改核心逻辑。)
以下是一个自定义的:name
选择器的示例,该选择器将匹配元素的name
属性:例如h1:name(foo)
。实际上它是h1[name=foo]
的别名。
use Wikimedia\Zest\ZestInst; $z = new ZestInst; $z->addSelector1( ':name', function( string $param ):callable { return function ( $el, array $opts ) use ( $param ):bool { if ($el->getAttribute('name') === $param) return true; return false; }; } ); // Use it! $z->find( 'h1:name(foo)', $document );
如果您想添加依赖于全局属性(如:target
)的选择器,可以将全局信息添加到$opts
中,并在调用您的选择器函数时可用。
注意:如果您的伪类不带参数,请使用addSelector0
。
添加属性运算符
$z = new ZestInst; // `$attr` is the attribute // `$val` is the value to match $z->addOperator( '!=', function( string $attr, string $val ):bool { return $attr !== $val; } ); // Use it! $z->find( 'h1[name != "foo"]', $document );
添加组合器
添加组合器稍微复杂一些。一开始可能有些令人困惑,因为逻辑是颠倒的。Zest从右到左解释选择器。
以下是一个实现父组合器的示例
$z = new ZestInst; $z->addCombinator( '<', function( callable $test ): callable { return function( $el, array $opts ) use ( $test ): ?DOMNode { // `$el` is the current element $el = $el->firstChild; while ($el) { // return the relevant element // if it passed the test if ($el->nodeType === 1 && call_user_func($test, $el, $opts)) { return $el; } $el = $el->nextSibling; } return null; }; } ); // Use it! $z->find( 'h1 < section', $document );
$test
函数测试它需要查找的任何简单选择器,但它的具体操作并不重要。最重要的是,一旦找到相关元素,就返回它。
测试
$ composer test
许可证和致谢
原始的Zest代码库版权所有(c)2011-2012,Christopher Jeffrey。
PHP的移植最初由C. Scott Ananian完成,版权所有(c)2019 Wikimedia Foundation。
额外的代码和功能版权所有(c)2020-2021 Wikimedia Foundation。
原始的Zest代码库和此移植都遵循MIT许可证;有关更多信息,请参阅LICENSE。