【问题标题】:PHP DOMXPath->query()/->evaluate() not matching inner textPHP DOMXPath->query()/->evaluate() 不匹配内部文本
【发布时间】:2019-04-17 10:14:17
【问题描述】:

我目前正在尝试创建一个纯 PHP 菜单遍历系统 - 这是因为我正在为一些人做一个即兴项目,但他们想要尽可能少的 JS(即:无)并且最好是纯 PHP。

我有一个如下所示的菜单:

ul {
  list-style-type: none;
}

nav > ul.sidebar-list ul.sub {
  display: none;
}

nav > ul.sidebar-list ul.sub.active {
  display: block;
}
<nav class="sidebar" aria-labelledby="primary-navigation">
  <ul class="sidebar-list">

    <!--each element has a sub-menu which is initially hidden by css when the page is loaded. Via php the appropriate path the current page and top-level links will be visible only-->
    <a href="#"><li>Home</li></a>
    <!--sub-items-->
    <ul class="sub active">
      <a href="#"><li>Barn</li></a>
      <a href="#"><li>Activities</li></a>
      <ul class="sub active">
        <a href="#"><li>News</li></a>
        <a href="#"><li>Movements</li></a>
        <a href="#"><li>Reviews</li></a>
        <a href="#"><li>About Us</li></a>
        <a href="#"><li>Terms of Use</li></a>
      </ul>
    </ul>
    <a href="#"><li>Events</li></a>
    <ul class="sub">
      <a href="#"><li>Overview</li></a>
      <a href="#"><li>Farming</li></a>
      <a href="#"><li>Practises</li></a>
      <a href="#"><li>Links</li></a>
      <ul class="sub">
        <a href="#"><li>Another Farm</li></a>
        <a href="#"><li>24m</li></a>
      </ul>
    </ul>
  </ul>
</nav>

为了尝试将页面的标题内部文本与菜单项内部文本匹配(可能不是最好的处理方式,但我仍在学习 php)我运行:

$menu = new DOMDocument();
assert($menu->loadHTMLFile($menu_path), "Loading nav.html (menu file) failed");
//show content to log of the html document
error_log("HTML file: \n\n".$menu->textContent);

//set up a query to find an element matching the title string found
$xpath = new DOMXPath($menu);

$menu_query = "//a/li[matches(text(), '$title_text', 'i')]";
$elements = $xpath->query($menu_query);
error_log($elements ? ("Result of xpath query is: ".print_r($elements, TRUE)): "The xpath query for searching the menu is incorrect and will not find you anything!\ntype of return: ".gettype($elements));

我得到了正确的回报:https://www.freeformatter.com/xpath-tester.html,但在脚本中我没有。我尝试了许多不同的文本匹配组合,例如://x:a/x:li[lower-case(text())='$title_text'],但总是一个空节点列表。

【问题讨论】:

  • fn:lower-case() 是 XPath 2.0+ 函数。
  • @Alejandro 我如何知道我正在运行的 XPath 版本?另外,如果肯定不支持它,它只会返回 false 对吗?
  • 如果没有为您的执行上下文定义函数,XPath 引擎应该报告错误。在 XPath 执行上下文中,无法知道支持的语言版本。如果 XPath 2.0 函数失败,那么您有一个 XPath 1.0 引擎。
  • 它在php.net/manual/en/domxpath.query.php 上说如果查询无效,它将返回false,但是如果我使用不受支持的函数,您是说php 实际上会抛出错误吗?因为我已经尝试过 lower-case() 并且它没有抛出错误,所以只返回了 0 个匹配项

标签: php html xpath domdocument


【解决方案1】:

PHP 使用 XPath 1.0。 matches 是一个 XPath 2.0 函数,因此如果您正在寻找它们,您会在错误日志中看到警告。

PHP Warning:  DOMXPath::query(): xmlXPathCompOpEval: function matches not found in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
PHP   2. DOMXPath->query() php shell code:1

一个简单的区分大小写的匹配可以通过相等检查来完成。

$title_text = "Farming";
$menu_query = "//a/li[. = '$title_text']";

但不区分大小写的搜索涉及将字符从大写转换为小写:

$title_text = "FaRmInG";
$title_text = strtolower($title_text);
$menu_query = "//a/li[translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '$title_text']";

无论哪种情况,我们最终都会得到一个可以迭代的 NodeList:

$html = <<< HTML
<nav class="sidebar" aria-labelledby="primary-navigation">
  <ul class="sidebar-list">

    <!--each element has a sub-menu which is initially hidden by css when the page is loaded. Via php the appropriate path the current page and top-level links will be visible only-->
    <a href="#"><li>Home</li></a>
    <!--sub-items-->
    <ul class="sub active">
      <a href="#"><li>Barn</li></a>
      <a href="#"><li>Activities</li></a>
      <ul class="sub active">
        <a href="#"><li>News</li></a>
        <a href="#"><li>Movements</li></a>
        <a href="#"><li>Reviews</li></a>
        <a href="#"><li>About Us</li></a>
        <a href="#"><li>Terms of Use</li></a>
      </ul>
    </ul>
    <a href="#"><li>Events</li></a>
    <ul class="sub">
      <a href="#"><li>Overview</li></a>
      <a href="#"><li>Farming</li></a>
      <a href="#"><li>Practises</li></a>
      <a href="#"><li>Links</li></a>
      <ul class="sub">
        <a href="#"><li>Another Farm</li></a>
        <a href="#"><li>24m</li></a>
      </ul>
    </ul>
  </ul>
</nav>
HTML;
$menu = new DOMDocument();
$menu->loadHTML($html);
$xpath = new DOMXPath($menu);
$elements = $xpath->query($menu_query);
foreach ($elements as $element) {
    print_r($element);
}

【讨论】:

  • 谢谢,检查错误日志会很有用,我打开了 libxml_surpress_errors,因为它正在写入文档
  • 它现在确实成功匹配,目前它可以在没有 xpath 命名空间声明的情况下工作,这更好!
  • 好吧,这里的任何地方都没有涉及命名空间,所以我不明白你为什么需要它们,但很高兴你让它工作了。
猜你喜欢
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-05
  • 2018-03-09
  • 1970-01-01
  • 2013-08-23
  • 1970-01-01
相关资源
最近更新 更多