1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\Php\PhpVersion;
6: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
7: use PHPStan\Reflection\Callables\CallableParametersAcceptor;
8: use PHPStan\Reflection\ClassConstantReflection;
9: use PHPStan\Reflection\ClassMemberAccessAnswerer;
10: use PHPStan\Reflection\ClassReflection;
11: use PHPStan\Reflection\ExtendedMethodReflection;
12: use PHPStan\Reflection\ExtendedPropertyReflection;
13: use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
14: use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
15: use PHPStan\TrinaryLogic;
16: use PHPStan\Type\Constant\ConstantArrayType;
17: use PHPStan\Type\Constant\ConstantStringType;
18: use PHPStan\Type\Enum\EnumCaseObjectType;
19: use PHPStan\Type\Generic\TemplateTypeMap;
20: use PHPStan\Type\Generic\TemplateTypeReference;
21: use PHPStan\Type\Generic\TemplateTypeVariance;
22:
23: /**
24: * Represents a PHPStan type in the type system.
25: *
26: * This is the central interface of PHPStan's type system. Every type that PHPStan
27: * can reason about implements this interface — from simple scalars like StringType
28: * to complex generics like GenericObjectType.
29: *
30: * Each Type knows what it accepts, what is a supertype of it, what properties/methods/constants
31: * it has, what operations it supports, and how to describe itself for error messages.
32: *
33: * Important: Never use `instanceof` to check types. For example, `$type instanceof StringType`
34: * will miss union types, intersection types with accessory types, and other composite forms.
35: * Always use the `is*()` methods or `isSuperTypeOf()` instead:
36: *
37: * // Wrong:
38: * if ($type instanceof StringType) { ... }
39: *
40: * // Correct:
41: * if ($type->isString()->yes()) { ... }
42: *
43: * @api
44: * @api-do-not-implement
45: * @see https://phpstan.org/developing-extensions/type-system
46: */
47: interface Type
48: {
49:
50: /**
51: * Returns all class names referenced anywhere in this type, recursively
52: * (including generic arguments, callable signatures, etc.).
53: *
54: * @see Type::getObjectClassNames() for only direct object type class names
55: *
56: * @return list<non-empty-string>
57: */
58: public function getReferencedClasses(): array;
59:
60: /**
61: * Returns class names of the object types this type directly represents.
62: * Unlike getReferencedClasses(), excludes classes in generic arguments, etc.
63: *
64: * @return list<non-empty-string>
65: */
66: public function getObjectClassNames(): array;
67:
68: /** @return list<ClassReflection> */
69: public function getObjectClassReflections(): array;
70:
71: /**
72: * Return class-string<Foo> for object type Foo.
73: */
74: public function getClassStringType(): Type;
75:
76: /**
77: * Returns the object type for a class-string or literal class name string.
78: * For non-class-string types, returns ErrorType.
79: */
80: public function getClassStringObjectType(): Type;
81:
82: /**
83: * Like getClassStringObjectType(), but also returns object types as-is.
84: * Used for `$classOrObject::method()` where the left side can be either.
85: */
86: public function getObjectTypeOrClassStringObjectType(): Type;
87:
88: public function isObject(): TrinaryLogic;
89:
90: public function isEnum(): TrinaryLogic;
91:
92: /** @return list<ArrayType|ConstantArrayType> */
93: public function getArrays(): array;
94:
95: /**
96: * Only ConstantArrayType instances (array shapes with known keys).
97: *
98: * @return list<ConstantArrayType>
99: */
100: public function getConstantArrays(): array;
101:
102: /** @return list<ConstantStringType> */
103: public function getConstantStrings(): array;
104:
105: /**
106: * Unlike isSuperTypeOf(), accepts() takes into account PHP's implicit type coercion.
107: * With $strictTypes = false, int is accepted by float, and Stringable objects are
108: * accepted by string.
109: */
110: public function accepts(Type $type, bool $strictTypes): AcceptsResult;
111:
112: /**
113: * "Does every value of $type belong to $this type?"
114: *
115: * Preferable to instanceof checks because it correctly handles
116: * union types, intersection types, and all other composite types.
117: */
118: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult;
119:
120: public function equals(Type $type): bool;
121:
122: public function describe(VerbosityLevel $level): string;
123:
124: public function canAccessProperties(): TrinaryLogic;
125:
126: /** @deprecated Use hasInstanceProperty or hasStaticProperty instead */
127: public function hasProperty(string $propertyName): TrinaryLogic;
128:
129: /** @deprecated Use getInstanceProperty or getStaticProperty instead */
130: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection;
131:
132: /** @deprecated Use getUnresolvedInstancePropertyPrototype or getUnresolvedStaticPropertyPrototype instead */
133: public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection;
134:
135: public function hasInstanceProperty(string $propertyName): TrinaryLogic;
136:
137: public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection;
138:
139: /**
140: * Unlike getInstanceProperty(), this defers template type resolution.
141: * Use getInstanceProperty() in most rule implementations.
142: */
143: public function getUnresolvedInstancePropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection;
144:
145: public function hasStaticProperty(string $propertyName): TrinaryLogic;
146:
147: public function getStaticProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection;
148:
149: public function getUnresolvedStaticPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection;
150:
151: public function canCallMethods(): TrinaryLogic;
152:
153: public function hasMethod(string $methodName): TrinaryLogic;
154:
155: public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection;
156:
157: /**
158: * Unlike getMethod(), this defers template type and static type resolution.
159: * Use getMethod() in most rule implementations.
160: */
161: public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection;
162:
163: public function canAccessConstants(): TrinaryLogic;
164:
165: public function hasConstant(string $constantName): TrinaryLogic;
166:
167: public function getConstant(string $constantName): ClassConstantReflection;
168:
169: public function isIterable(): TrinaryLogic;
170:
171: public function isIterableAtLeastOnce(): TrinaryLogic;
172:
173: /**
174: * Returns the count of elements as a Type (typically IntegerRangeType).
175: */
176: public function getArraySize(): Type;
177:
178: /**
179: * Works for both arrays and Traversable objects.
180: */
181: public function getIterableKeyType(): Type;
182:
183: /** @deprecated use getIterableKeyType */
184: public function getFirstIterableKeyType(): Type;
185:
186: /** @deprecated use getIterableKeyType */
187: public function getLastIterableKeyType(): Type;
188:
189: public function getIterableValueType(): Type;
190:
191: /** @deprecated use getIterableValueType */
192: public function getFirstIterableValueType(): Type;
193:
194: /** @deprecated use getIterableValueType */
195: public function getLastIterableValueType(): Type;
196:
197: public function isArray(): TrinaryLogic;
198:
199: public function isConstantArray(): TrinaryLogic;
200:
201: /**
202: * An oversized array is a constant array shape that grew too large to track
203: * precisely and was degraded to a generic array type.
204: */
205: public function isOversizedArray(): TrinaryLogic;
206:
207: /**
208: * A list is an array with sequential integer keys starting from 0 with no gaps.
209: */
210: public function isList(): TrinaryLogic;
211:
212: public function isOffsetAccessible(): TrinaryLogic;
213:
214: /**
215: * Whether accessing a non-existent offset is safe (won't cause errors).
216: * Unlike isOffsetAccessible() which checks if offset access is supported at all.
217: */
218: public function isOffsetAccessLegal(): TrinaryLogic;
219:
220: public function hasOffsetValueType(Type $offsetType): TrinaryLogic;
221:
222: public function getOffsetValueType(Type $offsetType): Type;
223:
224: /**
225: * May add a new key. When $offsetType is null, appends (like $a[] = $value).
226: *
227: * @see Type::setExistingOffsetValueType() for modifying an existing key without widening
228: */
229: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type;
230:
231: /**
232: * Unlike setOffsetValueType(), assumes the key already exists.
233: * Preserves the array shape and list type.
234: */
235: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type;
236:
237: public function unsetOffset(Type $offsetType): Type;
238:
239: /** Models array_keys($array, $searchValue, $strict). */
240: public function getKeysArrayFiltered(Type $filterValueType, TrinaryLogic $strict): Type;
241:
242: /** Models array_keys($array). */
243: public function getKeysArray(): Type;
244:
245: /** Models array_values($array). */
246: public function getValuesArray(): Type;
247:
248: /** Models array_chunk($array, $length, $preserveKeys). */
249: public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type;
250:
251: /** Models array_fill_keys($keys, $value). */
252: public function fillKeysArray(Type $valueType): Type;
253:
254: /** Models array_flip($array). */
255: public function flipArray(): Type;
256:
257: /** Models array_intersect_key($array, ...$otherArrays). */
258: public function intersectKeyArray(Type $otherArraysType): Type;
259:
260: /** Models array_pop() effect on the array. */
261: public function popArray(): Type;
262:
263: /** Models array_reverse($array, $preserveKeys). */
264: public function reverseArray(TrinaryLogic $preserveKeys): Type;
265:
266: /** Models array_search($needle, $array, $strict). */
267: public function searchArray(Type $needleType, ?TrinaryLogic $strict = null): Type;
268:
269: /** Models array_shift() effect on the array. */
270: public function shiftArray(): Type;
271:
272: /** Models shuffle() effect on the array. Result is always a list. */
273: public function shuffleArray(): Type;
274:
275: /** Models array_slice($array, $offset, $length, $preserveKeys). */
276: public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type;
277:
278: /** Models array_splice() effect on the array (the modified array, not the removed portion). */
279: public function spliceArray(Type $offsetType, Type $lengthType, Type $replacementType): Type;
280:
281: /** @return list<EnumCaseObjectType> */
282: public function getEnumCases(): array;
283:
284: /**
285: * Returns the single enum case this type represents, or null if not exactly one case.
286: */
287: public function getEnumCaseObject(): ?EnumCaseObjectType;
288:
289: /**
290: * Returns a list of finite values this type can take.
291: *
292: * Examples:
293: *
294: * - for bool: [true, false]
295: * - for int<0, 3>: [0, 1, 2, 3]
296: * - for enums: list of enum cases
297: * - for scalars: the scalar itself
298: *
299: * For infinite types it returns an empty array.
300: *
301: * @return list<Type>
302: */
303: public function getFiniteTypes(): array;
304:
305: /** Models the ** operator. */
306: public function exponentiate(Type $exponent): Type;
307:
308: public function isCallable(): TrinaryLogic;
309:
310: /** @return list<CallableParametersAcceptor> */
311: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array;
312:
313: public function isCloneable(): TrinaryLogic;
314:
315: /** Models the (bool) cast. */
316: public function toBoolean(): BooleanType;
317:
318: /** Models numeric coercion for arithmetic operators. */
319: public function toNumber(): Type;
320:
321: /** Models the (int) cast. */
322: public function toInteger(): Type;
323:
324: /** Models the (float) cast. */
325: public function toFloat(): Type;
326:
327: /** Models the (string) cast. */
328: public function toString(): Type;
329:
330: /** Models the (array) cast. */
331: public function toArray(): Type;
332:
333: /**
334: * Models PHP's implicit array key coercion: floats truncated to int,
335: * booleans become 0/1, null becomes '', numeric strings become int.
336: */
337: public function toArrayKey(): Type;
338:
339: /**
340: * Returns how this type might change when passed to a typed parameter
341: * or assigned to a typed property.
342: *
343: * With $strictTypes = true: int widens to int|float (since int is accepted
344: * by float parameters in strict mode).
345: * With $strictTypes = false: additional coercions apply, e.g. Stringable
346: * objects are accepted by string parameters.
347: *
348: * Used internally to determine what types a value might be coerced to
349: * when checking parameter acceptance.
350: */
351: public function toCoercedArgumentType(bool $strictTypes): self;
352:
353: public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryLogic;
354:
355: public function isSmallerThanOrEqual(Type $otherType, PhpVersion $phpVersion): TrinaryLogic;
356:
357: /**
358: * Is Type of a known constant value? Includes literal strings, integers, floats, true, false, null, and array shapes.
359: *
360: * Unlike isConstantScalarValue(), this also returns yes for constant array types (array shapes
361: * with known keys and values). Use this when you need to detect any constant value including arrays.
362: */
363: public function isConstantValue(): TrinaryLogic;
364:
365: /**
366: * Is Type of a known constant scalar value? Includes literal strings, integers, floats, true, false, and null.
367: *
368: * Unlike isConstantValue(), this does NOT return yes for array shapes.
369: * Use this when you specifically need scalar constants only.
370: */
371: public function isConstantScalarValue(): TrinaryLogic;
372:
373: /** @return list<ConstantScalarType> */
374: public function getConstantScalarTypes(): array;
375:
376: /** @return list<int|float|string|bool|null> */
377: public function getConstantScalarValues(): array;
378:
379: public function isNull(): TrinaryLogic;
380:
381: public function isTrue(): TrinaryLogic;
382:
383: public function isFalse(): TrinaryLogic;
384:
385: public function isBoolean(): TrinaryLogic;
386:
387: public function isFloat(): TrinaryLogic;
388:
389: public function isInteger(): TrinaryLogic;
390:
391: public function isString(): TrinaryLogic;
392:
393: public function isNumericString(): TrinaryLogic;
394:
395: public function isNonEmptyString(): TrinaryLogic;
396:
397: /**
398: * Non-falsy string is a non-empty string that is also not '0'.
399: * Stricter subset of non-empty-string.
400: */
401: public function isNonFalsyString(): TrinaryLogic;
402:
403: /**
404: * A literal-string is a string composed entirely from string literals
405: * in the source code (not from user input). Used for SQL injection prevention.
406: */
407: public function isLiteralString(): TrinaryLogic;
408:
409: public function isLowercaseString(): TrinaryLogic;
410:
411: public function isUppercaseString(): TrinaryLogic;
412:
413: public function isClassString(): TrinaryLogic;
414:
415: public function isVoid(): TrinaryLogic;
416:
417: public function isScalar(): TrinaryLogic;
418:
419: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType;
420:
421: /**
422: * Type narrowing methods for comparison operators.
423: * For example, for ConstantIntegerType(5), getSmallerType() returns int<min, 4>.
424: */
425: public function getSmallerType(PhpVersion $phpVersion): Type;
426:
427: public function getSmallerOrEqualType(PhpVersion $phpVersion): Type;
428:
429: public function getGreaterType(PhpVersion $phpVersion): Type;
430:
431: public function getGreaterOrEqualType(PhpVersion $phpVersion): Type;
432:
433: /**
434: * Returns actual template type for a given object.
435: *
436: * Example:
437: *
438: * @-template T
439: * class Foo {}
440: *
441: * // $fooType is Foo<int>
442: * $t = $fooType->getTemplateType(Foo::class, 'T');
443: * $t->isInteger(); // yes
444: *
445: * Returns ErrorType in case of a missing type.
446: *
447: * @param class-string $ancestorClassName
448: */
449: public function getTemplateType(string $ancestorClassName, string $templateTypeName): Type;
450:
451: /**
452: * Infers the real types of TemplateTypes found in $this, based on
453: * the received Type. E.g. if $this is array<T> and $receivedType
454: * is array<int>, infers T = int.
455: */
456: public function inferTemplateTypes(Type $receivedType): TemplateTypeMap;
457:
458: /**
459: * Returns the template types referenced by this Type, recursively.
460: *
461: * The return value is a list of TemplateTypeReferences, who contain the
462: * referenced template type as well as the variance position in which it was
463: * found.
464: *
465: * For example, calling this on array<Foo<T>,Bar> (with T a template type)
466: * will return one TemplateTypeReference for the type T.
467: *
468: * @param TemplateTypeVariance $positionVariance The variance position in
469: * which the receiver type was
470: * found.
471: *
472: * @return list<TemplateTypeReference>
473: */
474: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array;
475:
476: /** Models abs(). */
477: public function toAbsoluteNumber(): Type;
478:
479: /**
480: * Returns a new instance with all inner types mapped through $cb.
481: * Returns the same instance if inner types did not change.
482: *
483: * Not used directly — use TypeTraverser::map() instead.
484: *
485: * @param callable(Type):Type $cb
486: */
487: public function traverse(callable $cb): Type;
488:
489: /**
490: * Like traverse(), but walks two types simultaneously.
491: *
492: * Not used directly — use SimultaneousTypeTraverser::map() instead.
493: *
494: * @param callable(Type $left, Type $right): Type $cb
495: */
496: public function traverseSimultaneously(Type $right, callable $cb): Type;
497:
498: public function toPhpDocNode(): TypeNode;
499:
500: /** @see TypeCombinator::remove() */
501: public function tryRemove(Type $typeToRemove): ?Type;
502:
503: /**
504: * Removes constant value information. E.g. 'foo' -> string, 1 -> int.
505: * Used when types become too complex to track precisely (e.g. loop iterations).
506: */
507: public function generalize(GeneralizePrecision $precision): Type;
508:
509: /**
510: * Performance optimization to skip template resolution when no templates are present.
511: */
512: public function hasTemplateOrLateResolvableType(): bool;
513:
514: }
515: