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--EXPECTF-- 297Node Object 298( 299 [children:protected] => Array 300 ( 301 ) 302 303 [parent:protected] => Node Object 304 ( 305 [children:protected] => Array 306 ( 307 [0] => Node Object 308 *RECURSION* 309 ) 310 311 [parent:protected] => Node Object 312 ( 313 [children:protected] => Array 314 ( 315 [0] => Node Object 316 *RECURSION* 317 ) 318 319 [parent:protected] => 320 [data:protected] => HEAD 321 [uid:protected] => %s 322 [index:protected] => 0 323 [assureUnique:protected] => 324 [storage:ArrayIterator:private] => Array 325 ( 326 ) 327 328 ) 329 330 [data:protected] => value1 331 [uid:protected] => 1 332 [index:protected] => 1 333 [assureUnique:protected] => 334 [storage:ArrayIterator:private] => Array 335 ( 336 ) 337 338 ) 339 340 [data:protected] => value2 341 [uid:protected] => 2 342 [index:protected] => 0 343 [assureUnique:protected] => 344 [storage:ArrayIterator:private] => Array 345 ( 346 ) 347 348) 349