Completed
Branch scrutinizer (79ce98)
by Thomas
02:53
created

Selection::eq()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
namespace Sulao\HtmlQuery;
4
5
use ArrayAccess, ArrayIterator;
6
use Closure, Countable;
7
use DOMDocument, DOMNode;
8
use DOMNodeList;
9
use IteratorAggregate;
10
use Traversable;
11
12
//https://codereview.stackexchange.com/questions/8410/divide-big-class-in-subclasses
13
//https://stackoverflow.com/questions/7955257/php-how-to-divide-a-class-into-multiple-class
14
15
/**
16
 * Class Selection
17
 *
18
 * @package Sulao\HtmlQuery
19
 */
20
abstract class Selection implements Countable, IteratorAggregate, ArrayAccess
21
{
22
    use Selector;
0 ignored issues
show
Bug introduced by
The trait Sulao\HtmlQuery\Selector requires the property $nodeType which is not provided by Sulao\HtmlQuery\Selection.
Loading history...
23
24
    /**
25
     * Return DOMDocument
26
     *
27
     * @return DOMDocument
28
     */
29
    public function getDoc(): DOMDocument
30
    {
31
        return $this->doc;
32
    }
33
34
    /**
35
     * Return DOMNodes
36
     *
37
     * @return DOMNode[]
38
     */
39
    public function getNodes(): array
40
    {
41
        return $this->nodes;
42
    }
43
44
    /**
45
     * Iterate over the matched nodes, executing the function for each node.
46
     *
47
     * @param Closure $function function(DOMNode|HtmlQuery $node, $index)
48
     * @param bool    $reverse  Iterate over the nodes reversely
49
     *
50
     * @return static
51
     */
52
    public function each(Closure $function, bool $reverse = false)
53
    {
54
        $resolve = $this->shouldResolve($function, 0);
55
56
        $nodes = $reverse ? array_reverse($this->nodes, true) : $this->nodes;
57
        foreach ($nodes as $index => $node) {
58
            $node = $resolve ? $this->resolve($node) : $node;
59
            $function($node, $index);
60
        }
61
62
        return $this;
63
    }
64
65
    /**
66
     * Pass each matched node through a function,
67
     * producing an array containing the return values.
68
     *
69
     * @param Closure $function function($index, DOMNode|HtmlQuery $node)
70
     *
71
     * @return array
72
     */
73
    public function map(Closure $function)
74
    {
75
        $resolve = $this->shouldResolve($function, 0);
76
77
        $data = [];
78
        foreach ($this->nodes as $index => $node) {
79
            $node = $resolve ? $this->resolve($node) : $node;
80
            $data[] = $function($node, $index);
81
        }
82
83
        return $data;
84
    }
85
86
    /**
87
     * Pass each matched node through a function,
88
     * Break and return true when the function with the first node return true.
89
     *
90
     * @param Closure $function function($index, DOMNode|HtmlQuery $node)
91
     *
92
     * @return bool
93
     */
94
    public function mapAnyTrue(Closure $function)
95
    {
96
        $resolve = $this->shouldResolve($function, 0);
97
98
        foreach ($this->nodes as $index => $node) {
99
            $node = $resolve ? $this->resolve($node) : $node;
100
            if ($function($node, $index)) {
101
                return true;
102
            }
103
        }
104
105
        return false;
106
    }
107
108
    /**
109
     * Pass the first matched node through a function,
110
     * and return the return value of the function.
111
     *
112
     * @param Closure $function function(DOMNode|HtmlQuery $node)
113
     *
114
     * @return mixed|null
115
     */
116
    public function mapFirst(Closure $function)
117
    {
118
        if (!$this->count()) {
119
            return null;
120
        }
121
122
        $resolve = $this->shouldResolve($function, 0);
123
        $node = $resolve ? $this->resolve($this->nodes[0]) : $this->nodes[0];
124
125
        return $function($node);
126
    }
127
128
    /**
129
     * Reduce the current nodes to the one at the specified index.
130
     *
131
     * @param int $index
132
     *
133
     * @return static
134
     */
135
    public function eq(int $index)
136
    {
137
        $node = array_key_exists($index, $this->nodes)
138
            ? $this->nodes[$index]
139
            : [];
140
141
        return $this->resolve($node);
142
    }
143
144
    /**
145
     * Reduce the current nodes to the first one.
146
     *
147
     * @return static
148
     */
149
    public function first()
150
    {
151
        return $this->eq(0);
152
    }
153
154
    /**
155
     * Reduce the current nodes to the final one.
156
     *
157
     * @return static
158
     */
159
    public function last()
160
    {
161
        return $this->eq(count($this->nodes) - 1);
162
    }
163
164
165
    /**
166
     * Reduce the matched nodes to a subset specified by a range of indices.
167
     *
168
     * @param int      $offset
169
     * @param int|null $length
170
     *
171
     * @return static
172
     */
173
    public function slice(int $offset, ?int $length = null)
174
    {
175
        return $this->resolve(array_slice($this->nodes, $offset, $length));
176
    }
177
178
    /**
179
     * Return DOMNodes
180
     *
181
     * @return DOMNode[]
182
     */
183
    public function toArray(): array
184
    {
185
        return $this->getNodes();
186
    }
187
188
    public function unset(int $offset)
189
    {
190
        unset($this->nodes[$offset]);
191
        $this->nodes = array_values($this->nodes);
192
    }
193
194
    public function count(): int
195
    {
196
        return count($this->toArray());
197
    }
198
199
    public function getIterator()
200
    {
201
        return new ArrayIterator($this->toArray());
202
    }
203
204
    public function offsetSet($offset, $value)
205
    {
206
        if (!($value instanceof DOMNode)) {
207
            throw new Exception(
208
                'Expect an instance of DOMNode, '
209
                    . gettype($value) . ' given.'
210
            );
211
        }
212
213
        if (!in_array($value, $this->nodes, true)) {
214
            $this->nodes[] = $value;
215
        }
216
    }
217
218
    public function offsetExists($offset)
219
    {
220
        return isset($this->nodes[$offset]);
221
    }
222
223
    public function offsetUnset($offset)
224
    {
225
        $this->unset($offset);
226
    }
227
228
    public function offsetGet($offset)
229
    {
230
        return isset($this->nodes[$offset]) ? $this->nodes[$offset] : null;
231
    }
232
233
    /**
234
     * Validate the nodes
235
     *
236
     * @param DOMNode|DOMNode[]|DOMNodeList|static $nodes
237
     *
238
     * @return DOMNode[]
239
     * @throws Exception
240
     */
241
    protected function validateNodes($nodes)
242
    {
243
        if (empty($nodes)) {
244
            $nodes = [];
245
        } elseif ($nodes instanceof Traversable) {
246
            $nodes = iterator_to_array($nodes);
247
        } elseif ($nodes instanceof DOMNode || !is_array($nodes)) {
248
            $nodes = [$nodes];
249
        }
250
251
        $nodes = Helper::strictArrayUnique($nodes);
252
        foreach ($nodes as $node) {
253
            if (!($node instanceof DOMNode)) {
254
                throw new Exception(
255
                    'Expect an instance of DOMNode, '
256
                    . gettype($node) . ' given.'
257
                );
258
            }
259
260
            if ((!$node->ownerDocument && $node !== $this->doc)
261
                || ($node->ownerDocument && $node->ownerDocument !== $this->doc)
262
            ) {
263
                throw new Exception(
264
                    'The DOMNode does not belong to the DOMDocument.'
265
                );
266
            }
267
        }
268
269
        return $nodes;
270
    }
271
}
272