Yaffs site version 1.1
[yaffs-website] / vendor / psy / psysh / src / Psy / CodeCleaner / UseStatementPass.php
1 <?php
2
3 /*
4  * This file is part of Psy Shell.
5  *
6  * (c) 2012-2017 Justin Hileman
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Psy\CodeCleaner;
13
14 use PhpParser\Node;
15 use PhpParser\Node\Name;
16 use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
17 use PhpParser\Node\Stmt\GroupUse;
18 use PhpParser\Node\Stmt\Namespace_;
19 use PhpParser\Node\Stmt\Use_;
20
21 /**
22  * Provide implicit use statements for subsequent execution.
23  *
24  * The use statement pass remembers the last use statement line encountered:
25  *
26  *     use Foo\Bar as Baz;
27  *
28  * ... which it then applies implicitly to all future evaluated code, until the
29  * current namespace is replaced by another namespace.
30  */
31 class UseStatementPass extends CodeCleanerPass
32 {
33     private $aliases       = array();
34     private $lastAliases   = array();
35     private $lastNamespace = null;
36
37     /**
38      * Re-load the last set of use statements on re-entering a namespace.
39      *
40      * This isn't how namespaces normally work, but because PsySH has to spin
41      * up a new namespace for every line of code, we do this to make things
42      * work like you'd expect.
43      *
44      * @param Node $node
45      */
46     public function enterNode(Node $node)
47     {
48         if ($node instanceof Namespace_) {
49             // If this is the same namespace as last namespace, let's do ourselves
50             // a favor and reload all the aliases...
51             if (strtolower($node->name) === strtolower($this->lastNamespace)) {
52                 $this->aliases = $this->lastAliases;
53             }
54         }
55     }
56
57     /**
58      * If this statement is a namespace, forget all the aliases we had.
59      *
60      * If it's a use statement, remember the alias for later. Otherwise, apply
61      * remembered aliases to the code.
62      *
63      * @param Node $node
64      */
65     public function leaveNode(Node $node)
66     {
67         if ($node instanceof Use_) {
68             // Store a reference to every "use" statement, because we'll need
69             // them in a bit.
70             foreach ($node->uses as $use) {
71                 $this->aliases[strtolower($use->alias)] = $use->name;
72             }
73
74             return false;
75         } elseif ($node instanceof GroupUse) {
76             // Expand every "use" statement in the group into a full, standalone
77             // "use" and store 'em with the others.
78             foreach ($node->uses as $use) {
79                 $this->aliases[strtolower($use->alias)] = Name::concat($node->prefix, $use->name, array(
80                     'startLine' => $node->prefix->getAttribute('startLine'),
81                     'endLine'   => $use->name->getAttribute('endLine'),
82                 ));
83             }
84
85             return false;
86         } elseif ($node instanceof Namespace_) {
87             // Start fresh, since we're done with this namespace.
88             $this->lastNamespace = $node->name;
89             $this->lastAliases   = $this->aliases;
90             $this->aliases       = array();
91         } else {
92             foreach ($node as $name => $subNode) {
93                 if ($subNode instanceof Name) {
94                     // Implicitly thunk all aliases.
95                     if ($replacement = $this->findAlias($subNode)) {
96                         $node->$name = $replacement;
97                     }
98                 }
99             }
100
101             return $node;
102         }
103     }
104
105     /**
106      * Find class/namespace aliases.
107      *
108      * @param Name $name
109      *
110      * @return FullyQualifiedName|null
111      */
112     private function findAlias(Name $name)
113     {
114         $that = strtolower($name);
115         foreach ($this->aliases as $alias => $prefix) {
116             if ($that === $alias) {
117                 return new FullyQualifiedName($prefix->toString());
118             } elseif (substr($that, 0, strlen($alias) + 1) === $alias . '\\') {
119                 return new FullyQualifiedName($prefix->toString() . substr($name, strlen($alias)));
120             }
121         }
122     }
123 }