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