1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Twig;
13
14use Twig\Node\Node;
15use Twig\NodeVisitor\NodeVisitorInterface;
16
17/**
18 * A node traverser.
19 *
20 * It visits all nodes and their children and calls the given visitor for each.
21 *
22 * @author Fabien Potencier <fabien@symfony.com>
23 */
24final class NodeTraverser
25{
26    private $env;
27    private $visitors = [];
28
29    /**
30     * @param NodeVisitorInterface[] $visitors
31     */
32    public function __construct(Environment $env, array $visitors = [])
33    {
34        $this->env = $env;
35        foreach ($visitors as $visitor) {
36            $this->addVisitor($visitor);
37        }
38    }
39
40    public function addVisitor(NodeVisitorInterface $visitor): void
41    {
42        $this->visitors[$visitor->getPriority()][] = $visitor;
43    }
44
45    /**
46     * Traverses a node and calls the registered visitors.
47     */
48    public function traverse(Node $node): Node
49    {
50        ksort($this->visitors);
51        foreach ($this->visitors as $visitors) {
52            foreach ($visitors as $visitor) {
53                $node = $this->traverseForVisitor($visitor, $node);
54            }
55        }
56
57        return $node;
58    }
59
60    private function traverseForVisitor(NodeVisitorInterface $visitor, Node $node): ?Node
61    {
62        $node = $visitor->enterNode($node, $this->env);
63
64        foreach ($node as $k => $n) {
65            if (null !== $m = $this->traverseForVisitor($visitor, $n)) {
66                if ($m !== $n) {
67                    $node->setNode($k, $m);
68                }
69            } else {
70                $node->removeNode($k);
71            }
72        }
73
74        return $visitor->leaveNode($node, $this->env);
75    }
76}
77