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