PHP DOM: с Помощью XPath

В этой статье мы подробно рассмотрим XPath, как он функционирует, и как он реализуется в PHP. Вы увидите, что XPath может значительно сократить объем кода, вам нужно написать запрос и фильтр XML данных, что часто дают более высокую производительность.

Мы будем использовать DTD и XML, чтобы продемонстрировать функциональность PHP DOM XPath. Напоминаю, что DTD и XML выглядит следующим образом:



<!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)>




<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE library SYSTEM "library.dtd">
<library>
  <book isbn="isbn1234">
    <title>A Book</title>
    <author>An Author</author>
    <genre>Horror</genre>
    <chapter position="first">
      <chaptitle>chapter one</chaptitle>
      <text><![CDATA[Lorem Ipsum...]]></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><![CDATA[<i>Sit Dolor Amet...</i>]]></text>
    </chapter>
  </book>
</library>


Основные запросы XPath

XPath является синтаксис для запросов XML-документа. В ее простейшей форме, можно определить путь к элементу, к какому вы хотите. Используя XML-документ, следующий запрос XPath возвращает коллекцию всех элементов книги:



//library/book


Вот и все. Две косые черты указывают на библиотеки является корневым элементом документа, и косой чертой указывается в книга раньше. 

Но что, если вы хотите указать конкретную книгу. Допустим, вы хотите вернуть любой книги, написанные «An Author». XPath для того что бы быть:

auhtor

Вы можете использовать здесь text()   в квадратных скобках, чтобы выполнить сравнение со значением узла, а в конце /.. указывает что, мы хотим, чтобы родительский элемент (например, движение вверх по дереву один узел).

XPath запросы могут быть выполнены с использованием одной из двух функций:  query()  и  evaluate() . Как выполнить запрос, но разница заключается в результате типа в котором они возвращаются.  query()  всегда будет возвращать список DOMNodeList  в то время как evaluate()  будет возвращать типизированный результат, если это возможно. Например, если ваш запрос XPath возвращает количество книг, написанных определенным автором, а не фактическое сами книги, то query() возвращает пустой список DOMNodeList .evaluate()  просто возвращает количество так что вы можете использовать его немедленно вместо того, чтобы извлекать данные из узла.

Преимущества кода и скорости с XPath

Давайте сделаем быструю демонстрацию того, что возвращает количество книг, написанных автором. Первый способ мы рассмотрим будет работать, но не использовать XPath. Это должно показать вам, как это можно сделать без XPath XPath и почему так мощно.



<?php
public function getNumberOfBooksByAuthor($author) {
    $total = 0;
    $elements = $this->domDocument->getElementsByTagName("author");
    foreach ($elements as $element) {
        if ($element->nodeValue == $author) {
            $total++;
        }
    }
    return $number;
}


Следующий метод достигает того же результата, но использует XPath, чтобы выбрать только те книги, которые написаны для конкретного автора:



<?php
public function getNumberOfBooksByAuthor($author)  {
    $query = "//library/book/author1/..";
    $xpath = new DOMXPath($this->domDocument);
    $result = $xpath->query($query);
    return $result->length;
}?>


Обратите внимание, как мы в этот раз сняли необходимость PHP для проверки со значением автора. Но мы можем сделать еще один шаг еще дальше и использовать XPath функции  count()  для подсчета вхождений этого пути.




<?php
public function getNumberOfBooksByAuthor($author)  {
    $query = "count(//library/book/author1/..)";
    $xpath = new DOMXPath($this->domDocument);
    return $xpath->evaluate($query);
}


Мы можем получить информацию, нам нужно только лишь линией XPath, и нет необходимости выполнять трудоемкую фильтрации с PHP. В самом деле, это гораздо проще и короткий способ написать эту функцию!

Обратите внимание, что  evaluate()  использовалась в предыдущем примере. Это потому, что функция  count()  возвращает типизированный результат. Использование  query() вернет список DOMNodeList  но вы увидите, что это пустой список.

Это не только сделает ваш код чище, но это также работает с выгодной скоростью. Я обнаружил, что версия 1 на 30% быстрее, чем в среднем версии 2, а 3-й версии был примерно на 10 процентов быстрее, чем версия 2 (около 15% быстрее, чем версия 1). Хотя эти показатели будут варьироваться в зависимости от вашего сервера и запросов, с помощью XPath в его чистом виде, как правило дают значительное преимущество скорости, а также делает код проще для чтения и поддержки.

Функции XPath

Есть целый ряд функций, которые могут быть использованы с XPath. Если вы обнаружите, что вы итерации список DOMNodeList  с или сравнения  NodeValue  с, вы, вероятно, найти функцию XPath, которые могут устранить многие кодирования PHP.

Мы видим, как  count()  функции. Давайте использовать функцию id() возвращает названия книг с данного номера ISBN. Выражение XPath необходимо использовать:



id("isbn1234 isbn1235")/title


Заметьте, что значения, которые вы ищете заключены в кавычки и разделенных пространством, нет необходимости в запятой для разделения условий.



<?php
public function findBooksByISBNs(array $isbns) {
    $ids = join(" ", $isbns);
    $query = "id('$ids')/title"; 

    $xpath = new DOMXPath($this->domDocument);
    $result = $xpath->query($query); 

    $books = array();
    foreach ($result as $node) {
        $book = array("title" => $booknode->nodeValue);
        $books[] = $book;
    }
    return $books;
}


Выполнение сложных функций в XPath является относительно простым, весь фокус в том, чтобы ознакомиться с функциями, которые доступны.

Использование PHP функций с XPath

Иногда вы можете обнаружить, что вам нужна большая функциональность, что стандартные функции XPath не могут этого сделать. К счастью, PHP DOM также позволяет включать PHP собственные функции в запросе XPath.

Рассмотрим возвращения число слов в названии книги. Это простейшая функция:



<?php
public function getNumberOfWords($isbn) {
    $query = "//library/book[@isbn = '$isbn']"; 

    $xpath = new DOMXPath($this->domDocument);
    $result = $xpath->query($query); 

    $title = $result->item(0)->getElementsByTagName("title")
        ->item(0)->nodeValue; 

    return str_word_count($title);
}


Но мы также можем включить функцию  str_word_count ()  непосредственно в запросе XPath. Есть несколько шагов, которые должны выполнить, чтобы сделать это. Прежде всего, мы должны зарегистрировать пространство имен с целью XPath. PHP функций в запросах XPath предшествует «PHP: functionString», а затем имя функции, функции, которую необходимо использовать, в круглых скобках. Кроме того, пространство имен, для определения является http://php.net/xpath. Пространство имен, должено быть установлено в этом, любые другие значения приведет к ошибкам. Затем нужно вызвать функцию registerPHPFunctions ()  , которая говорит PHP, что всякий раз, когда он сталкивается с функцией в пространстве имен с «PHP», то эта PHP должна справиться с этим.

Фактический синтаксис вызова функции:



php:functionString("nameoffunction", arg, arg...)


Сведя все это вместе приводит к следующему переопределению  getNumberOfWords ()  :



<?php
public function getNumberOfWords($isbn) {
    $xpath = new DOMXPath($this->domDocument);

    //register the php namespace
    $xpath->registerNamespace("php", "http://php.net/xpath"); 

    //ensure php functions can be called within xpath
    $xpath->registerPHPFunctions();

    $query = "php:functionString('str_word_count',(//library/book[@isbn = '$isbn']/title))"; 

    return $xpath->evaluate($query);
}


Вам не нужно вызывать функцию XPath  text()  , чтобы предоставить текст узла. Метод RegisterPHPFunctions ()   делает это автоматически. Но следующее же действует:



php:functionString('str_word_count',
(//library/book[@isbn = '$isbn']/title1)))


Регистрация функции PHP не ограничиваются функциями, которые поставляются с PHP. Вы можете определить свои собственные функции и обеспечивать тех, кто в XPath. Единственная разница состоит в том, что при определении функции, можно использовать «php:function» вместо «php: functionString». Возможно предоставить функции самостоятельно либо статическими методами. Вызов методов экземпляра не поддерживается.

Давайте использовать обычную функцию, которая выходит за рамки класса, чтобы продемонстрировать основные функции. Мы будем использовать функции возврата только для книги «Джордж Оруэлл». Книга должна возвращать true для каждого узла который вы хотите включить в запрос.



<?php
function compare($node) {
    return $node[0]->nodeValue == "George Orwell";
}


Аргумент, передаваемый в функцию массив с DOMElement. Это зависит от функции для перебора массива и определить, является ли узел и проходит испытания, должны быть возвращены в список DOMNodeList . В этом примере, узел испытывает /book , и мы используем /author , чтобы сделать определение.

Теперь мы можем создать метод  getGeorgeOrwellBooks ()  :



<?php
public function getGeorgeOrwellBooks() {
    $xpath = new DOMXPath($this->domDocument);
    $xpath->registerNamespace("php", "http://php.net/xpath");
    $xpath->registerPHPFunctions(); 

    $query = "//library/book1";
    $result = $xpath->query($query); 

    $books = array();
    foreach($result as $node) {
        $books[] = $node->getElementsByTagName("title")
            ->item(0)->nodeValue;
    } 

    return $books;
}


Если  compare ()  был статический метод, то вам нужно будет изменить запрос XPath, вот так:

book

По правде говоря, все эти функции могут быть легко закодированы просто с XPath, но этот пример показывает, как можно расширить XPath запросы и становятся все более сложными.

Вызов метода объекта не представляется возможным в XPath. Если вы обнаружите, вам нужно получить доступ к некоторым свойствам объекта или метода выполнения запроса XPath, лучшим решением было бы сделать то, что вы можете с XPath, а затем работать по полученным списком DOMNodeList с любым объектом методов и свойств по мере необходимости.

Заключение

XPath представляет собой отличный способ сокращения количества кода, пишите его для ускорения выполнения кода при работе с XML-данными. Не являясь частью официальной спецификации DOM, дополнительные функциональные возможности, которые предоставляет PHP DOM позволяет продлить срок нормальной функции XPath с пользовательской функциональностью. Это очень мощное средство, с увеличением функции XPath.

1 комментарий на тему “PHP DOM: с Помощью XPath”

  1. комментарии

    Благодарю за пост, из того, что я нашел это вообще единственный пост, где использовали собственный пример, для registerPHPFunctions… Если вам не трудно, выложите, пример использования strpos???

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML -теги и атрибуты: <a href= http://pixelcom.crimea.ua/"" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>