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