bug65328.phpt 6.1 KB

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