Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of Psy Shell.
5
 *
200 lars 6
 * (c) 2012-2023 Justin Hileman
148 lars 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
use PhpParser\Node\Stmt\UseUse;
21
use PhpParser\NodeTraverser;
22
 
23
/**
24
 * Provide implicit use statements for subsequent execution.
25
 *
26
 * The use statement pass remembers the last use statement line encountered:
27
 *
28
 *     use Foo\Bar as Baz;
29
 *
30
 * ... which it then applies implicitly to all future evaluated code, until the
31
 * current namespace is replaced by another namespace.
32
 */
33
class UseStatementPass extends CodeCleanerPass
34
{
35
    private $aliases = [];
36
    private $lastAliases = [];
37
    private $lastNamespace = null;
38
 
39
    /**
40
     * Re-load the last set of use statements on re-entering a namespace.
41
     *
42
     * This isn't how namespaces normally work, but because PsySH has to spin
43
     * up a new namespace for every line of code, we do this to make things
44
     * work like you'd expect.
45
     *
46
     * @param Node $node
47
     *
48
     * @return int|Node|null Replacement node (or special return value)
49
     */
50
    public function enterNode(Node $node)
51
    {
52
        if ($node instanceof Namespace_) {
53
            // If this is the same namespace as last namespace, let's do ourselves
54
            // a favor and reload all the aliases...
55
            if (\strtolower($node->name ?: '') === \strtolower($this->lastNamespace ?: '')) {
56
                $this->aliases = $this->lastAliases;
57
            }
58
        }
59
    }
60
 
61
    /**
62
     * If this statement is a namespace, forget all the aliases we had.
63
     *
64
     * If it's a use statement, remember the alias for later. Otherwise, apply
65
     * remembered aliases to the code.
66
     *
67
     * @param Node $node
68
     *
69
     * @return int|Node|Node[]|null Replacement node (or special return value)
70
     */
71
    public function leaveNode(Node $node)
72
    {
73
        // Store a reference to every "use" statement, because we'll need them in a bit.
74
        if ($node instanceof Use_) {
75
            foreach ($node->uses as $use) {
76
                $alias = $use->alias ?: \end($use->name->parts);
77
                $this->aliases[\strtolower($alias)] = $use->name;
78
            }
79
 
80
            return NodeTraverser::REMOVE_NODE;
81
        }
82
 
83
        // Expand every "use" statement in the group into a full, standalone "use" and store 'em with the others.
84
        if ($node instanceof GroupUse) {
85
            foreach ($node->uses as $use) {
86
                $alias = $use->alias ?: \end($use->name->parts);
87
                $this->aliases[\strtolower($alias)] = Name::concat($node->prefix, $use->name, [
88
                    'startLine' => $node->prefix->getAttribute('startLine'),
89
                    'endLine'   => $use->name->getAttribute('endLine'),
90
                ]);
91
            }
92
 
93
            return NodeTraverser::REMOVE_NODE;
94
        }
95
 
96
        // Start fresh, since we're done with this namespace.
97
        if ($node instanceof Namespace_) {
98
            $this->lastNamespace = $node->name;
99
            $this->lastAliases = $this->aliases;
100
            $this->aliases = [];
101
 
102
            return;
103
        }
104
 
105
        // Do nothing with UseUse; this an entry in the list of uses in the use statement.
106
        if ($node instanceof UseUse) {
107
            return;
108
        }
109
 
110
        // For everything else, we'll implicitly thunk all aliases into fully-qualified names.
111
        foreach ($node as $name => $subNode) {
112
            if ($subNode instanceof Name) {
113
                if ($replacement = $this->findAlias($subNode)) {
114
                    $node->$name = $replacement;
115
                }
116
            }
117
        }
118
 
119
        return $node;
120
    }
121
 
122
    /**
123
     * Find class/namespace aliases.
124
     *
125
     * @param Name $name
126
     *
127
     * @return FullyQualifiedName|null
128
     */
129
    private function findAlias(Name $name)
130
    {
131
        $that = \strtolower($name);
132
        foreach ($this->aliases as $alias => $prefix) {
133
            if ($that === $alias) {
134
                return new FullyQualifiedName($prefix->toString());
135
            } elseif (\substr($that, 0, \strlen($alias) + 1) === $alias.'\\') {
136
                return new FullyQualifiedName($prefix->toString().\substr($name, \strlen($alias)));
137
            }
138
        }
139
    }
140
}