Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.00% covered (danger)
48.00%
12 / 25
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
HtmlXPath
48.00% covered (danger)
48.00%
12 / 25
33.33% covered (danger)
33.33%
2 / 6
41.56
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 node
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 toArray
41.67% covered (danger)
41.67%
5 / 12
0.00% covered (danger)
0.00%
0 / 1
13.15
 query
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 evaluate
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 collections
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core\Html;
6
7use DOMComment;
8use DOMDocument;
9use DOMElement;
10use DOMNode;
11use DOMNodeList;
12use DOMText;
13use DOMXPath;
14use PeServer\Core\Collection\Arr;
15use PeServer\Core\Collection\Collections as Collections;
16use PeServer\Core\Html\HtmlCommentElement;
17use PeServer\Core\Html\HtmlDocument;
18use PeServer\Core\Html\HtmlTagElement;
19use PeServer\Core\Html\HtmlNodeBase;
20use PeServer\Core\Html\HtmlTextElement;
21use PeServer\Core\Text;
22use PeServer\Core\Throws\HtmlXPathException;
23
24/**
25 * `DOMXPath` ラッパー。
26 */
27class HtmlXPath
28{
29    #region variable
30
31    /**
32     */
33    public readonly DOMXPath $path;
34
35    #endregion
36
37    public function __construct(
38        private readonly HtmlDocument $document,
39        private readonly ?HtmlTagElement $element
40    ) {
41        $this->path = new DOMXPath($document->raw);
42    }
43
44    #region function
45
46    private function node(): ?DOMNode
47    {
48        if ($this->element === null) {
49            return null;
50        }
51
52        return $this->element->raw;
53    }
54
55    //@phpstan-ignore-next-line
56    private function toArray(DOMNodeList $nodeList): array
57    {
58        $result = [];
59        foreach ($nodeList as $node) {
60            if ($node instanceof DOMElement) {
61                $result[] = new HtmlTagElement($this->document, $node);
62            } elseif ($node instanceof DOMText) {
63                $result[] = new HtmlTextElement($this->document, $node);
64            } elseif ($node instanceof DOMComment) {
65                $result[] = new HtmlCommentElement($this->document, $node);
66            } elseif ($node instanceof DOMDocument) {
67                $result[] = new HtmlDocument();
68            } else {
69                throw new HtmlXPathException(Text::dump($node));
70            }
71        }
72        return $result;
73    }
74
75    /**
76     * 与えられた XPath 式を評価する
77     *
78     * https://www.php.net/manual/domxpath.query.php
79     *
80     * @param string $expression
81     * @return HtmlNodeBase[]
82     * @throws HtmlXPathException
83     */
84    public function query(string $expression): array
85    {
86        $nodeList = $this->path->query($expression, $this->node());
87        if ($nodeList === false) {
88            throw new HtmlXPathException();
89        }
90
91        return $this->toArray($nodeList);
92    }
93
94    /**
95     * 与えられた XPath 式を評価し、可能であれば結果を返す
96     *
97     * https://www.php.net/manual/domxpath.evaluate.php
98     *
99     * @param string $expression
100     * @return HtmlNodeBase[]
101     * @throws HtmlXPathException
102     */
103    public function evaluate(string $expression): array
104    {
105        $nodeList = $this->path->evaluate($expression, $this->node());
106        if ($nodeList === false) {
107            throw new HtmlXPathException();
108        }
109
110        return $this->toArray($nodeList);
111    }
112
113    /**
114     * `evaluate` 結果を `Collections` として返す。
115     *
116     * @param string $expression
117     * @return Collections
118     * @phpstan-return Collections<array-key, HtmlNodeBase>
119     * @throws HtmlXPathException
120     */
121    public function collections(string $expression): Collections
122    {
123        return Collections::from($this->evaluate($expression));
124    }
125
126    #endregion
127}