PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/system/php/spl/iterators/RecursiveIteratorIterator.php

https://gitlab.com/iranjith4/hhvm
PHP | 403 lines | 214 code | 33 blank | 156 comment | 35 complexity | 4fdb31d4316ffd7156e28e69c124de7f MD5 | raw file
  1. <?php
  2. // This doc comment block generated by idl/sysdoc.php
  3. /**
  4. * ( excerpt from
  5. * http://php.net/manual/en/class.recursiveiteratoriterator.php )
  6. *
  7. * Can be used to iterate through recursive iterators.
  8. *
  9. */
  10. class RecursiveIteratorIterator implements OuterIterator {
  11. const LEAVES_ONLY = 0;
  12. const SELF_FIRST = 1;
  13. const CHILD_FIRST = 2;
  14. const CATCH_GET_CHILD = 16;
  15. const STATE_NEXT = 10;
  16. const STATE_TEST = 11;
  17. const STATE_SELF = 12;
  18. const STATE_CHILD = 13;
  19. const STATE_START = 14;
  20. const NEXT_COMPLETE = 10;
  21. const NEXT_REPEAT = 11;
  22. private $iterators = array();
  23. private $originalIterator;
  24. private $mode;
  25. private $flags;
  26. private $maxDepth = -1;
  27. // Flag for checking whether to call
  28. // begin/endIteration() or not
  29. private $inIteration = false;
  30. // This doc comment block generated by idl/sysdoc.php
  31. /**
  32. * ( excerpt from
  33. * http://php.net/manual/en/recursiveiteratoriterator.construct.php )
  34. *
  35. * Creates a RecursiveIteratorIterator from a RecursiveIterator.
  36. *
  37. * @iterator mixed The iterator being constructed from. Either a
  38. * RecursiveIterator or IteratorAggregate.
  39. * @mode mixed Optional mode. Possible values are
  40. * RecursiveIteratorIterator::LEAVES_ONLY - The
  41. * default. Lists only leaves in iteration.
  42. * RecursiveIteratorIterator::SELF_FIRST - Lists leaves
  43. * and parents in iteration with parents coming first.
  44. * RecursiveIteratorIterator::CHILD_FIRST - Lists
  45. * leaves and parents in iteration with leaves coming
  46. * first.
  47. * @flags mixed Optional flag. Possible values are
  48. * RecursiveIteratorIterator::CATCH_GET_CHILD which
  49. * will then ignore exceptions thrown in calls to
  50. * RecursiveIteratorIterator::getChildren().
  51. *
  52. * @return mixed No value is returned.
  53. */
  54. public function __construct(\Traversable $iterator,
  55. $mode = RecursiveIteratorIterator::LEAVES_ONLY,
  56. $flags = 0) {
  57. if ($iterator && ($iterator instanceof IteratorAggregate)) {
  58. $iterator = $iterator->getIterator();
  59. }
  60. if (!$iterator || !($iterator instanceof RecursiveIterator)) {
  61. throw new InvalidArgumentException(
  62. "An instance of RecursiveIterator or IteratorAggregate creating " .
  63. "it is required"
  64. );
  65. }
  66. $this->iterators[] = array($iterator, self::STATE_START);
  67. $this->originalIterator = $iterator;
  68. $this->mode = (int) $mode;
  69. $this->flags = $flags;
  70. }
  71. // This doc comment block generated by idl/sysdoc.php
  72. /**
  73. * ( excerpt from
  74. * http://php.net/manual/en/recursiveiteratoriterator.getinneriterator.php
  75. * )
  76. *
  77. * Gets the current active sub iterator. Warning: This function is
  78. * currently not documented; only its argument list is available.
  79. *
  80. * @return mixed The current active sub iterator.
  81. */
  82. public function getInnerIterator() {
  83. $it = $this->iterators[count($this->iterators)-1][0];
  84. if (!$it instanceof RecursiveIterator) {
  85. throw new Exception(
  86. "inner iterator must implement RecursiveIterator"
  87. );
  88. }
  89. return $it;
  90. }
  91. // This doc comment block generated by idl/sysdoc.php
  92. /**
  93. * ( excerpt from
  94. * http://php.net/manual/en/recursiveiteratoriterator.current.php )
  95. *
  96. *
  97. * @return mixed The current elements value.
  98. */
  99. public function current() {
  100. return $this->getInnerIterator()->current();
  101. }
  102. // This doc comment block generated by idl/sysdoc.php
  103. /**
  104. * ( excerpt from
  105. * http://php.net/manual/en/recursiveiteratoriterator.key.php )
  106. *
  107. *
  108. * @return mixed The current key.
  109. */
  110. public function key() {
  111. return $this->getInnerIterator()->key();
  112. }
  113. // This doc comment block generated by idl/sysdoc.php
  114. /**
  115. * ( excerpt from
  116. * http://php.net/manual/en/recursiveiteratoriterator.next.php )
  117. *
  118. *
  119. * @return mixed No value is returned.
  120. */
  121. public function next() {
  122. $this->nextImpl();
  123. }
  124. private function nextImpl() {
  125. while ($this->nextInnerImpl() === self::NEXT_REPEAT) {
  126. /* loop */
  127. }
  128. }
  129. /* We maintain a stack of sub-iterators - each of which has a state.
  130. *
  131. * This walks the overall tree for one step, and updates the corresponding
  132. * iterator states as needed (current iterator, parent iterator, child
  133. * iterator).
  134. *
  135. * Returns self::NEXT_COMPLETE if that one step got us to the next position
  136. * (this changes depending on LEAVES_ONLY, SELF_FIRST, and CHILD_FIRST), or
  137. * SELF::NEXT_REPEAT if more steps are needed.
  138. */
  139. private function nextInnerImpl() {
  140. $it = $this->getInnerIterator();
  141. switch ($this->getInnerIteratorState()) {
  142. case self::STATE_NEXT:
  143. $it->next();
  144. // fallthrough
  145. case self::STATE_START:
  146. if (!$it->valid()) {
  147. if ($this->getDepth() > 0) {
  148. $this->endChildren();
  149. array_pop($this->iterators);
  150. return self::NEXT_REPEAT;
  151. }
  152. return self::NEXT_COMPLETE;
  153. }
  154. $this->setInnerIteratorState(self::STATE_TEST);
  155. return self::NEXT_REPEAT;
  156. case self::STATE_TEST:
  157. if ($this->callHasChildren()) {
  158. if ($this->maxDepth == -1 || $this->maxDepth > $this->getDepth()) {
  159. switch ($this->mode) {
  160. case self::LEAVES_ONLY:
  161. case self::CHILD_FIRST:
  162. // We never look at SELF in LEAVES_ONLY
  163. $this->setInnerIteratorState(self::STATE_CHILD);
  164. return self::NEXT_REPEAT;
  165. case self::SELF_FIRST:
  166. $this->setInnerIteratorState(self::STATE_SELF);
  167. return self::NEXT_REPEAT;
  168. }
  169. } else if ($this->mode == self::LEAVES_ONLY) {
  170. // We're already at the recursion limit, and the current node isn't
  171. // a leaf
  172. $this->setInnerIteratorState(self::STATE_NEXT);
  173. return self::NEXT_REPEAT;
  174. }
  175. }
  176. $this->nextElement();
  177. $this->setInnerIteratorState(self::STATE_NEXT);
  178. return self::NEXT_COMPLETE;
  179. case self::STATE_SELF:
  180. $this->nextElement();
  181. if ($this->mode == self::SELF_FIRST) {
  182. $this->setInnerIteratorState(self::STATE_CHILD);
  183. } else {
  184. $this->setInnerIteratorState(self::STATE_NEXT);
  185. }
  186. return self::NEXT_COMPLETE;
  187. case self::STATE_CHILD:
  188. $children = $this->callGetChildren();
  189. if (!$children instanceof RecursiveIterator) {
  190. throw new UnexpectedValueException(
  191. 'Objects returned by RecursiveIterator::getChildren() must '.
  192. 'implement RecursiveIterator'
  193. );
  194. }
  195. if ($this->mode == self::CHILD_FIRST) {
  196. $this->setInnerIteratorState(self::STATE_SELF);
  197. } else {
  198. $this->setInnerIteratorState(self::STATE_NEXT);
  199. }
  200. $children->rewind();
  201. $this->iterators[] = array($children, self::STATE_START);
  202. $this->beginChildren();
  203. return self::NEXT_REPEAT;
  204. }
  205. throw new Exception("unreachable");
  206. }
  207. // This doc comment block generated by idl/sysdoc.php
  208. /**
  209. * ( excerpt from
  210. * http://php.net/manual/en/recursiveiteratoriterator.rewind.php )
  211. *
  212. *
  213. * @return mixed No value is returned.
  214. */
  215. public function rewind() {
  216. while ($this->iterators) {
  217. array_pop($this->iterators);
  218. }
  219. $it = $this->originalIterator;
  220. $this->iterators = array(array($it, self::STATE_START));
  221. $it->rewind();
  222. if (!$this->inIteration) {
  223. $this->beginIteration();
  224. }
  225. $this->nextImpl();
  226. $this->inIteration = true;
  227. }
  228. // This doc comment block generated by idl/sysdoc.php
  229. /**
  230. * ( excerpt from
  231. * http://php.net/manual/en/recursiveiteratoriterator.valid.php )
  232. *
  233. *
  234. * @return mixed TRUE if the current position is valid, otherwise
  235. * FALSE
  236. */
  237. public function valid() {
  238. $depth = $this->getDepth();
  239. while ($depth >= 0) {
  240. if ($this->getSubIterator($depth)->valid()) {
  241. return true;
  242. }
  243. $depth--;
  244. }
  245. if ($this->inIteration) {
  246. $this->endIteration();
  247. }
  248. $this->inIteration = false;
  249. return false;
  250. }
  251. /**
  252. * Called after calling getChildren(), and its associated rewind().
  253. */
  254. public function beginChildren()
  255. {
  256. }
  257. /**
  258. * Called when iteration begins (after the first rewind() call).
  259. */
  260. public function beginIteration()
  261. {
  262. }
  263. /**
  264. * Get children of the current element.
  265. *
  266. * @return RecursiveIterator
  267. */
  268. public function callGetChildren()
  269. {
  270. return $this->getInnerIterator()->getChildren();
  271. }
  272. /**
  273. * Called for each element to test whether it has children.
  274. *
  275. * @return bool
  276. */
  277. public function callHasChildren()
  278. {
  279. return $this->getInnerIterator()->hasChildren();
  280. }
  281. /**
  282. * Called when end recursing one level.
  283. */
  284. public function endChildren()
  285. {
  286. }
  287. /**
  288. * Called when the iteration ends (when valid() first returns FALSE).
  289. */
  290. public function endIteration()
  291. {
  292. }
  293. /**
  294. * Get the current depth of the recursive iteration.
  295. *
  296. * @return int The current depth of the recursive iteration.
  297. */
  298. public function getDepth()
  299. {
  300. return count($this->iterators)-1;
  301. }
  302. /**
  303. * Gets the maximum allowable depth.
  304. *
  305. * @return int The maximum accepted depth, or FALSE if any depth is
  306. * allowed.
  307. */
  308. public function getMaxDepth()
  309. {
  310. return ($this->maxDepth == -1) ? false : $this->maxDepth;
  311. }
  312. /**
  313. * Gets the current active sub iterator.
  314. *
  315. * @param int $level
  316. *
  317. * @return RecursiveIterator The current active sub iterator.
  318. */
  319. public function getSubIterator($level = null)
  320. {
  321. $currentLevel = count($this->iterators)-1;
  322. if (is_null($level)) {
  323. $level = $currentLevel;
  324. }
  325. if ($level < 0 || $level > $currentLevel) {
  326. return null;
  327. }
  328. return $this->iterators[$level][0];
  329. }
  330. /**
  331. * Called when the next element is available.
  332. */
  333. public function nextElement()
  334. {
  335. }
  336. /**
  337. * Set the maximum allowed depth.
  338. *
  339. * @param int $max_depth The maximum allowed depth. -1 is used for
  340. * any depth.
  341. *
  342. * @throws Exception Emits an Exception if max_depth is less
  343. * than -1.
  344. */
  345. public function setMaxDepth($max_depth = -1)
  346. {
  347. if ($max_depth < -1) {
  348. throw new OutOfRangeException("Parameter max_depth must be >= -1");
  349. }
  350. $this->maxDepth = $max_depth;
  351. }
  352. private function isEmpty() {
  353. return count($this->iterators) == 0;
  354. }
  355. private function getInnerIteratorState() {
  356. return $this->iterators[count($this->iterators)-1][1];
  357. }
  358. private function setInnerIteratorState($state) {
  359. $this->iterators[count($this->iterators)-1][1] = $state;
  360. }
  361. /**
  362. * Undocumented behavior but Zend does it and frameworks rely on it, so..
  363. */
  364. public function __call($func, $params) {
  365. return call_user_func_array(
  366. array($this->getInnerIterator(), $func),
  367. $params
  368. );
  369. }
  370. }