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 declare(strict_types=1);
2
 
3
namespace PhpParser\NodeVisitor;
4
 
5
use PhpParser\ErrorHandler;
6
use PhpParser\NameContext;
7
use PhpParser\Node;
8
use PhpParser\Node\Expr;
9
use PhpParser\Node\Name;
10
use PhpParser\Node\Name\FullyQualified;
11
use PhpParser\Node\Stmt;
12
use PhpParser\NodeVisitorAbstract;
13
 
14
class NameResolver extends NodeVisitorAbstract
15
{
16
    /** @var NameContext Naming context */
17
    protected $nameContext;
18
 
19
    /** @var bool Whether to preserve original names */
20
    protected $preserveOriginalNames;
21
 
22
    /** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */
23
    protected $replaceNodes;
24
 
25
    /**
26
     * Constructs a name resolution visitor.
27
     *
28
     * Options:
29
     *  * preserveOriginalNames (default false): An "originalName" attribute will be added to
30
     *    all name nodes that underwent resolution.
31
     *  * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a
32
     *    resolvedName attribute is added. (Names that cannot be statically resolved receive a
33
     *    namespacedName attribute, as usual.)
34
     *
35
     * @param ErrorHandler|null $errorHandler Error handler
36
     * @param array $options Options
37
     */
38
    public function __construct(ErrorHandler $errorHandler = null, array $options = []) {
39
        $this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing);
40
        $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false;
41
        $this->replaceNodes = $options['replaceNodes'] ?? true;
42
    }
43
 
44
    /**
45
     * Get name resolution context.
46
     *
47
     * @return NameContext
48
     */
49
    public function getNameContext() : NameContext {
50
        return $this->nameContext;
51
    }
52
 
53
    public function beforeTraverse(array $nodes) {
54
        $this->nameContext->startNamespace();
55
        return null;
56
    }
57
 
58
    public function enterNode(Node $node) {
59
        if ($node instanceof Stmt\Namespace_) {
60
            $this->nameContext->startNamespace($node->name);
61
        } elseif ($node instanceof Stmt\Use_) {
62
            foreach ($node->uses as $use) {
63
                $this->addAlias($use, $node->type, null);
64
            }
65
        } elseif ($node instanceof Stmt\GroupUse) {
66
            foreach ($node->uses as $use) {
67
                $this->addAlias($use, $node->type, $node->prefix);
68
            }
69
        } elseif ($node instanceof Stmt\Class_) {
70
            if (null !== $node->extends) {
71
                $node->extends = $this->resolveClassName($node->extends);
72
            }
73
 
74
            foreach ($node->implements as &$interface) {
75
                $interface = $this->resolveClassName($interface);
76
            }
77
 
78
            $this->resolveAttrGroups($node);
79
            if (null !== $node->name) {
80
                $this->addNamespacedName($node);
81
            }
82
        } elseif ($node instanceof Stmt\Interface_) {
83
            foreach ($node->extends as &$interface) {
84
                $interface = $this->resolveClassName($interface);
85
            }
86
 
87
            $this->resolveAttrGroups($node);
88
            $this->addNamespacedName($node);
89
         } elseif ($node instanceof Stmt\Enum_) {
90
            foreach ($node->implements as &$interface) {
91
                $interface = $this->resolveClassName($interface);
92
            }
93
 
94
            $this->resolveAttrGroups($node);
95
            if (null !== $node->name) {
96
                $this->addNamespacedName($node);
97
            }
98
        } elseif ($node instanceof Stmt\Trait_) {
99
            $this->resolveAttrGroups($node);
100
            $this->addNamespacedName($node);
101
        } elseif ($node instanceof Stmt\Function_) {
102
            $this->resolveSignature($node);
103
            $this->resolveAttrGroups($node);
104
            $this->addNamespacedName($node);
105
        } elseif ($node instanceof Stmt\ClassMethod
106
                  || $node instanceof Expr\Closure
107
                  || $node instanceof Expr\ArrowFunction
108
        ) {
109
            $this->resolveSignature($node);
110
            $this->resolveAttrGroups($node);
111
        } elseif ($node instanceof Stmt\Property) {
112
            if (null !== $node->type) {
113
                $node->type = $this->resolveType($node->type);
114
            }
115
            $this->resolveAttrGroups($node);
116
        } elseif ($node instanceof Stmt\Const_) {
117
            foreach ($node->consts as $const) {
118
                $this->addNamespacedName($const);
119
            }
120
        } else if ($node instanceof Stmt\ClassConst) {
121
            $this->resolveAttrGroups($node);
122
        } else if ($node instanceof Stmt\EnumCase) {
123
            $this->resolveAttrGroups($node);
124
        } elseif ($node instanceof Expr\StaticCall
125
                  || $node instanceof Expr\StaticPropertyFetch
126
                  || $node instanceof Expr\ClassConstFetch
127
                  || $node instanceof Expr\New_
128
                  || $node instanceof Expr\Instanceof_
129
        ) {
130
            if ($node->class instanceof Name) {
131
                $node->class = $this->resolveClassName($node->class);
132
            }
133
        } elseif ($node instanceof Stmt\Catch_) {
134
            foreach ($node->types as &$type) {
135
                $type = $this->resolveClassName($type);
136
            }
137
        } elseif ($node instanceof Expr\FuncCall) {
138
            if ($node->name instanceof Name) {
139
                $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION);
140
            }
141
        } elseif ($node instanceof Expr\ConstFetch) {
142
            $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT);
143
        } elseif ($node instanceof Stmt\TraitUse) {
144
            foreach ($node->traits as &$trait) {
145
                $trait = $this->resolveClassName($trait);
146
            }
147
 
148
            foreach ($node->adaptations as $adaptation) {
149
                if (null !== $adaptation->trait) {
150
                    $adaptation->trait = $this->resolveClassName($adaptation->trait);
151
                }
152
 
153
                if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
154
                    foreach ($adaptation->insteadof as &$insteadof) {
155
                        $insteadof = $this->resolveClassName($insteadof);
156
                    }
157
                }
158
            }
159
        }
160
 
161
        return null;
162
    }
163
 
150 lars 164
    private function addAlias(Stmt\UseUse $use, int $type, Name $prefix = null) {
148 lars 165
        // Add prefix for group uses
166
        $name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
167
        // Type is determined either by individual element or whole use declaration
168
        $type |= $use->type;
169
 
170
        $this->nameContext->addAlias(
171
            $name, (string) $use->getAlias(), $type, $use->getAttributes()
172
        );
173
    }
174
 
175
    /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */
176
    private function resolveSignature($node) {
177
        foreach ($node->params as $param) {
178
            $param->type = $this->resolveType($param->type);
179
            $this->resolveAttrGroups($param);
180
        }
181
        $node->returnType = $this->resolveType($node->returnType);
182
    }
183
 
184
    private function resolveType($node) {
185
        if ($node instanceof Name) {
186
            return $this->resolveClassName($node);
187
        }
188
        if ($node instanceof Node\NullableType) {
189
            $node->type = $this->resolveType($node->type);
190
            return $node;
191
        }
192
        if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) {
193
            foreach ($node->types as &$type) {
194
                $type = $this->resolveType($type);
195
            }
196
            return $node;
197
        }
198
        return $node;
199
    }
200
 
201
    /**
202
     * Resolve name, according to name resolver options.
203
     *
204
     * @param Name $name Function or constant name to resolve
205
     * @param int  $type One of Stmt\Use_::TYPE_*
206
     *
207
     * @return Name Resolved name, or original name with attribute
208
     */
209
    protected function resolveName(Name $name, int $type) : Name {
210
        if (!$this->replaceNodes) {
211
            $resolvedName = $this->nameContext->getResolvedName($name, $type);
212
            if (null !== $resolvedName) {
213
                $name->setAttribute('resolvedName', $resolvedName);
214
            } else {
215
                $name->setAttribute('namespacedName', FullyQualified::concat(
216
                    $this->nameContext->getNamespace(), $name, $name->getAttributes()));
217
            }
218
            return $name;
219
        }
220
 
221
        if ($this->preserveOriginalNames) {
222
            // Save the original name
223
            $originalName = $name;
224
            $name = clone $originalName;
225
            $name->setAttribute('originalName', $originalName);
226
        }
227
 
228
        $resolvedName = $this->nameContext->getResolvedName($name, $type);
229
        if (null !== $resolvedName) {
230
            return $resolvedName;
231
        }
232
 
233
        // unqualified names inside a namespace cannot be resolved at compile-time
234
        // add the namespaced version of the name as an attribute
235
        $name->setAttribute('namespacedName', FullyQualified::concat(
236
            $this->nameContext->getNamespace(), $name, $name->getAttributes()));
237
        return $name;
238
    }
239
 
240
    protected function resolveClassName(Name $name) {
241
        return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL);
242
    }
243
 
244
    protected function addNamespacedName(Node $node) {
245
        $node->namespacedName = Name::concat(
246
            $this->nameContext->getNamespace(), (string) $node->name);
247
    }
248
 
249
    protected function resolveAttrGroups(Node $node)
250
    {
251
        foreach ($node->attrGroups as $attrGroup) {
252
            foreach ($attrGroup->attrs as $attr) {
253
                $attr->name = $this->resolveClassName($attr->name);
254
            }
255
        }
256
    }
257
}