xref: /PHP-8.0/ext/spl/tests/bug65328.phpt (revision 7aacc705)
1--TEST--
2Bug #65328 (Segfault when getting SplStack object Value)
3--FILE--
4<?php
5/**
6 * @author AlexanderC
7 */
8
9class Tree
10{
11    /**
12     * @var Node
13     */
14    protected $head;
15
16    /**
17     * @param Node $head
18     */
19    public function __construct(Node $head = null)
20    {
21        $this->head = $head ? : new Node('HEAD');
22    }
23
24    /**
25     * @return Node
26     */
27    public function getHead()
28    {
29        return $this->head;
30    }
31
32    /**
33     * @param mixed $uid
34     * @return Node|bool
35     */
36    public function find($uid)
37    {
38        $iterator = $this->getIterator();
39
40        /** @var Node $node */
41        foreach($iterator as $node) {
42            if($node->getUid() === $uid) {
43                return $node;
44            }
45        }
46
47        return false;
48    }
49
50    /**
51     * @param mixed $uid
52     * @return \SplStack
53     */
54    public function & findAll($uid)
55    {
56        $result = new \SplStack();
57
58        /** @var Node $node */
59        foreach($this->getIterator() as $node) {
60            if($node->getUid() == $uid) {
61                $result->push($node);
62            }
63        }
64
65        return $result;
66    }
67
68    /**
69     * @return \RecursiveIteratorIterator
70     */
71    public function getIterator()
72    {
73        return new \RecursiveIteratorIterator(
74            $this->head->getChildren(),
75            \RecursiveIteratorIterator::SELF_FIRST
76        );
77    }
78}
79
80class Node extends \RecursiveArrayIterator implements \Countable
81{
82    /**
83     * @var array
84     */
85    protected $children = [];
86
87    /**
88     * @var Node
89     */
90    protected $parent;
91
92    /**
93     * @var mixed
94     */
95    protected $data;
96
97    /**
98     * @var mixed
99     */
100    protected $uid;
101
102    /**
103     * @var int
104     */
105    protected $index = 0;
106
107    /**
108     * @var bool
109     */
110    protected $assureUnique;
111
112    /**
113     * @param mixed $data
114     * @param mixed $uid
115     * @param Node $parent
116     * @param bool $assureUnique
117     */
118    public function __construct($data, $uid = null, Node $parent = null, $assureUnique = false)
119    {
120        if(null !== $parent) {
121            $this->parent = $parent;
122        }
123
124        $this->data = $data;
125        $this->uid = $uid ? : uniqid(sha1(serialize($data)), true);
126        $this->assureUnique = $assureUnique;
127    }
128
129    /**
130     * @param mixed $uid
131     */
132    public function setUid($uid)
133    {
134        $this->uid = $uid;
135    }
136
137    /**
138     * @return mixed
139     */
140    public function getUid()
141    {
142        return $this->uid;
143    }
144
145    /**
146     * @param Node $child
147     */
148    public function addChild(Node $child)
149    {
150        $child->setParent($this);
151        $this->children[] = $child;
152    }
153
154    /**
155     * @param array $children
156     */
157    public function setChildren(array $children)
158    {
159        $this->children = $children;
160    }
161
162    /**
163     * @return array
164     */
165    public function getChildrenArray()
166    {
167        return $this->children;
168    }
169
170    /**
171     * @param mixed $data
172     */
173    public function setData($data)
174    {
175        $this->data = $data;
176    }
177
178    /**
179     * @return mixed
180     */
181    public function getData()
182    {
183        return $this->data;
184    }
185
186    /**
187     * @param Node $parent
188     * @throws \RuntimeException
189     */
190    public function setParent(Node $parent)
191    {
192        if(true === $this->assureUnique && !self::checkUnique($parent, $this->uid)) {
193            throw new \RuntimeException("Node uid is not unique in assigned node tree");
194        }
195
196        $this->parent = $parent;
197    }
198
199    /**
200     * @param Node $node
201     * @param mixed $uid
202     * @return bool
203     */
204    protected static function checkUnique(Node $node, $uid)
205    {
206        $headNode = $node;
207        do {
208            $headNode = $node;
209        } while($node = $node->getParent());
210
211        $tree = new Tree($headNode);
212
213        return !$tree->find($uid);
214    }
215
216    /**
217     * @return \IJsonRPC\Helpers\Tree\Node
218     */
219    public function getParent()
220    {
221        return $this->parent;
222    }
223
224    /**
225     * @return Node
226     */
227    public function current()
228    {
229        return $this->children[$this->index];
230    }
231
232    /**
233     * @return scalar
234     */
235    public function key()
236    {
237        return $this->index;
238    }
239
240    /**
241     * @return void
242     */
243    public function next()
244    {
245        ++$this->index;
246    }
247
248    /**
249     * @return void
250     */
251    public function rewind()
252    {
253        $this->index = 0;
254    }
255
256    /**
257     * @return bool
258     */
259    public function valid()
260    {
261        return array_key_exists($this->index, $this->children);
262    }
263
264    /**
265     * @return int
266     */
267    public function count()
268    {
269        return count($this->children);
270    }
271
272    /**
273     * @return bool
274     */
275    public function hasChildren()
276    {
277        return !empty($this->children);
278    }
279
280    /**
281     * @return \RecursiveArrayIterator
282     */
283    public function getChildren()
284    {
285        return new \RecursiveArrayIterator($this->children);
286    }
287}
288
289$tree = new Tree();
290$node1 = new Node('value1', 1);
291$tree->getHead()->addChild($node1);
292$node2 = new Node('value2', 2);
293$node1->addChild($node2);
294
295print_r($tree->findAll(2)->offsetGet(0));
296?>
297--EXPECTF--
298Node Object
299(
300    [children:protected] => Array
301        (
302        )
303
304    [parent:protected] => Node Object
305        (
306            [children:protected] => Array
307                (
308                    [0] => Node Object
309 *RECURSION*
310                )
311
312            [parent:protected] => Node Object
313                (
314                    [children:protected] => Array
315                        (
316                            [0] => Node Object
317 *RECURSION*
318                        )
319
320                    [parent:protected] =>
321                    [data:protected] => HEAD
322                    [uid:protected] => %s
323                    [index:protected] => 0
324                    [assureUnique:protected] =>
325                    [storage:ArrayIterator:private] => Array
326                        (
327                        )
328
329                )
330
331            [data:protected] => value1
332            [uid:protected] => 1
333            [index:protected] => 1
334            [assureUnique:protected] =>
335            [storage:ArrayIterator:private] => Array
336                (
337                )
338
339        )
340
341    [data:protected] => value2
342    [uid:protected] => 2
343    [index:protected] => 0
344    [assureUnique:protected] =>
345    [storage:ArrayIterator:private] => Array
346        (
347        )
348
349)
350