TreeHelper.php 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Console\Helper;
  11. use Symfony\Component\Console\Output\OutputInterface;
  12. /**
  13. * The TreeHelper class provides methods to display tree-like structures.
  14. *
  15. * @author Simon André <smn.andre@gmail.com>
  16. *
  17. * @implements \RecursiveIterator<int, TreeNode>
  18. */
  19. final class TreeHelper implements \RecursiveIterator
  20. {
  21. /**
  22. * @var \Iterator<int, TreeNode>
  23. */
  24. private \Iterator $children;
  25. private function __construct(
  26. private readonly OutputInterface $output,
  27. private readonly TreeNode $node,
  28. private readonly TreeStyle $style,
  29. ) {
  30. $this->children = new \IteratorIterator($this->node->getChildren());
  31. $this->children->rewind();
  32. }
  33. public static function createTree(OutputInterface $output, string|TreeNode|null $root = null, iterable $values = [], ?TreeStyle $style = null): self
  34. {
  35. $node = $root instanceof TreeNode ? $root : new TreeNode($root ?? '');
  36. return new self($output, TreeNode::fromValues($values, $node), $style ?? TreeStyle::default());
  37. }
  38. public function current(): TreeNode
  39. {
  40. return $this->children->current();
  41. }
  42. public function key(): int
  43. {
  44. return $this->children->key();
  45. }
  46. public function next(): void
  47. {
  48. $this->children->next();
  49. }
  50. public function rewind(): void
  51. {
  52. $this->children->rewind();
  53. }
  54. public function valid(): bool
  55. {
  56. return $this->children->valid();
  57. }
  58. public function hasChildren(): bool
  59. {
  60. if (null === $current = $this->current()) {
  61. return false;
  62. }
  63. foreach ($current->getChildren() as $child) {
  64. return true;
  65. }
  66. return false;
  67. }
  68. public function getChildren(): \RecursiveIterator
  69. {
  70. return new self($this->output, $this->current(), $this->style);
  71. }
  72. /**
  73. * Recursively renders the tree to the output, applying the tree style.
  74. */
  75. public function render(): void
  76. {
  77. $treeIterator = new \RecursiveTreeIterator($this);
  78. $this->style->applyPrefixes($treeIterator);
  79. $this->output->writeln($this->node->getValue());
  80. $visited = new \SplObjectStorage();
  81. foreach ($treeIterator as $node) {
  82. $currentNode = $node instanceof TreeNode ? $node : $treeIterator->getInnerIterator()->current();
  83. if (isset($visited[$currentNode])) {
  84. throw new \LogicException(\sprintf('Cycle detected at node: "%s".', $currentNode->getValue()));
  85. }
  86. $visited[$currentNode] = true;
  87. $this->output->writeln($node);
  88. }
  89. }
  90. }