The docs are receiving too little love...
4.0 KiB
Name resolution
Since the introduction of namespaces in PHP 5.3, literal names in PHP code are subject to a relatively complex name resolution process, which is based on the current namespace, the current import table state, as well the type of the referenced symbol. PHP-Parser implements name resolution and related functionality, both as reusable logic (NameContext), as well as a node visitor (NameResolver) based on it.
The NameResolver visitor
The NameResolver
visitor can (and for nearly all uses of the AST, is) be applied to resolve names
to their fully-qualified form, to the degree that this is possible.
$nameResolver = new PhpParser\NodeVisitor\NameResolver;
$nodeTraverser = new PhpParser\NodeTraverser;
$nodeTraverser->addVisitor($nameResolver);
// Resolve names
$stmts = $nodeTraverser->traverse($stmts);
In the default configuration, the name resolver will perform three actions:
- Declarations of functions, classes, interfaces, traits and global constants will have a
namespacedName
property added, which contains the function/class/etc name including the namespace prefix. For historic reasons this is a property rather than an attribute. - Names will be replaced by fully qualified resolved names, which are instances of
Node\Name\FullyQualified
. - Unqualified function and constant names inside a namespace cannot be statically resolved. Inside
a namespace
Foo
, a call tostrlen()
may either refer to the namespaced\Foo\strlen()
, or the global\strlen()
. Because PHP-Parser does not have the necessary context to decide this, such names are left unresolved. Additionally anamespacedName
attribute is added to the name node.
The name resolver accepts an option array as the second argument, with the following default values:
$nameResolver = new PhpParser\NodeVisitor\NameResolver(null, [
'preserveOriginalNames' => false,
'replaceNodes' => true,
]);
If the preserveOriginalNames
option is enabled, then the resolved (fully qualified) name will have
an originalName
attribute, which contains the unresolved name.
If the replaceNodes
option is disabled, then names will no longer be resolved in-place. Instead a
resolvedName
attribute will be added to each name, which contains the resolved (fully qualified)
name. Once again, if an unqualified function or constant name cannot be resolved, then the
resolvedName
attribute will not be present, and instead a namespacedName
attribute is added.
The replaceNodes
attribute is useful if you wish to perform modifications on the AST, as you
probably do not wish the resoluting code to have fully resolved names as a side-effect.
The NameContext
The actual name resolution logic is implemented in the NameContext
class, which has the following
public API:
class NameContext {
public function __construct(ErrorHandler $errorHandler);
public function startNamespace(Name $namespace = null);
public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []);
public function getNamespace();
public function getResolvedName(Name $name, int $type);
public function getResolvedClassName(Name $name) : Name;
public function getPossibleNames(string $name, int $type) : array;
public function getShortName(string $name, int $type) : Name;
}
The $type
parameters accept on of the Stmt\Use_::TYPE_*
constants, which represent the three
basic symbol types in PHP (functions, constants and everything else).
Next to name resolution, the NameContext
also supports the reverse operation of finding a short
representation of a name given the current name resolution environment.
The name context is intended to be used for name resolution operations outside the AST itself, such
as class names inside doc comments. A visitor running in parallel with the name resolver can access
the name context using $nameResolver->getNameContext()
. Alternatively a visitor can use an
independent context and explicitly feed Namespace
and Use
nodes to it.