Passed
Branch scrutinizer (a800ff)
by Thomas
02:24
created

Selection::offsetUnset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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