1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Analyser;
4:
5: use PhpParser\Node;
6: use PhpParser\Node\Expr;
7: use PhpParser\Node\Name;
8: use PhpParser\Node\Param;
9: use PHPStan\Php\PhpVersions;
10: use PHPStan\Reflection\ClassConstantReflection;
11: use PHPStan\Reflection\ClassMemberAccessAnswerer;
12: use PHPStan\Reflection\ClassReflection;
13: use PHPStan\Reflection\ExtendedMethodReflection;
14: use PHPStan\Reflection\ExtendedPropertyReflection;
15: use PHPStan\Reflection\FunctionReflection;
16: use PHPStan\Reflection\MethodReflection;
17: use PHPStan\Reflection\NamespaceAnswerer;
18: use PHPStan\Reflection\ParameterReflection;
19: use PHPStan\Reflection\Php\PhpFunctionFromParserNodeReflection;
20: use PHPStan\TrinaryLogic;
21: use PHPStan\Turbo\ReferencedByTurboExtension;
22: use PHPStan\Type\ClosureType;
23: use PHPStan\Type\Type;
24: use PHPStan\Type\TypeWithClassName;
25:
26: /**
27: * Represents the state of the analyser at a specific position in the AST.
28: *
29: * The Scope tracks everything PHPStan knows at a given point in code: variable types,
30: * the current class/function/method context, whether strict_types is enabled, and more.
31: * It is the primary interface through which rules and extensions query information
32: * about the analysed code.
33: *
34: * The Scope is passed as a parameter to:
35: * - Custom rules (2nd parameter of processNode())
36: * - Dynamic return type extensions (last parameter of getTypeFrom*Call())
37: * - Dynamic throw type extensions
38: * - Type-specifying extensions (3rd parameter of specifyTypes())
39: *
40: * The Scope is immutable from the extension's perspective. Each AST node gets
41: * its own Scope reflecting the analysis state at that point. For example, after
42: * an `if ($x instanceof Foo)` check, the Scope inside the if-branch knows that
43: * $x is of type Foo.
44: *
45: * @api
46: * @api-do-not-implement
47: */
48: #[ReferencedByTurboExtension(key: 'scope')]
49: interface Scope extends ClassMemberAccessAnswerer, NamespaceAnswerer
50: {
51:
52: public const SUPERGLOBAL_VARIABLES = [
53: 'GLOBALS',
54: '_SERVER',
55: '_GET',
56: '_POST',
57: '_FILES',
58: '_COOKIE',
59: '_SESSION',
60: '_REQUEST',
61: '_ENV',
62: ];
63:
64: /**
65: * When analysing a trait, returns the file where the trait is used,
66: * not the trait file itself. Use getFileDescription() for the trait file path.
67: */
68: public function getFile(): string;
69:
70: /**
71: * For traits, returns the trait file path with the using class context,
72: * e.g. "TraitFile.php (in context of class MyClass)".
73: */
74: public function getFileDescription(): string;
75:
76: public function isDeclareStrictTypes(): bool;
77:
78: /**
79: * @phpstan-assert-if-true !null $this->getTraitReflection()
80: */
81: public function isInTrait(): bool;
82:
83: /**
84: * Returns the trait itself, not the class using the trait.
85: * Use getClassReflection() for the using class.
86: */
87: public function getTraitReflection(): ?ClassReflection;
88:
89: public function getFunction(): ?PhpFunctionFromParserNodeReflection;
90:
91: public function getFunctionName(): ?string;
92:
93: public function getParentScope(): ?self;
94:
95: public function hasVariableType(string $variableName): TrinaryLogic;
96:
97: public function getVariableType(string $variableName): Type;
98:
99: /**
100: * True at the top level of a file or after extract() — contexts where
101: * arbitrary variables may exist.
102: */
103: public function canAnyVariableExist(): bool;
104:
105: /** @return array<int, string> */
106: public function getDefinedVariables(): array;
107:
108: /**
109: * Variables with TrinaryLogic::Maybe certainty — defined in some code paths but not others.
110: *
111: * @return array<int, string>
112: */
113: public function getMaybeDefinedVariables(): array;
114:
115: public function hasConstant(Name $name): bool;
116:
117: /**
118: * @deprecated Use getInstancePropertyReflection or getStaticPropertyReflection instead
119: */
120: public function getPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection;
121:
122: public function getInstancePropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection;
123:
124: public function getStaticPropertyReflection(Type $typeWithProperty, string $propertyName): ?ExtendedPropertyReflection;
125:
126: public function getMethodReflection(Type $typeWithMethod, string $methodName): ?ExtendedMethodReflection;
127:
128: public function getConstantReflection(Type $typeWithConstant, string $constantName): ?ClassConstantReflection;
129:
130: public function getConstantExplicitTypeFromConfig(string $constantName, Type $constantType): Type;
131:
132: public function getIterableKeyType(Type $iteratee): Type;
133:
134: public function getIterableValueType(Type $iteratee): Type;
135:
136: /**
137: * @phpstan-assert-if-true !null $this->getAnonymousFunctionReflection()
138: * @phpstan-assert-if-true !null $this->getAnonymousFunctionReturnType()
139: */
140: public function isInAnonymousFunction(): bool;
141:
142: public function getAnonymousFunctionReflection(): ?ClosureType;
143:
144: public function getAnonymousFunctionReturnType(): ?Type;
145:
146: /**
147: * Returns the PHPDoc-enhanced type. Use getNativeType() for native types only.
148: */
149: public function getType(Expr $node): Type;
150:
151: /**
152: * Returns only what PHP's native type system knows, ignoring PHPDoc.
153: */
154: public function getNativeType(Expr $expr): Type;
155:
156: /**
157: * Like getType(), but preserves void for function/method calls
158: * (normally getType() replaces void with null).
159: */
160: public function getKeepVoidType(Expr $node): Type;
161:
162: /**
163: * Unlike getType() which may defer evaluation, this uses the scope's
164: * current state immediately.
165: */
166: public function getScopeType(Expr $expr): Type;
167:
168: public function getScopeNativeType(Expr $expr): Type;
169:
170: /**
171: * Resolves a Name AST node to a fully qualified class name string.
172: *
173: * Handles special names: `self` and `static` resolve to the current class,
174: * `parent` resolves to the parent class. Other names are returned as-is
175: * (they should already be fully qualified by the PHP parser's name resolver).
176: *
177: * Inside a Closure::bind() context, `self`/`static` resolve to the bound class.
178: *
179: * @return non-empty-string
180: */
181: public function resolveName(Name $name): string;
182:
183: /**
184: * Resolves a Name AST node to a TypeWithClassName.
185: *
186: * Unlike resolveName() which returns a plain string, this returns a proper
187: * Type object that preserves late-static-binding information:
188: * - `static` returns a StaticType (preserves LSB in subclasses)
189: * - `self` returns a ThisType when inside the same class hierarchy
190: * - Other names return an ObjectType
191: */
192: public function resolveTypeByName(Name $name): TypeWithClassName;
193:
194: /**
195: * Returns the PHPStan Type representing a given PHP value.
196: *
197: * Converts runtime PHP values to their corresponding constant types:
198: * integers become ConstantIntegerType, strings become ConstantStringType,
199: * arrays become ConstantArrayType (if small enough), etc.
200: *
201: * @param mixed $value
202: */
203: public function getTypeFromValue($value): Type;
204:
205: /**
206: * Returns whether an expression has a tracked type in this scope.
207: *
208: * Returns TrinaryLogic::Yes if the expression's type is definitely known,
209: * TrinaryLogic::Maybe if it might be known, and TrinaryLogic::No if there
210: * is no type information for it.
211: *
212: * This checks the scope's expression type map without computing the type
213: * (unlike getType() which always computes a type).
214: */
215: public function hasExpressionType(Expr $node): TrinaryLogic;
216:
217: /**
218: * Returns whether the given class name is being checked inside a
219: * class_exists(), interface_exists(), or trait_exists() call.
220: *
221: * When true, rules should suppress "class not found" errors because
222: * the code is explicitly checking for the class's existence.
223: */
224: public function isInClassExists(string $className): bool;
225:
226: /**
227: * Returns whether the given function name is being checked inside a
228: * function_exists() call.
229: *
230: * When true, rules should suppress "function not found" errors because
231: * the code is explicitly checking for the function's existence.
232: */
233: public function isInFunctionExists(string $functionName): bool;
234:
235: /**
236: * Returns whether the current analysis context is inside a Closure::bind()
237: * or Closure::bindTo() call.
238: *
239: * When true, the closure's $this and self/static may refer to a different
240: * class than the one where the closure was defined.
241: */
242: public function isInClosureBind(): bool;
243:
244: /**
245: * Returns the stack of function/method calls that are currently being analysed.
246: *
247: * When analysing arguments of a function call, this returns the chain of
248: * enclosing calls. Used by extensions that need to know the calling context,
249: * such as type-specifying extensions for functions like class_exists().
250: *
251: * @return list<FunctionReflection|MethodReflection>
252: */
253: public function getFunctionCallStack(): array;
254:
255: /**
256: * Like getFunctionCallStack(), but also includes the parameter being passed to.
257: *
258: * Each entry is a tuple of the function/method reflection and the parameter
259: * reflection for the argument position being analysed (or null if unknown).
260: *
261: * @return list<array{FunctionReflection|MethodReflection, ParameterReflection|null}>
262: */
263: public function getFunctionCallStackWithParameters(): array;
264:
265: /**
266: * Returns whether a function parameter has a default value of null.
267: *
268: * Checks the parameter's default value AST node to determine if
269: * `= null` was specified. Used by function definition checks.
270: */
271: public function isParameterValueNullable(Param $parameter): bool;
272:
273: /**
274: * Resolves a type AST node (from a parameter/return type declaration) to a Type.
275: *
276: * Handles named types, identifier types (int, string, etc.), union types,
277: * intersection types, and nullable types. The $isNullable flag adds null
278: * to the type, and $isVariadic wraps the type in an array.
279: *
280: * @param Node\Name|Node\Identifier|Node\ComplexType|null $type
281: */
282: public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type;
283:
284: /**
285: * Returns whether the given expression is currently being assigned to.
286: *
287: * Returns true during the analysis of the right-hand side of an assignment
288: * to this expression. For example, when analysing `$a = expr`, this returns
289: * true for the $a variable during the analysis of `expr`.
290: *
291: * Used to prevent infinite recursion when resolving types during assignment.
292: */
293: public function isInExpressionAssign(Expr $expr): bool;
294:
295: /**
296: * Returns whether accessing the given expression in an undefined state is allowed.
297: *
298: * Returns true when the expression is on the left-hand side of an assignment
299: * or in similar contexts where it's valid for the expression to be undefined
300: * (e.g. `$a['key'] = value` where $a['key'] doesn't need to exist yet).
301: */
302: public function isUndefinedExpressionAllowed(Expr $expr): bool;
303:
304: /**
305: * Returns a new Scope with types narrowed by assuming the expression is truthy.
306: *
307: * Given an expression like `$x instanceof Foo`, returns a scope where
308: * $x is known to be of type Foo. This is the scope used inside the
309: * if-branch of `if ($x instanceof Foo)`.
310: *
311: * Uses the TypeSpecifier internally to determine type narrowing.
312: *
313: * @return static
314: */
315: public function filterByTruthyValue(Expr $expr): self;
316:
317: /**
318: * Returns a new Scope with types narrowed by assuming the expression is falsy.
319: *
320: * The opposite of filterByTruthyValue(). Given `$x instanceof Foo`, returns
321: * a scope where $x is known NOT to be of type Foo. This is the scope used
322: * in the else-branch of `if ($x instanceof Foo)`.
323: *
324: * @return static
325: */
326: public function filterByFalseyValue(Expr $expr): self;
327:
328: /**
329: * Returns whether the current statement is a "first-level" statement.
330: *
331: * A first-level statement is one that is directly inside a function/method
332: * body, not nested inside control structures like if/else, loops, or
333: * try/catch. Used to determine whether certain checks should be more
334: * or less strict.
335: */
336: public function isInFirstLevelStatement(): bool;
337:
338: /**
339: * Returns the PHP version(s) being analysed against.
340: *
341: * Returns a PhpVersions object that can represent a range of PHP versions
342: * (when the exact version is not known). Use its methods like
343: * supportsEnums(), supportsReadonlyProperties(), etc. to check for
344: * version-specific features.
345: */
346: public function getPhpVersion(): PhpVersions;
347:
348: /** @internal */
349: public function toWalkScope(): MutatingScope;
350:
351: /** @deprecated The scope answers every ask directly - call the methods on the scope itself, or toWalkScope() for the engine-facing walk scope. */
352: public function toMutatingScope(): MutatingScope;
353:
354: }
355: