Passed
Branch scrutinizer (dd8772)
by Thomas
03:03
created

Resolver::closureResolve()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Sulao\HtmlQuery;
4
5
use Closure;
6
use DOMDocument, DOMNode, DOMNodeList, DOMXPath;
7
use ReflectionFunction;
8
9
/**
10
 * Trait Selector
11
 *
12
 * @package Sulao\HtmlQuery
13
 */
14
trait Resolver
15
{
16
    /**
17
     * @var DOMDocument
18
     */
19
    protected $doc;
20
21
    /**
22
     * @var DOMNode[]
23
     */
24
    protected $nodes;
25
26
    /**
27
     * Selector constructor.
28
     *
29
     * @param DOMDocument                          $doc
30
     * @param DOMNode|DOMNode[]|DOMNodeList|static $nodes
31
     *
32
     * @return static
33
     */
34
    abstract public function __construct(DOMDocument $doc, $nodes);
35
36
    /**
37
     * Get the descendants of each matched node, filtered by a selector.
38
     *
39
     * @param string|DOMNode|DOMNode[]|DOMNodeList|static $selector
40
     *
41
     * @return static
42
     */
43
    abstract public function find($selector);
44
45
    /**
46
     * Resolve DOMNode(s) to a static instance.
47
     *
48
     * @param DOMNode|DOMNode[]|DOMNodeList|static $nodes
49
     *
50
     * @return static
51
     */
52
    protected function resolve($nodes)
53
    {
54
        if ($nodes instanceof static) {
55
            return $nodes;
56
        }
57
58
        return new static($this->doc, $nodes);
59
    }
60
61
    /**
62
     * If the parameter is a css selector, get the descendants
63
     * of dom document filtered by the css selector.
64
     * If the parameter is selection, resolve that selection to static object.
65
     *
66
     * @param string|DOMNode|DOMNode[]|DOMNodeList|static $selector
67
     *
68
     * @return static
69
     */
70
    protected function targetResolve($selector)
71
    {
72
        if (is_string($selector)) {
73
            return $this->resolve($this->doc)->find($selector);
74
        }
75
76
        return $this->resolve($selector);
77
    }
78
79
    /**
80
     * If the parameter is string, consider it as raw html,
81
     * then create document fragment for it.
82
     * If the parameter is selection, resolve that selection to static instance.
83
     *
84
     * @param string|DOMNode|DOMNode[]|DOMNodeList|static $content
85
     *
86
     * @return static
87
     */
88
    protected function contentResolve($content)
89
    {
90
        if (is_string($content)) {
91
            return $this->htmlResolve($content);
92
        }
93
94
        return $this->resolve($content);
95
    }
96
97
    /**
98
     * Resolve the html content to static instance.
99
     *
100
     * @param string $html
101
     *
102
     * @return static
103
     */
104
    protected function htmlResolve(string $html)
105
    {
106
        $frag = $this->doc->createDocumentFragment();
107
        $frag->appendXML($html);
108
109
        return $this->resolve($frag);
110
    }
111
112
    /**
113
     * Resolve the nodes under the relation to static instance.
114
     * up to but not including the node matched by the $until selector.
115
     *
116
     * @param string $relation
117
     * @param string|DOMNode|DOMNode[]|DOMNodeList|static $until
118
     *
119
     * @return static
120
     */
121
    protected function relationResolve(string $relation, ?string $until = null)
122
    {
123
        $untilNodes = !is_null($until)
124
            ? $this->targetResolve($until)->nodes
125
            : [];
126
127
        $nodes = [];
128
        foreach ($this->nodes as $node) {
129
            while (($node = $node->$relation)
130
                && $node->nodeType !== XML_DOCUMENT_NODE
131
            ) {
132
                if ($node->nodeType !== XML_ELEMENT_NODE) {
133
                    continue;
134
                }
135
136
                if (in_array($node, $untilNodes, true)) {
137
                    break;
138
                }
139
140
                if (!in_array($node, $nodes, true)) {
141
                    $nodes[] = $node;
142
                }
143
            }
144
        }
145
146
        return $this->resolve($nodes);
147
    }
148
149
    /**
150
     * Get the class of the specified parameter of the closure.
151
     *
152
     * @param Closure $function
153
     * @param int     $index    Which parameter of the closure, starts with 0
154
     *
155
     * @return string
156
     */
157
    protected function getClosureClass(Closure $function, int $index)
158
    {
159
        $reflection = new ReflectionFunction($function);
160
        $parameters = $reflection->getParameters();
161
162
        if (!empty($parameters) && array_key_exists($index, $parameters)) {
163
            $class = $parameters[$index]->getClass();
164
            if ($class) {
0 ignored issues
show
introduced by
$class is of type ReflectionClass, thus it always evaluated to true.
Loading history...
165
                return $class->getName();
166
            }
167
        }
168
169
        return '';
170
    }
171
172
    /**
173
     * Resolve the node to static or HtmlNode instance or leaving it as DOMNode,
174
     * Then pass it to closure
175
     *
176
     * @param string  $class
177
     * @param DOMNode $node
178
     *
179
     * @return DOMNode|HtmlNode|static
180
     */
181
    protected function closureResolve(string $class, DOMNode $node)
182
    {
183
        if ($class === static::class) {
184
            return $this->resolve($node);
185
        } elseif ($class === HtmlNode::class) {
186
            return new HtmlNode($node);
187
        }
188
189
        return $node;
190
    }
191
192
    /**
193
     * Resolve the xpath to static instance.
194
     *
195
     * @param string $xpath
196
     *
197
     * @return static
198
     */
199
    protected function xpathFind(string $xpath)
200
    {
201
        $nodes = [];
202
        foreach ($this->nodes as $node) {
203
            $nodes = array_merge($nodes, $this->xpathQuery($xpath, $node));
204
        }
205
206
        $nodes = Helper::strictArrayUnique($nodes);
207
208
        return $this->resolve($nodes);
209
    }
210
211
    /**
212
     * Query xpath to an array of DOMNode
213
     *
214
     * @param string       $xpath
215
     * @param DOMNode|null $node
216
     *
217
     * @return DOMNode[]
218
     */
219
    protected function xpathQuery(
220
        string $xpath,
221
        ?DOMNode $node = null
222
    ): array {
223
        $docXpath = new DOMXpath($this->doc);
224
        $nodeList = $docXpath->query($xpath, $node);
225
226
        if (!($nodeList instanceof DOMNodeList)) {
227
            return [];
228
        }
229
230
        return iterator_to_array($nodeList);
231
    }
232
}
233