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|null $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