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