1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* This file is part of the humbug/php-scoper package. |
7
|
|
|
* |
8
|
|
|
* Copyright (c) 2017 Théo FIDRY <[email protected]>, |
9
|
|
|
* Pádraic Brady <[email protected]> |
10
|
|
|
* |
11
|
|
|
* For the full copyright and license information, please view the LICENSE |
12
|
|
|
* file that was distributed with this source code. |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace Humbug\PhpScoper\PhpParser\NodeVisitor\Resolver; |
16
|
|
|
|
17
|
|
|
use Humbug\PhpScoper\PhpParser\Node\NamedIdentifier; |
18
|
|
|
use Humbug\PhpScoper\PhpParser\NodeVisitor\Collection\NamespaceStmtCollection; |
19
|
|
|
use Humbug\PhpScoper\PhpParser\NodeVisitor\Collection\UseStmtCollection; |
20
|
|
|
use Humbug\PhpScoper\PhpParser\NodeVisitor\NameStmtPrefixer; |
21
|
|
|
use Humbug\PhpScoper\PhpParser\NodeVisitor\ParentNodeAppender; |
22
|
|
|
use PhpParser\Node; |
23
|
|
|
use PhpParser\Node\Expr\ConstFetch; |
24
|
|
|
use PhpParser\Node\Expr\FuncCall; |
25
|
|
|
use PhpParser\Node\Identifier; |
26
|
|
|
use PhpParser\Node\Name; |
27
|
|
|
use PhpParser\Node\Name\FullyQualified; |
28
|
|
|
use PhpParser\Node\Scalar\String_; |
29
|
|
|
use function in_array; |
30
|
|
|
use function ltrim; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Attempts to resolve the node name into a fully qualified node. Returns a valid (non fully-qualified) name node on |
34
|
|
|
* failure. |
35
|
|
|
* |
36
|
|
|
* @private |
37
|
|
|
*/ |
38
|
|
|
final class FullyQualifiedNameResolver |
39
|
|
|
{ |
40
|
|
|
private $namespaceStatements; |
41
|
|
|
private $useStatements; |
42
|
|
|
|
43
|
10 |
|
public function __construct(NamespaceStmtCollection $namespaceStatements, UseStmtCollection $useStatements) |
44
|
|
|
{ |
45
|
10 |
|
$this->namespaceStatements = $namespaceStatements; |
46
|
10 |
|
$this->useStatements = $useStatements; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @param Name|String_|Identifier $node |
51
|
|
|
*/ |
52
|
7 |
|
public function resolveName(Node $node): ResolvedValue |
53
|
|
|
{ |
54
|
7 |
|
if ($node instanceof FullyQualified) { |
55
|
6 |
|
return new ResolvedValue($node, null, null); |
56
|
|
|
} |
57
|
|
|
|
58
|
7 |
|
if ($node instanceof String_) { |
59
|
|
|
return $this->resolveStringName($node); |
60
|
|
|
} |
61
|
|
|
|
62
|
7 |
|
if ($node instanceof Identifier) { |
63
|
|
|
$node = NamedIdentifier::create($node); |
64
|
|
|
} |
65
|
|
|
|
66
|
7 |
|
$namespaceName = $this->namespaceStatements->findNamespaceForNode($node); |
67
|
|
|
|
68
|
7 |
|
$useName = $this->useStatements->findStatementForNode($namespaceName, $node); |
|
|
|
|
69
|
|
|
|
70
|
7 |
|
return new ResolvedValue( |
71
|
7 |
|
$this->resolveNodeName($node, $namespaceName, $useName), |
|
|
|
|
72
|
7 |
|
$namespaceName, |
73
|
7 |
|
$useName |
74
|
|
|
); |
75
|
|
|
} |
76
|
|
|
|
77
|
7 |
|
private function resolveNodeName(Name $name, ?Name $namespace, ?Name $use): Name |
78
|
|
|
{ |
79
|
7 |
|
if (null !== $use) { |
80
|
|
|
return FullyQualified::concat($use, $name->slice(1), $name->getAttributes()); |
81
|
|
|
} |
82
|
|
|
|
83
|
7 |
|
if (null === $namespace) { |
84
|
4 |
|
return new FullyQualified($name, $name->getAttributes()); |
85
|
|
|
} |
86
|
|
|
|
87
|
3 |
|
if (in_array((string) $name, NameStmtPrefixer::PHP_FUNCTION_KEYWORDS, true)) { |
88
|
|
|
return $name; |
89
|
|
|
} |
90
|
|
|
|
91
|
3 |
|
$parentNode = ParentNodeAppender::getParent($name); |
92
|
|
|
|
93
|
|
|
if ( |
94
|
3 |
|
($parentNode instanceof ConstFetch || $parentNode instanceof FuncCall) |
95
|
3 |
|
&& 1 === count($name->parts) |
96
|
|
|
) { |
97
|
|
|
// Ambiguous name, cannot determine the FQ name |
98
|
3 |
|
return $name; |
99
|
|
|
} |
100
|
|
|
|
101
|
3 |
|
return FullyQualified::concat($namespace, $name, $name->getAttributes()); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
private function resolveStringName(String_ $node): ResolvedValue |
105
|
|
|
{ |
106
|
|
|
$name = new FullyQualified(ltrim($node->value, '\\')); |
107
|
|
|
|
108
|
|
|
$deducedNamespaceName = $name->slice(0, -1); |
109
|
|
|
$namespaceName = null; |
110
|
|
|
|
111
|
|
|
if (null !== $deducedNamespaceName) { |
112
|
|
|
$namespaceName = $this->namespaceStatements->findNamespaceByName($deducedNamespaceName->toString()); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
return new ResolvedValue( |
116
|
|
|
$name, |
117
|
|
|
$namespaceName, |
118
|
|
|
null |
119
|
|
|
); |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.