wikimedia/zest-css

PHP 的快速、轻量级、可扩展的 CSS 选择器引擎

3.0.1 2024-03-11 18:54 UTC

This package is auto-updated.

Last update: 2024-09-07 21:13:11 UTC


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::getElementByIdDOMDocument#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。