首页 >后端开发 >php教程 >PHP DOM:使用XPATH

PHP DOM:使用XPATH

尊渡假赌尊渡假赌尊渡假赌
尊渡假赌尊渡假赌尊渡假赌原创
2025-02-26 09:07:16530浏览

PHP DOM: Using XPath

核心要点

  • XPath 是一种用于查询 XML 文档的语法,它提供了一种更简单、更简洁的方式来编写功能,并减少了编写查询和过滤 XML 数据所需的代码量。
  • XPath 查询可以使用两个函数执行:query()evaluate()。虽然两者都执行查询,但区别在于它们返回的结果类型,query() 返回 DOMNodeList,而 evaluate() 则尽可能返回类型化结果。
  • 使用 XPath 可以使代码更简洁、更高效。在比较测试中,使用纯 XPath 的速度优势相当明显,XPath 版本比非 XPath 版本快约 10%。
  • PHP DOM 允许您使用自定义功能扩展标准 XPath 函数。这包括将 PHP 自身函数整合到 XPath 查询中,以及注册在 XPath 中使用的 PHP 函数。这扩展了 XPath 的功能,使其能够执行更复杂的查询。

本文将深入探讨 XPath,包括其功能和在 PHP 中的实现方式。您将发现 XPath 可以大大减少编写查询和过滤 XML 数据所需的代码量,并且通常也能提高性能。我将使用上一篇文章中相同的 DTD 和 XML 来演示 PHP DOM XPath 功能。为了快速回顾,以下是 DTD 和 XML 的样子:

<code class="language-xml"><!DOCTYPE library [
  <!ELEMENT library (book*)>
  <!ELEMENT book (title, author, genre, chapter*)>
  <!ATTLIST book isbn ID #REQUIRED>
  <!ELEMENT title (#PCDATA)>
  <!ELEMENT author (#PCDATA)>
  <!ELEMENT genre (#PCDATA)>
  <!ELEMENT chapter (chaptitle,text)>
  <!ATTLIST chapter position NMTOKEN #REQUIRED>
  <!ELEMENT chaptitle (#PCDATA)>
  <!ELEMENT text (#PCDATA)>
]></code>
<code class="language-xml"><?xml version="1.0" encoding="utf-8"?>
<library>
  <book isbn="isbn1234">
    <title>A Book</title>
    <author>An Author</author>
    <genre>Horror</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text></text>
    </chapter>
  </book>
  <book isbn="isbn1235">
    <title>Another Book</title>
    <author>Another Author</author>
    <genre>Science Fiction</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text>Sit Dolor Amet...</text>
    </chapter>
  </book>
</library></code>

基本的 XPath 查询

XPath 是一种用于查询 XML 文档的语法。最简单的形式是定义您想要访问的元素的路径。使用上面的 XML 文档,以下 XPath 查询将返回所有存在的 book 元素的集合:

<code class="language-xpath">//library/book</code>

就是这样。两个正斜杠表示 library 是文档的根元素,单个斜杠表示 book 是其子元素。非常简单,不是吗?但是,如果您想指定特定的书籍呢?假设您想返回任何由“An Author”撰写的书籍。该 XPath 将是:

<code class="language-xpath">//library/book/author[text() = "An Author"]/..</code>

您可以在方括号中使用 text() 对节点的值执行比较,尾随的“/..”表示我们想要父元素(即向上移动一个节点)。XPath 查询可以使用两个函数之一执行:query()evaluate()。两者都执行查询,但区别在于它们返回的结果类型。query() 将始终返回 DOMNodeList,而 evaluate() 则尽可能返回类型化结果。例如,如果您的 XPath 查询是返回特定作者撰写的书籍数量而不是实际的书籍本身,那么 query() 将返回一个空的 DOMNodeListevaluate() 将直接返回数字,因此您可以立即使用它,而不必从节点中提取数据。

XPath 的代码和速度优势

让我们做一个快速演示,返回特定作者撰写的书籍数量。我们将首先查看一种可行的方法,但它不使用 XPath。这是为了向您展示如何在不使用 XPath 的情况下完成此操作,以及为什么 XPath 如此强大。

<code class="language-xml"><!DOCTYPE library [
  <!ELEMENT library (book*)>
  <!ELEMENT book (title, author, genre, chapter*)>
  <!ATTLIST book isbn ID #REQUIRED>
  <!ELEMENT title (#PCDATA)>
  <!ELEMENT author (#PCDATA)>
  <!ELEMENT genre (#PCDATA)>
  <!ELEMENT chapter (chaptitle,text)>
  <!ATTLIST chapter position NMTOKEN #REQUIRED>
  <!ELEMENT chaptitle (#PCDATA)>
  <!ELEMENT text (#PCDATA)>
]></code>

下一种方法实现了相同的结果,但使用 XPath 来选择仅由特定作者撰写的书籍:

<code class="language-xml"><?xml version="1.0" encoding="utf-8"?>
<library>
  <book isbn="isbn1234">
    <title>A Book</title>
    <author>An Author</author>
    <genre>Horror</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text></text>
    </chapter>
  </book>
  <book isbn="isbn1235">
    <title>Another Book</title>
    <author>Another Author</author>
    <genre>Science Fiction</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text>Sit Dolor Amet...</text>
    </chapter>
  </book>
</library></code>

请注意,我们这次消除了 PHP 对作者值进行测试的需要。但是,我们还可以更进一步,使用 XPath 函数 count() 来计算此路径的出现次数。

<code class="language-xpath">//library/book</code>

我们只需一行 XPath 就能检索到所需信息,无需使用 PHP 执行费力的过滤。事实上,这是一种编写此功能的更简单、更简洁的方法!请注意,在最后一个示例中使用了 evaluate()。这是因为函数 count() 返回类型化结果。使用 query() 将返回 DOMNodeList,但您会发现它是一个空列表。这不仅使您的代码更简洁,而且还具有速度优势。我发现版本 1 的平均速度比版本 2 快 30%,但版本 3 比版本 2 快约 10%(比版本 1 快约 15%)。虽然这些测量结果会根据您的服务器和查询而有所不同,但使用纯 XPath 通常会带来相当大的速度优势,同时还能使您的代码更易于阅读和维护。

XPath 函数

XPath 可以使用相当多的函数,并且有很多优秀的资源详细说明了可用的函数。如果您发现自己正在迭代 DOMNodeLists 或比较 nodeValues,您可能会发现一个 XPath 函数可以消除很多 PHP 代码。您已经看到了 count() 函数的用法。让我们使用 id() 函数来返回具有给定 ISBN 的书籍的标题。您需要使用的 XPath 表达式是:

<code class="language-xpath">//library/book/author[text() = "An Author"]/..</code>

请注意,此处要搜索的值用引号括起来并用空格分隔;无需使用逗号分隔术语。

<code class="language-php"><?php
public function getNumberOfBooksByAuthor($author) {
    $total = 0;
    $elements = $this->domDocument->getElementsByTagName("author");
    foreach ($elements as $element) {
        if ($element->nodeValue == $author) {
            $total++;
        }
    }
    return $total; // 修正:这里应该是 $total,而不是 $number
}
?></code>

在 XPath 中执行复杂函数相对简单;诀窍是熟悉可用的函数。

在 XPath 中使用 PHP 函数

有时您可能会发现自己需要一些标准 XPath 函数无法提供的更强大的功能。幸运的是,PHP DOM 还允许您将 PHP 自身函数整合到 XPath 查询中。让我们考虑返回书籍标题中的单词数量。最简单的函数,我们可以这样编写方法:

<code class="language-xml"><!DOCTYPE library [
  <!ELEMENT library (book*)>
  <!ELEMENT book (title, author, genre, chapter*)>
  <!ATTLIST book isbn ID #REQUIRED>
  <!ELEMENT title (#PCDATA)>
  <!ELEMENT author (#PCDATA)>
  <!ELEMENT genre (#PCDATA)>
  <!ELEMENT chapter (chaptitle,text)>
  <!ATTLIST chapter position NMTOKEN #REQUIRED>
  <!ELEMENT chaptitle (#PCDATA)>
  <!ELEMENT text (#PCDATA)>
]></code>

但是,我们也可以将函数 str_word_count() 直接整合到 XPath 查询中。为此需要完成几个步骤。首先,我们必须使用 XPath 对象注册一个命名空间。XPath 查询中的 PHP 函数以“php:functionString”开头,然后是您想要使用的函数的名称,括在括号中。此外,要定义的命名空间是 http://php.net/xpath。命名空间必须设置为这个;任何其他值都会导致错误。然后,我们需要调用 registerPHPFunctions(),它告诉 PHP 每当遇到以“php:”为命名空间的函数时,都应该由 PHP 处理它。调用函数的实际语法是:

<code class="language-xml"><?xml version="1.0" encoding="utf-8"?>
<library>
  <book isbn="isbn1234">
    <title>A Book</title>
    <author>An Author</author>
    <genre>Horror</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text></text>
    </chapter>
  </book>
  <book isbn="isbn1235">
    <title>Another Book</title>
    <author>Another Author</author>
    <genre>Science Fiction</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text>Sit Dolor Amet...</text>
    </chapter>
  </book>
</library></code>

将所有这些放在一起,得到 getNumberOfWords() 的以下重新实现:

<code class="language-xpath">//library/book</code>

请注意,您不需要调用 XPath 函数 text() 来提供节点的文本。registerPHPFunctions() 方法会自动执行此操作。但是,以下同样有效:

<code class="language-xpath">//library/book/author[text() = "An Author"]/..</code>

注册 PHP 函数不仅限于 PHP 自带的函数。您可以定义自己的函数并在 XPath 中提供这些函数。唯一的区别是,在定义函数时,您使用“php:function”而不是“php:functionString”。此外,只能提供函数本身或静态方法。不支持调用实例方法。让我们使用一个超出类范围的常规函数来演示基本功能。我们将使用的函数将仅返回“乔治·奥威尔”的书籍。对于您希望包含在查询中的每个节点,它必须返回 true

<code class="language-php"><?php
public function getNumberOfBooksByAuthor($author) {
    $total = 0;
    $elements = $this->domDocument->getElementsByTagName("author");
    foreach ($elements as $element) {
        if ($element->nodeValue == $author) {
            $total++;
        }
    }
    return $total; // 修正:这里应该是 $total,而不是 $number
}
?></code>

传递给函数的参数是 DOMElements 数组。函数负责迭代数组并确定要测试的节点是否应在 DOMNodeList 中返回。在此示例中,要测试的节点是 /book,我们使用 /author 来进行确定。现在我们可以创建方法 getGeorgeOrwellBooks()

<code class="language-php"><?php
public function getNumberOfBooksByAuthor($author) {
    $query = "//library/book/author[text() = '$author']/..";
    $xpath = new DOMXPath($this->domDocument);
    $result = $xpath->query($query);
    return $result->length;
}
?></code>

如果 compare() 是一个静态方法,那么您需要修改 XPath 查询,使其读取:

<code class="language-php"><?php
public function getNumberOfBooksByAuthor($author) {
    $query = "count(//library/book/author[text() = '$author']/..)";
    $xpath = new DOMXPath($this->domDocument);
    return $xpath->evaluate($query);
}
?></code>

事实上,所有这些功能都可以轻松地仅用 XPath 编写,但该示例展示了如何扩展 XPath 查询以使其更复杂。在 XPath 中无法调用对象方法。如果您发现需要访问某些对象属性或方法来完成 XPath 查询,最好的解决方案是使用 XPath 完成您能做到的部分,然后根据需要使用任何对象方法或属性处理生成的 DOMNodeList

总结

XPath 是一种在处理 XML 数据时减少代码编写量并加快代码执行速度的好方法。虽然不是官方 DOM 规范的一部分,但 PHP DOM 提供的附加功能允许您使用自定义功能扩展标准 XPath 函数。这是一个非常强大的功能,随着您对 XPath 函数的熟悉程度提高,您可能会发现自己越来越少地依赖它。

(图片来自 Fotolia)

关于使用 XPath 的 PHP DOM 的常见问题解答 (FAQ)

什么是 XPath,它如何在 PHP DOM 中使用?

XPath(XML 路径语言)是一种查询语言,用于从 XML 文档中选择节点。在 PHP DOM 中,XPath 用于遍历 XML 文档中的元素和属性。它允许您通过多种方法找到并选择 XML 文档的特定部分,例如按名称选择节点、按其属性值选择节点或按其在文档中的位置选择节点。这使得它成为在 PHP 中解析和操作 XML 数据的强大工具。

如何创建 DOMXPath 的实例?

要创建 DOMXPath 的实例,您首先需要创建一个 DOMDocument 类的实例。获得 DOMDocument 对象后,您可以通过将 DOMDocument 对象传递给 DOMXPath 构造函数来创建一个新的 DOMXPath 对象。这是一个示例:

<code class="language-xml"><!DOCTYPE library [
  <!ELEMENT library (book*)>
  <!ELEMENT book (title, author, genre, chapter*)>
  <!ATTLIST book isbn ID #REQUIRED>
  <!ELEMENT title (#PCDATA)>
  <!ELEMENT author (#PCDATA)>
  <!ELEMENT genre (#PCDATA)>
  <!ELEMENT chapter (chaptitle,text)>
  <!ATTLIST chapter position NMTOKEN #REQUIRED>
  <!ELEMENT chaptitle (#PCDATA)>
  <!ELEMENT text (#PCDATA)>
]></code>

如何使用 XPath 选择节点?

您可以使用 DOMXPath 对象的 query() 方法选择节点。query() 方法将 XPath 表达式作为参数,并返回一个包含与表达式匹配的所有节点的 DOMNodeList 对象。例如:

<code class="language-xml"><?xml version="1.0" encoding="utf-8"?>
<library>
  <book isbn="isbn1234">
    <title>A Book</title>
    <author>An Author</author>
    <genre>Horror</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text></text>
    </chapter>
  </book>
  <book isbn="isbn1235">
    <title>Another Book</title>
    <author>Another Author</author>
    <genre>Science Fiction</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text>Sit Dolor Amet...</text>
    </chapter>
  </book>
</library></code>

这将选择所有作为 <book></book> 元素子元素的 <title></title> 元素。

DOMXPath 中 query()evaluate() 方法的区别是什么?

query()evaluate() 方法都用于评估 XPath 表达式。区别在于它们返回的结果类型。query() 方法返回与 XPath 表达式匹配的所有节点的 DOMNodeList。另一方面,evaluate() 返回类型化结果,例如布尔值、数字或字符串,具体取决于 XPath 表达式。如果表达式结果为节点集,evaluate() 将返回 DOMNodeList。

如何在 XPath 查询中处理命名空间?

要在 XPath 查询中处理命名空间,您需要使用 registerNamespace() 方法将命名空间注册到 DOMXPath 对象。此方法有两个参数:前缀和命名空间 URI。注册命名空间后,您可以在 XPath 查询中使用前缀。例如:

<code class="language-xpath">//library/book</code>

如何使用 XPath 选择属性?

您可以使用 @ 符号后跟属性名称来选择 XPath 中的属性。例如,要选择 <a></a> 元素的所有 href 属性,您可以使用以下 XPath 表达式://a/@href

如何在 PHP DOM 中使用 XPath 函数?

XPath 提供了许多可以在 XPath 表达式中使用的函数。这些函数可用于操作字符串、数字、节点集等等。要在 PHP DOM 中使用 XPath 函数,只需在 XPath 表达式中包含该函数即可。例如,要选择所有具有价格元素且值大于 30 的 <book></book> 元素,您可以使用 number() 函数,如下所示://book[number(price) > 30]

我可以在 PHP DOM 中将 XPath 与 HTML 文档一起使用吗?

是的,您可以在 PHP DOM 中将 XPath 与 HTML 文档一起使用。但是,由于 HTML 不总是格式良好的 XML,因此在尝试将 XPath 与 HTML 一起使用时可能会遇到问题。为了避免这些问题,您可以使用 DOMDocument 类的 loadHTML() 方法加载 HTML 文档。此方法将解析 HTML 并纠正任何格式错误,允许您将 XPath 与生成的 DOMDocument 对象一起使用。

如何在 PHP DOM 中使用 XPath 时处理错误?

在 PHP DOM 中使用 XPath 时,可能会由于多种原因发生错误,例如 XPath 表达式格式错误或无法加载 XML 文档。为了处理这些错误,您可以使用 libxml_use_internal_errors() 函数启用用户错误处理。此函数将导致 libxml 错误存储在内部,允许您在代码中处理它们。然后,您可以使用 libxml_get_errors() 函数检索错误并根据需要处理它们。

我可以使用 PHP DOM 中的 XPath 修改 XML 文档吗?

虽然 XPath 本身不提供修改 XML 文档的方法,但您可以将 XPath 与 DOM API 结合使用来修改 XML 文档。您可以使用 XPath 选择要修改的节点,然后使用 DOM API 提供的方法进行修改。例如,您可以使用 DOMNode 类的 removeChild() 方法删除节点,或使用 DOMElement 类的 setAttribute() 方法更改属性的值。

以上是PHP DOM:使用XPATH的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn