1 | <?php declare(strict_types = 1); |
||
2 | namespace Templado\Engine; |
||
3 | |||
4 | use DOMNode; |
||
5 | use DOMNodeList; |
||
6 | use DOMXPath; |
||
7 | |||
8 | class XPathSelector implements Selector { |
||
9 | |||
10 | /** @var string */ |
||
11 | private $queryString; |
||
12 | |||
13 | /** @var array<string, string> */ |
||
14 | private $prefixMap = []; |
||
15 | |||
16 | 57 | public function __construct(string $query) { |
|
17 | 57 | $this->queryString = $query; |
|
18 | 57 | } |
|
19 | |||
20 | 6 | public function registerPrefix(string $prefix, string $uri): void { |
|
21 | 6 | $this->prefixMap[$prefix] = $uri; |
|
22 | 6 | } |
|
23 | |||
24 | 57 | public function select(DOMNode $context): Selection { |
|
25 | 57 | $backup = $this->toggleErrorHandling(true); |
|
26 | 57 | $result = $this->getXPath($context)->query( |
|
27 | 57 | $this->queryString, |
|
28 | 19 | $context |
|
29 | ); |
||
30 | |||
31 | 57 | if (!$result instanceof DOMNodeList) { |
|
0 ignored issues
–
show
introduced
by
![]() |
|||
32 | 42 | $error = \libxml_get_last_error(); |
|
33 | 42 | $this->toggleErrorHandling($backup); |
|
34 | |||
35 | 42 | throw new XPathSelectorException( |
|
36 | 42 | \sprintf( |
|
37 | 42 | '%s: "%s"', |
|
38 | 42 | \trim($error->message), |
|
39 | 42 | $this->queryString |
|
40 | ), |
||
41 | 42 | $error->code |
|
42 | ); |
||
43 | } |
||
44 | 15 | $this->toggleErrorHandling($backup); |
|
45 | |||
46 | 15 | return new Selection($result); |
|
47 | } |
||
48 | |||
49 | 57 | private function getXPath(DOMNode $node): DOMXPath { |
|
50 | 57 | $xp = new DOMXPath($node->ownerDocument); |
|
51 | 57 | $xp->registerPhpFunctions(); |
|
52 | |||
53 | 57 | if (empty($this->prefixMap) || isset($this->prefixMap['html'])) { |
|
54 | 51 | $this->prefixMap['html'] = 'http://www.w3.org/1999/xhtml'; |
|
55 | } |
||
56 | |||
57 | 57 | foreach ($this->prefixMap as $prefix => $uri) { |
|
58 | 57 | $xp->registerNamespace($prefix, $uri); |
|
59 | } |
||
60 | |||
61 | 57 | return $xp; |
|
62 | } |
||
63 | |||
64 | 57 | private function toggleErrorHandling(bool $mode): bool { |
|
65 | 57 | $backup = \libxml_use_internal_errors($mode); |
|
66 | 57 | \libxml_clear_errors(); |
|
67 | |||
68 | 57 | return $backup; |
|
69 | } |
||
70 | } |
||
71 |