1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Reflection;
4:
5: use PhpParser\Node\Stmt\ClassMethod;
6: use PhpParser\Node\Stmt\Function_;
7: use PHPStan\Analyser\Scope;
8: use PHPStan\BetterReflection\Reflection\Adapter\ReflectionFunction;
9: use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter;
10: use PHPStan\BetterReflection\Reflection\ReflectionConstant;
11: use PHPStan\ShouldNotHappenException;
12: use function array_slice;
13: use function count;
14: use function explode;
15: use function implode;
16: use function sprintf;
17:
18: /** @api */
19: class InitializerExprContext implements NamespaceAnswerer
20: {
21:
22: /**
23: * @param non-empty-string|null $namespace
24: */
25: private function __construct(
26: private ?string $file,
27: private ?string $namespace,
28: private ?string $className,
29: private ?string $traitName,
30: private ?string $function,
31: private ?string $method,
32: )
33: {
34: }
35:
36: public static function fromScope(Scope $scope): self
37: {
38: return new self(
39: $scope->getFile(),
40: $scope->getNamespace(),
41: $scope->isInClass() ? $scope->getClassReflection()->getName() : null,
42: $scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
43: $scope->isInAnonymousFunction() ? '{closure}' : ($scope->getFunction() !== null ? $scope->getFunction()->getName() : null),
44: $scope->isInAnonymousFunction() ? '{closure}' : ($scope->getFunction() instanceof MethodReflection
45: ? sprintf('%s::%s', $scope->getFunction()->getDeclaringClass()->getName(), $scope->getFunction()->getName())
46: : ($scope->getFunction() instanceof FunctionReflection ? $scope->getFunction()->getName() : null)),
47: );
48: }
49:
50: /**
51: * @return non-empty-string|null
52: */
53: private static function parseNamespace(string $name): ?string
54: {
55: $parts = explode('\\', $name);
56: if (count($parts) > 1) {
57: $ns = implode('\\', array_slice($parts, 0, -1));
58: if ($ns === '') {
59: throw new ShouldNotHappenException('Namespace cannot be empty.');
60: }
61: return $ns;
62: }
63:
64: return null;
65: }
66:
67: public static function fromClassReflection(ClassReflection $classReflection): self
68: {
69: return self::fromClass($classReflection->getName(), $classReflection->getFileName());
70: }
71:
72: public static function fromClass(string $className, ?string $fileName): self
73: {
74: return new self(
75: $fileName,
76: self::parseNamespace($className),
77: $className,
78: null,
79: null,
80: null,
81: );
82: }
83:
84: public static function fromReflectionParameter(ReflectionParameter $parameter): self
85: {
86: $declaringFunction = $parameter->getDeclaringFunction();
87: if ($declaringFunction instanceof ReflectionFunction) {
88: $file = $declaringFunction->getFileName();
89: return new self(
90: $file === false ? null : $file,
91: self::parseNamespace($declaringFunction->getName()),
92: null,
93: null,
94: $declaringFunction->getName(),
95: $declaringFunction->getName(),
96: );
97: }
98:
99: $file = $declaringFunction->getFileName();
100:
101: $betterReflection = $declaringFunction->getBetterReflection();
102:
103: return new self(
104: $file === false ? null : $file,
105: self::parseNamespace($betterReflection->getDeclaringClass()->getName()),
106: $declaringFunction->getDeclaringClass()->getName(),
107: $betterReflection->getDeclaringClass()->isTrait() ? $betterReflection->getDeclaringClass()->getName() : null,
108: $declaringFunction->getName(),
109: sprintf('%s::%s', $declaringFunction->getDeclaringClass()->getName(), $declaringFunction->getName()),
110: );
111: }
112:
113: public static function fromStubParameter(
114: ?string $className,
115: string $stubFile,
116: ClassMethod|Function_ $function,
117: ): self
118: {
119: $namespace = null;
120: if ($className !== null) {
121: $namespace = self::parseNamespace($className);
122: } else {
123: if ($function instanceof Function_ && $function->namespacedName !== null) {
124: $namespace = self::parseNamespace($function->namespacedName->toString());
125: }
126: }
127: return new self(
128: $stubFile,
129: $namespace,
130: $className,
131: null,
132: $function instanceof Function_ && $function->namespacedName !== null ? $function->namespacedName->toString() : ($function instanceof ClassMethod ? $function->name->toString() : null),
133: $function instanceof ClassMethod && $className !== null
134: ? sprintf('%s::%s', $className, $function->name->toString())
135: : ($function instanceof Function_ && $function->namespacedName !== null ? $function->namespacedName->toString() : null),
136: );
137: }
138:
139: public static function fromGlobalConstant(ReflectionConstant $constant): self
140: {
141: return new self(
142: $constant->getFileName(),
143: $constant->getNamespaceName(),
144: null,
145: null,
146: null,
147: null,
148: );
149: }
150:
151: public static function createEmpty(): self
152: {
153: return new self(null, null, null, null, null, null);
154: }
155:
156: public function getFile(): ?string
157: {
158: return $this->file;
159: }
160:
161: public function getClassName(): ?string
162: {
163: return $this->className;
164: }
165:
166: public function getNamespace(): ?string
167: {
168: return $this->namespace;
169: }
170:
171: public function getTraitName(): ?string
172: {
173: return $this->traitName;
174: }
175:
176: public function getFunction(): ?string
177: {
178: return $this->function;
179: }
180:
181: public function getMethod(): ?string
182: {
183: return $this->method;
184: }
185:
186: }
187: