1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Tester;
9:
10:
11: 12: 13: 14: 15:
16: class DomQuery extends \SimpleXMLElement
17: {
18:
19: 20: 21:
22: public static function fromHtml($html)
23: {
24: if (strpos($html, '<') === FALSE) {
25: $html = '<body>' . $html;
26: }
27: $dom = new \DOMDocument();
28: $dom->loadHTML($html);
29: return simplexml_import_dom($dom, __CLASS__);
30: }
31:
32:
33: 34: 35:
36: public static function fromXml($xml)
37: {
38: return simplexml_load_string($xml, __CLASS__);
39: }
40:
41:
42: 43: 44: 45:
46: public function find($selector)
47: {
48: return $this->xpath(self::css2xpath($selector));
49: }
50:
51:
52: 53: 54: 55:
56: public function has($selector)
57: {
58: return (bool) $this->find($selector);
59: }
60:
61:
62: 63: 64: 65:
66: public static function css2xpath($css)
67: {
68: $xpath = '//*';
69: preg_match_all('/
70: ([#.:]?)([a-z][a-z0-9_-]*)| # id, class, pseudoclass (1,2)
71: \[([a-z0-9_-]+)(?:([~*^$]?)=([^\]]+))?\]| # [attr=val] (3,4,5)
72: \s*([>,+~])\s*| # > , + ~ (6)
73: (\s+)| # whitespace (7)
74: (\*) # * (8)
75: /ix', trim($css), $matches, PREG_SET_ORDER);
76: foreach ($matches as $m) {
77: if ($m[1] === '#') {
78: $xpath .= "[@id='$m[2]']";
79: } elseif ($m[1] === '.') {
80: $xpath .= "[contains(concat(' ', normalize-space(@class), ' '), ' $m[2] ')]";
81: } elseif ($m[1] === ':') {
82: throw new \InvalidArgumentException('Not implemented.');
83: } elseif ($m[2]) {
84: $xpath = rtrim($xpath, '*') . $m[2];
85: } elseif ($m[3]) {
86: $attr = '@' . strtolower($m[3]);
87: if (!isset($m[5])) {
88: $xpath .= "[$attr]";
89: continue;
90: }
91: $val = trim($m[5], '"\'');
92: if ($m[4] === '') {
93: $xpath .= "[$attr='$val']";
94: } elseif ($m[4] === '~') {
95: $xpath .= "[contains(concat(' ', normalize-space($attr), ' '), ' $val ')]";
96: } elseif ($m[4] === '*') {
97: $xpath .= "[contains($attr, '$val')]";
98: } elseif ($m[4] === '^') {
99: $xpath .= "[starts-with($attr, '$val')]";
100: } elseif ($m[4] === '$') {
101: $xpath .= "[substring($attr, string-length($attr)-0)='$val']";
102: }
103: } elseif ($m[6] === '>') {
104: $xpath .= '/*';
105: } elseif ($m[6] === ',') {
106: $xpath .= '|//*';
107: } elseif ($m[6] === '~') {
108: $xpath .= '/following-sibling::*';
109: } elseif ($m[6] === '+') {
110: throw new \InvalidArgumentException('Not implemented.');
111: } elseif ($m[7]) {
112: $xpath .= '//*';
113: }
114: }
115: return $xpath;
116: }
117:
118: }
119: