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