phpQuery - szybkie parsowanie dokumentów na podobieństwo jQuery

Artykuł dodany: 31 października 2011. Ostatnia modyfikacja: 31 października 2011.

Stopień trudności (1 - dla początkujących, 5 - dla ekspertów): 3

Zapewne nieraz zdarzało Wam się korzystać z javascriptu aby przetworzyć zawartość wygenerowanego po stronie serwera dokumentu. Czy to zaczynając od prostego kolorowania bloków, poprzez obsługę zdarzeń, tworzenie nowych okien aż po zaawansowane aplikacje na kształt Gmail. Ze względu na różnice w przeglądarkach i dla własnej wygody prędzej czy później mieliście styczność z gotowymi bibliotekami JS – YUI, Dojo toolkit, Mootools, ExtJs (Sencha), czy wreszcie przez wielu krytykowaną, ale jednak niezwykle popularną biblioteką jQuery. Biblioteki te pomimo wielu różnic mają kilka wspólnych cech – oferują prosty dostęp do drzewa DOM za pomocą selektorów CSS oraz niezwykle intuicyjne jego przetwarzanie. Po stronie serwera również istnieje możliwość dostępu do dokumentu za pomocą parsera DOM jednakże jest to czynność dużo bardziej skomplikowana, zwłaszcza dla zaawansowanych struktur. Z pomocą może przyjść repozytorium kodu Google i jeden bardzo użyteczny projekt – phpQuery.

Aby otrzymać dostęp do struktury dokumentu w PHP należy zainstalować rozszerzenie DOM. Pierwszy rzut oka w dokumentację wystarczy aby stwierdzić, że posiada ono ogromną ilość metod, jest trudne w użyciu a jeśli nie macie doświadczenia w programowaniu obiektowym niewiele z tego zrozumiecie. Prosty dostęp do danych można zaprezentować takim oto kodem:

przykladDom.xml

<?xml version="1.0" encoding="UTF-8"?>
<mynotes>
  <note id="1">
    <tasks>Zrobić zakupy</tasks>
    <details>Żona nie daje mi spokoju o proszek do prania. Pamiętać o tym!!!</details>
  </note>
  <note id="2">
    <tasks>Urodziny szefowej Basi</tasks>
    <details>Kwiaty i ciasteczka bo inaczej będzie zła.</details>
  </note>
</mynotes>
<?php
  $objDOM = new DOMDocument();
  $objDOM->load("przykladDom.xml");

  $note = $objDOM->getElementsByTagName("note");

  foreach ( $note as $value ) {
    $tasks = $value->getElementsByTagName("tasks");
    $task  = $tasks->item(0)->nodeValue;

    $details = $value->getElementsByTagName("details");
    $detail  = $details->item(0)->nodeValue;

    $id = $value->getAttribute("id");

    echo "Notatka $id: $task :: $detail <br>";
  }  

Powyższy kod PHP iteruje po każdym elemencie drzewa, pobiera wartości dla elementów “tasks”, “details” oraz atrybut “id” i wypisuje ich wartość. Wynikiem działania skryptu będzie:

Notatka 1: Zrobić zakupy :: Żona nie daje mi spokoju o proszek do prania. Pamiętać o tym!!! <br>
Notatka 2: Urodziny szefowej Basi :: Kwiaty i ciasteczka bo inaczej będzie zła. <br> 

Kod nie jest jeszcze aż tak skomplikowany ale wyglądałby tym gorzej im więcej danych byłoby w nim zawartych.

Przyjrzyjmy się teraz bibliotece phpQuery. Pierwsze co zwraca uwagę na stronie wiki to odwołania do dokumentacji jQuery. Większość selektorów i metod działa identycznie. Oczywiście ze względu na pewne różnice w językach inaczej iterujemy przez obiekty (implementacja biblioteki SPL), możemy również wstawiać kod PHP jako wartość (attrPHP, wrapPHP itp).

Dokument XML, podobnie jak w typowych metodach DOM, możemy wczytać ze stringa albo zewnętrznego pliku. Po pobraniu biblioteki i jej rozpakowaniu dołączmy kod do naszego testowego pliku. Nazwijmy go

docPQ.php

require 'phpQuery/phpQuery.php';

i wczytajmy zawartość utworzonego wcześniej pliku XML

$xml = phpQuery::newDocumentFileXML('przykladDom.xml');

Dostępne metody to:

phpQuery::newDocument($html, $contentType = null)
phpQuery::newDocumentFile($file, $contentType = null)
phpQuery::newDocumentHTML($html, $charset = 'utf-8')
phpQuery::newDocumentXHTML($html, $charset = 'utf-8')
phpQuery::newDocumentXML($html, $charset = 'utf-8')
phpQuery::newDocumentPHP($html, $contentType = null)
phpQuery::newDocumentFileHTML($file, $charset = 'utf-8')
phpQuery::newDocumentFileXHTML($file, $charset = 'utf-8')
phpQuery::newDocumentFileXML($file, $charset = 'utf-8')
phpQuery::newDocumentFilePHP($file, $contentType)

Możemy również ustawić dokument domyślny za pomocą statycznej metody

phpQuery::selectDocument($xml);

Powoduje to wewnętrzne przypisane unikalnego identyfikatora dokumentu. Po tej operacji możemy już operować na wczytanym pliku. Na początek najbardziej interesuje nas wypisanie tych samych wartości do których dostęp uzyskaliśmy metodami DOM. Tak jak za pomocą funkcji jQuery() dopasowujemy selektory, tak naszym odpowiednikiem w PHP jest funkcja pq().

<?php
require 'phpQuery/phpQuery.php';

$xml = phpQuery::newDocumentFileXML('przykladDom.xml');
phpQuery::selectDocument($xml);

foreach (pq('mynotes note') as $value) {
  $task = pq($value)->find('tasks')->text();
  $detail = pq($value)->find('details')->text();
  $id = pq($value)->attr('id');
  echo "Notatka $id: $task :: $detail <br>";
}

pq(‘mynotes note’) wyszukuje wszystkie elementy “note” które iterujemy konstrukcją foreach. Co ważne, zmienna $value po tej operacji to zwykły węzeł DOM na który działają wszystkie metody klasy DOMElement czyli na przykład:

$id = $value->getAttribute('id');

Konstrukcją pq($value) przekształcamy dany węzeł ponownie na obiekt phpQuery, wyszukujemy elementy “tasks” i “details” oraz pobieramy ich wartość metodą text(). Prostsze, szybsze, bardziej czytelne oraz o większej funkcjonalności. Jeśli spojrzysz na oryginalny kod wybieramy tylko pierwszy węzeł ($tasks->item(0)) co oznacza że gdyby zadań było kilka dla notatki, musimy ponownie iterować dane.

Jako że phpQuery implementuje ArrayAccess nasz plik możemy traktować jako zwykłą tablicę.

foreach ($xml['mynotes note'] as $value) {...}

A za pomocą interface’u Countable::count policzyć ilość elementów:

echo 'Elementów `note`: '.count(pq('mynotes note'));

O ile tylko posiadasz PHP w wersji co najmniej 5.3 możesz jeszcze bardziej upodobnić swój kod do jQuery za pomocą funkcji anonimowych.

pq('mynotes note')->each(function($value) {
  $task = pq($value)->find('tasks')->text();
  $detail = pq($value)->find('details')->text();
  $id = $value->getAttribute('id');
  echo "Notatka $id: $task :: $detail <br>";
});

Oczywiście phpQuery to nie tylko wybieranie elementów ale również tworzenie nowych i całościowe przekształcenia.

<?php

require 'phpQuery/phpQuery.php';

$xml = phpQuery::newDocumentFileXML('przykladDom.xml');

$html = phpQuery::newDocumentHTML('<html><body><ul/></body></html>');

phpQuery::selectDocument($xml);

foreach (pq('tasks') as $tasks) {
  $html['ul']->append("<li>$tasks->nodeValue</li>");
}

$html['ul li:odd']->attr('style', 'color: #f4f4f4');

echo $html;

Zmiennej $html przypisaliśmy podstawowy dokument HTML. Wybraliśmy wszystkie zadania z listy i wstawiliśmy je do elementu “ul”. Parzyste rzędy uzyskują kolor #f4f4f4.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"></head>
<body><ul>
<li>Zrobić zakupy</li>
<li style="color: #f4f4f4">Urodziny szefowej Basi</li>
</ul></body>
</html>

Mostek do Zend_Http_Client

Za pomocą phpQuery możliwe jest także pobieranie zdalnej zawartości z innych serwerów oraz wysyłanie danych. Funkcja realizowana jest za pomocą dołączonej biblioteki Zenda. Aby rozpocząć transfer należy zezwolić na dostęp dla domeny a następnie pobrać treść:

phpQuery::ajaxAllowHost('google.com');
$zendHttp = phpQuery::get('http://google.com/');
echo $zendHttp->getLastResponse()->getBody();

Jako wynik otrzymaliśmy kompletną stronę główną Google którą następnie można dowolnie przekształcać.

Podsumowanie

Niewątpliwie phpQuery to bardzo interesujący projekt. Przy mądrym korzystaniu z selektorów i właściwości PHP potrafi ogromnie ułatwić życie. Martwi tylko trochę aktualny brak wsparcia. Twórcą projektu jest Polak Tobiasz Cudnik a ostatnie naprawione błędy pochodzą z 2009 roku. Jednak niewiele zmieniło się przez ostatnich kilka lat tak więc biblioteki można spokojnie używać dalej. Tym bardziej że dobrej alternatywy do tej pory nie widać na horyzoncie.

Komentarze

Nie ma jeszcze żadnych komentarzy do wyświetlenia. Może chcesz zostać pierwszą osobą która podzieli się swoją opinią?

Dodaj komentarz

*
Nazwa zostanie wyświetlona wraz z komentarzem. Możesz też utworzyć nowe konto w serwisie, dzięki czemu uzyskasz dodatkową funkcjonalność.
*
Akceptowana jest ograniczona składnia Textile. Wszystkie tagi HTML zostaną usunięte.