Zend_Dom_Query
DOMDocumentを使うのにCSSセレクタが使えるというので、Server Side Dom Scriptingに使ってみた。
処理の流れとしては、
Zend_Dom_Query_Resultオブジェクトは検索結果のノードリストと検索のベースになったDomDocumentオブジェクトを保持しており、Iteretorで結果リストに対する処理を行うことができる。
検索結果オブジェクトに対する処理については、Zend_Dom_Query_Resultオブジェクト内のDomDocumentオブジェクトには反映される。Zend_Dom_Query_Result::getDocumentはDomDocumentオブジェクトが返る。Zend_Dom_Query::getDocument()は元の文字列なので注意。DOMDocumentオブジェクトは検索の度にnewされるので、クエリと変更を複数回行うといった処理には向かない。
http://framework.zend.com/issues/browse/ZF-3950
ここに、ちょっとその辺のことが書いてある。
Zend_Dom_Queryはリードオンリーな雰囲気。
MVC アプリケーションの機能テストを支援するために作られたものですが、 スクリーンスクレイパーを手早く作成するためにも使うことができます。
http://framework.zend.com/manual/ja/zend.dom.query.html
DOMの経験値低め
これまで、DOMScriptingは避け気味で来ていたので、技術基盤が薄い。勉強の素材としては、DomDocumentを使っているらしいMoodleか、DOMライクなPHPTALがいいかな。
継続処理可能なDOMDocumentにした。
DOMDocumentを継承しているので、普通にDOMScriptingできる。特に難しいことはしていないので問題はないと思う。
ZFに還元したいところなんだけど、Proposalとかを書くのはちょっとしんどいか。
こういう場合、クラス名とか、勝手につけたけど、どういう手順にするのが正しいのか疑問・・・
<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@zend.com so we can send you a copy immediately. * * @category Zend * @package Zend_Dom * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Dom_DOMDocument extends DOMDocument { /** * Perform a CSS selector query * * @param string $query * @return Zend_Dom_Query_Result */ public function query($query) { $xpathQuery = Zend_Dom_Query_Css2Xpath::transform($query); return $this->queryXpath($xpathQuery, $query); } /** * Perform an XPath query * * @param string $xpathQuery * @param string $query CSS selector query * @return Zend_Dom_Query_Result */ public function queryXpath($xpathQuery, $query = null) { $nodeList = $this->_getNodeList($xpathQuery); return new Zend_Dom_Query_Result($query, $xpathQuery, $this, $nodeList); } /** * Prepare node list * * @param DOMDocument $document * @param string|array $xpathQuery * @return array */ protected function _getNodeList($xpathQuery) { $xpath = new DOMXPath($this); $xpathQuery = (string) $xpathQuery; if (preg_match_all('|\[contains\((@[a-z0-9_-]+),\s?\' |i', $xpathQuery, $matches)) { foreach ($matches[1] as $attribute) { $queryString = '//*[' . $attribute . ']'; $attributeName = substr($attribute, 1); $nodes = $xpath->query($queryString); foreach ($nodes as $node) { $attr = $node->attributes->getNamedItem($attributeName); $attr->value = ' ' . $attr->value . ' '; } } } return $xpath->query($xpathQuery); } }
使い方
<?php $document = <<<EOB <html> <head><title>test</title></head> <div> <table> <tr> <td class="foo"> <div> Lorem ipsum <span class="bar"> <a href="/foo/bar" id="one">One</a> <a href="/foo/baz" id="two">Two</a> <a href="/foo/bat" id="three">Three</a> <a href="/foo/bla" id="four">Four</a> </span> </div> </td> </tr> </table> </div> </html> EOB; require_once('Zend/Dom/DOMDocument.php'); $dom = new Zend_Dom_DOMDocument(); $dom->loadXML($document); $results = $dom->query('.foo .bar a'); foreach ($results as $result) { // $result は DOMElement です /** * @var DOMElement $result */ //検索結果にtest="hoge"をセットしています。 $result->setAttribute('test', 'hoge'); } Zend_Debug::dump($dom->saveXML()); Zend_Debug::dump($dom);