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\ReflectionProvider;
14: use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
15: use PHPStan\Reflection\Type\UnresolvedPropertyPrototypeReflection;
16: use PHPStan\TrinaryLogic;
17: use PHPStan\Turbo\ReferencedByTurboExtension;
18: use PHPStan\Type\Constant\ConstantArrayType;
19: use PHPStan\Type\Constant\ConstantStringType;
20: use PHPStan\Type\Enum\EnumCaseObjectType;
21: use PHPStan\Type\Generic\TemplateTypeMap;
22: use PHPStan\Type\Generic\TemplateTypeReference;
23: use PHPStan\Type\Generic\TemplateTypeVariance;
24:
25: /**
26: * Represents a PHPStan type in the type system.
27: *
28: * This is the central interface of PHPStan's type system. Every type that PHPStan
29: * can reason about implements this interface — from simple scalars like StringType
30: * to complex generics like GenericObjectType.
31: *
32: * Each Type knows what it accepts, what is a supertype of it, what properties/methods/constants
33: * it has, what operations it supports, and how to describe itself for error messages.
34: *
35: * Important: Never use `instanceof` to check types. For example, `$type instanceof StringType`
36: * will miss union types, intersection types with accessory types, and other composite forms.
37: * Always use the `is*()` methods or `isSuperTypeOf()` instead:
38: *
39: * // Wrong:
40: * if ($type instanceof StringType) { ... }
41: *
42: * // Correct:
43: * if ($type->isString()->yes()) { ... }
44: *
45: * @api
46: * @api-do-not-implement
47: * @see https://phpstan.org/developing-extensions/type-system
48: */
49: #[ReferencedByTurboExtension(key: 'type')]
50: interface Type
51: {
52:
53: /**
54: * Returns all class names referenced anywhere in this type, recursively
55: * (including generic arguments, callable signatures, etc.).
56: *
57: * @see Type::getObjectClassNames() for only direct object type class names
58: *
59: * @return list<non-empty-string>
60: */
61: public function getReferencedClasses(): array;
62:
63: /**
64: * Returns class names of the object types this type directly represents.
65: * Unlike getReferencedClasses(), excludes classes in generic arguments, etc.
66: *
67: * @return list<non-empty-string>
68: */
69: public function getObjectClassNames(): array;
70:
71: /** @return list<ClassReflection> */
72: public function getObjectClassReflections(): array;
73:
74: /**
75: * Return class-string<Foo> for object type Foo.
76: */
77: public function getClassStringType(): Type;
78:
79: /**
80: * Returns the object type for a class-string or literal class name string.
81: * For non-class-string types, returns ErrorType.
82: */
83: public function getClassStringObjectType(): Type;
84:
85: /**
86: * Like getClassStringObjectType(), but also returns object types as-is.
87: * Used for `$classOrObject::method()` where the left side can be either.
88: */
89: public function getObjectTypeOrClassStringObjectType(): Type;
90:
91: public function isObject(): TrinaryLogic;
92:
93: public function isEnum(): TrinaryLogic;
94:
95: /** @return list<ArrayType|ConstantArrayType> */
96: public function getArrays(): array;
97:
98: /**
99: * Only ConstantArrayType instances (array shapes with known keys).
100: *
101: * @return list<ConstantArrayType>
102: */
103: public function getConstantArrays(): array;
104:
105: /** @return list<ConstantStringType> */
106: public function getConstantStrings(): array;
107:
108: /**
109: * Unlike isSuperTypeOf(), accepts() takes into account PHP's implicit type coercion.
110: * With $strictTypes = false, int is accepted by float, and Stringable objects are
111: * accepted by string.
112: */
113: public function accepts(Type $type, bool $strictTypes): AcceptsResult;
114:
115: /**
116: * "Does every value of $type belong to $this type?"
117: *
118: * Preferable to instanceof checks because it correctly handles
119: * union types, intersection types, and all other composite types.
120: */
121: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult;
122:
123: public function equals(Type $type): bool;
124:
125: public function describe(VerbosityLevel $level): string;
126:
127: public function canAccessProperties(): TrinaryLogic;
128:
129: /** @deprecated Use hasInstanceProperty or hasStaticProperty instead */
130: public function hasProperty(string $propertyName): TrinaryLogic;
131:
132: /** @deprecated Use getInstanceProperty or getStaticProperty instead */
133: public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection;
134:
135: /** @deprecated Use getUnresolvedInstancePropertyPrototype or getUnresolvedStaticPropertyPrototype instead */
136: public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection;
137:
138: public function hasInstanceProperty(string $propertyName): TrinaryLogic;
139:
140: public function getInstanceProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection;
141:
142: /**
143: * Unlike getInstanceProperty(), this defers template type resolution.
144: * Use getInstanceProperty() in most rule implementations.
145: */
146: public function getUnresolvedInstancePropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection;
147:
148: public function hasStaticProperty(string $propertyName): TrinaryLogic;
149:
150: public function getStaticProperty(string $propertyName, ClassMemberAccessAnswerer $scope): ExtendedPropertyReflection;
151:
152: public function getUnresolvedStaticPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection;
153:
154: public function canCallMethods(): TrinaryLogic;
155:
156: public function hasMethod(string $methodName): TrinaryLogic;
157:
158: public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): ExtendedMethodReflection;
159:
160: /**
161: * Unlike getMethod(), this defers template type and static type resolution.
162: * Use getMethod() in most rule implementations.
163: */
164: public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection;
165:
166: public function canAccessConstants(): TrinaryLogic;
167:
168: public function hasConstant(string $constantName): TrinaryLogic;
169:
170: public function getConstant(string $constantName): ClassConstantReflection;
171:
172: public function isIterable(): TrinaryLogic;
173:
174: public function isIterableAtLeastOnce(): TrinaryLogic;
175:
176: /**
177: * Returns the count of elements as a Type (typically IntegerRangeType).
178: */
179: public function getArraySize(): Type;
180:
181: /**
182: * Works for both arrays and Traversable objects.
183: */
184: public function getIterableKeyType(): Type;
185:
186: /** @deprecated use getIterableKeyType */
187: public function getFirstIterableKeyType(): Type;
188:
189: /** @deprecated use getIterableKeyType */
190: public function getLastIterableKeyType(): Type;
191:
192: public function getIterableValueType(): Type;
193:
194: /** @deprecated use getIterableValueType */
195: public function getFirstIterableValueType(): Type;
196:
197: /** @deprecated use getIterableValueType */
198: public function getLastIterableValueType(): Type;
199:
200: public function isArray(): TrinaryLogic;
201:
202: public function isConstantArray(): TrinaryLogic;
203:
204: /**
205: * An oversized array is a constant array shape that grew too large to track
206: * precisely and was degraded to a generic array type.
207: */
208: public function isOversizedArray(): TrinaryLogic;
209:
210: /**
211: * A list is an array with sequential integer keys starting from 0 with no gaps.
212: */
213: public function isList(): TrinaryLogic;
214:
215: public function isOffsetAccessible(): TrinaryLogic;
216:
217: /**
218: * Whether accessing a non-existent offset is safe (won't cause errors).
219: * Unlike isOffsetAccessible() which checks if offset access is supported at all.
220: */
221: public function isOffsetAccessLegal(): TrinaryLogic;
222:
223: public function hasOffsetValueType(Type $offsetType): TrinaryLogic;
224:
225: public function getOffsetValueType(Type $offsetType): Type;
226:
227: /**
228: * May add a new key. When $offsetType is null, appends (like $a[] = $value).
229: *
230: * @see Type::setExistingOffsetValueType() for modifying an existing key without widening
231: */
232: public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type;
233:
234: /**
235: * Unlike setOffsetValueType(), assumes the key already exists.
236: * Preserves the array shape and list type.
237: */
238: public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type;
239:
240: public function unsetOffset(Type $offsetType): Type;
241:
242: /** Models array_keys($array, $searchValue, $strict). */
243: public function getKeysArrayFiltered(Type $filterValueType, TrinaryLogic $strict): Type;
244:
245: /** Models array_keys($array). */
246: public function getKeysArray(): Type;
247:
248: /** Models array_values($array). */
249: public function getValuesArray(): Type;
250:
251: /** Models array_chunk($array, $length, $preserveKeys). */
252: public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type;
253:
254: /** Models array_fill_keys($keys, $value). */
255: public function fillKeysArray(Type $valueType): Type;
256:
257: /** Models array_flip($array). */
258: public function flipArray(): Type;
259:
260: /** Models array_intersect_key($array, ...$otherArrays). */
261: public function intersectKeyArray(Type $otherArraysType): Type;
262:
263: /** Models array_pop() effect on the array. */
264: public function popArray(): Type;
265:
266: /** Models array_reverse($array, $preserveKeys). */
267: public function reverseArray(TrinaryLogic $preserveKeys): Type;
268:
269: /** Models array_search($needle, $array, $strict). */
270: public function searchArray(Type $needleType, ?TrinaryLogic $strict = null): Type;
271:
272: /** Models array_shift() effect on the array. */
273: public function shiftArray(): Type;
274:
275: /**
276: * Models shuffle() effect on the array. Result is always a list.
277: *
278: * It's also used to model array after `sort` / `rsort` / `usort` calls.
279: */
280: public function shuffleArray(): Type;
281:
282: /** Models array_slice($array, $offset, $length, $preserveKeys). */
283: public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type;
284:
285: /** Models array_splice() effect on the array (the modified array, not the removed portion). */
286: public function spliceArray(Type $offsetType, Type $lengthType, Type $replacementType): Type;
287:
288: /**
289: * Narrows a list-shaped array type to "size lies in $sizeType" — the
290: * type-system equivalent of `count($list) === N` / `count($list) >= N`
291: * / `count($list) in [N, M]`. Used by `TypeSpecifier` when specifying
292: * types for `count()` comparisons; the call site is responsible for
293: * gating on outer list-ness, so each implementation may assume it's
294: * narrowing a list shape.
295: *
296: * `$sizeType` is expected to be a `ConstantIntegerType` (exact size) or
297: * an `IntegerRangeType` (a min/max bound). Concrete implementations
298: * (`ConstantArrayType`, `ArrayType`) rebuild the array with a required
299: * prefix `[0, min)` and an optional middle `[min, max)` (when `max` is
300: * set), or extend with `HasOffsetValueType` accessories when the upper
301: * bound is unbounded. Non-array types return `ErrorType`.
302: */
303: public function truncateListToSize(Type $sizeType): Type;
304:
305: /**
306: * Downgrades the list-ness of the array from `Yes` to `Maybe` (e.g. for
307: * `asort`/`uksort`/etc. which preserve keys but break list ordering).
308: * Other shape information (keys, values, accessories like NonEmpty) is
309: * preserved.
310: */
311: public function makeListMaybe(): Type;
312:
313: /**
314: * Models "same keys, every value transformed" (e.g. `array_walk`,
315: * `array_map($cb, $a)`, `preg_replace*` over an array subject). Keys
316: * and accessories like list-ness / non-emptiness are preserved.
317: *
318: * @param callable(Type): Type $cb
319: */
320: public function mapValueType(callable $cb): Type;
321:
322: /**
323: * Replaces the iterable key type via `$cb($currentKeyType)`. For
324: * `ArrayType` rewrites the key type wholesale; for `ConstantArrayType`
325: * the explicit keys (which are already precise constants) are preserved
326: * — pass-through, matching the prior `TypeTraverser`-based callers.
327: * Used to widen / narrow the key type after a foreach narrowed `$key`
328: * via `is_int($key)` / `is_string($key)` checks.
329: *
330: * @param callable(Type): Type $cb
331: */
332: public function mapKeyType(callable $cb): Type;
333:
334: /**
335: * Marks every explicit key in a `ConstantArrayType` as optional (the
336: * shape can have any subset of the original keys). For non-`CAT` arrays
337: * this is a no-op — they already model arbitrary subsets. Used by
338: * `preg_replace*` over array subjects, where the callback can drop
339: * entries.
340: */
341: public function makeAllArrayKeysOptional(): Type;
342:
343: /**
344: * Models `array_change_key_case($a, $case)`. String keys are case-folded
345: * (constant ones to a specific value, general ones via accessories);
346: * non-string keys, values, accessories and list-ness are preserved.
347: * `$case` matches PHP's `CASE_LOWER` / `CASE_UPPER`; `null` means the
348: * case is non-constant and the result is the union of both folds.
349: */
350: public function changeKeyCaseArray(?int $case): Type;
351:
352: /**
353: * Models `array_filter($a)` (no callback): drops entries whose value is
354: * definitely falsey, marks possibly-falsey entries optional, keeps
355: * definitely-truthy entries unchanged. Keys are preserved; list-ness
356: * is downgraded since gaps may appear.
357: */
358: public function filterArrayRemovingFalsey(): Type;
359:
360: /** @return list<EnumCaseObjectType> */
361: public function getEnumCases(): array;
362:
363: /**
364: * Returns the single enum case this type represents, or null if not exactly one case.
365: */
366: public function getEnumCaseObject(): ?EnumCaseObjectType;
367:
368: /**
369: * Returns a list of finite values this type can take.
370: *
371: * Examples:
372: *
373: * - for bool: [true, false]
374: * - for int<0, 3>: [0, 1, 2, 3]
375: * - for enums: list of enum cases
376: * - for scalars: the scalar itself
377: *
378: * For infinite types it returns an empty array.
379: *
380: * @return list<Type>
381: */
382: public function getFiniteTypes(): array;
383:
384: /** Models the ** operator. */
385: public function exponentiate(Type $exponent): Type;
386:
387: public function isCallable(): TrinaryLogic;
388:
389: /** @return list<CallableParametersAcceptor> */
390: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array;
391:
392: public function isCloneable(): TrinaryLogic;
393:
394: /** Models the (bool) cast. */
395: public function toBoolean(): BooleanType;
396:
397: /** Models numeric coercion for arithmetic operators. */
398: public function toNumber(): Type;
399:
400: /** Models the bitwise-not (`~$x`) operator. Returns `ErrorType` for types where `~` is undefined. */
401: public function toBitwiseNotType(): Type;
402:
403: /**
404: * Models `get_class($x)`'s return type per leaf: definite objects yield
405: * their `class-string` projection, definite non-objects yield `false`,
406: * and possibly-objects yield the union of both.
407: */
408: public function toGetClassResultType(): Type;
409:
410: /**
411: * Models the type of `$x::class`. For known final classes the literal
412: * class name is returned; for everything else an
413: * `IntersectionType[ClassString<X>, AccessoryLiteralStringType]`.
414: * `NullType` passes through (mirrors PHP's nullsafe `::class` semantics).
415: * `ReflectionProvider` is needed for the final-class lookup.
416: */
417: public function toClassConstantType(ReflectionProvider $reflectionProvider): Type;
418:
419: /**
420: * Projects a class-name-or-object `Type` (the right-hand side of
421: * `$x instanceof <expr>`) to the `ObjectType` it should be compared
422: * against. Constant class strings collapse to their `ObjectType`
423: * exactly; everything kept symbolically (object class names,
424: * `class-string<X>`) carries an uncertainty flag so the caller can
425: * fall back to `BooleanType` instead of a definite yes/no.
426: */
427: public function toObjectTypeForInstanceofCheck(): ClassNameToObjectTypeResult;
428:
429: /**
430: * Projects a class-name-or-object `Type` (the second argument of
431: * `is_a($x, $class, $allow_string)`) to the `ObjectType` to narrow
432: * `$x` against. When `$allowString` is true, the `is_a()` result also
433: * keeps the original class-string accepted alongside the object.
434: * `$allowSameClass` controls whether matching the input's own class
435: * collapses to `NeverType` for final classes (the call site's
436: * "always-true" suppression).
437: */
438: public function toObjectTypeForIsACheck(Type $objectOrClassType, bool $allowString, bool $allowSameClass): ClassNameToObjectTypeResult;
439:
440: /** Models the (int) cast. */
441: public function toInteger(): Type;
442:
443: /** Models the (float) cast. */
444: public function toFloat(): Type;
445:
446: /** Models the (string) cast. */
447: public function toString(): Type;
448:
449: /** Models the (array) cast. */
450: public function toArray(): Type;
451:
452: /**
453: * Models PHP's implicit array key coercion: floats truncated to int,
454: * booleans become 0/1, null becomes '', numeric strings become int.
455: */
456: public function toArrayKey(): Type;
457:
458: /**
459: * Returns how this type might change when passed to a typed parameter
460: * or assigned to a typed property.
461: *
462: * With $strictTypes = true: int widens to int|float (since int is accepted
463: * by float parameters in strict mode).
464: * With $strictTypes = false: additional coercions apply, e.g. Stringable
465: * objects are accepted by string parameters.
466: *
467: * Used internally to determine what types a value might be coerced to
468: * when checking parameter acceptance.
469: */
470: public function toCoercedArgumentType(bool $strictTypes): self;
471:
472: public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryLogic;
473:
474: public function isSmallerThanOrEqual(Type $otherType, PhpVersion $phpVersion): TrinaryLogic;
475:
476: /**
477: * Is Type of a known constant value? Includes literal strings, integers, floats, true, false, null, and array shapes.
478: *
479: * Unlike isConstantScalarValue(), this also returns yes for constant array types (array shapes
480: * with known keys and values). Use this when you need to detect any constant value including arrays.
481: */
482: public function isConstantValue(): TrinaryLogic;
483:
484: /**
485: * Is Type of a known constant scalar value? Includes literal strings, integers, floats, true, false, and null.
486: *
487: * Unlike isConstantValue(), this does NOT return yes for array shapes.
488: * Use this when you specifically need scalar constants only.
489: */
490: public function isConstantScalarValue(): TrinaryLogic;
491:
492: /** @return list<ConstantScalarType> */
493: public function getConstantScalarTypes(): array;
494:
495: /** @return list<int|float|string|bool|null> */
496: public function getConstantScalarValues(): array;
497:
498: public function isNull(): TrinaryLogic;
499:
500: public function isTrue(): TrinaryLogic;
501:
502: public function isFalse(): TrinaryLogic;
503:
504: public function isBoolean(): TrinaryLogic;
505:
506: public function isFloat(): TrinaryLogic;
507:
508: public function isInteger(): TrinaryLogic;
509:
510: public function isString(): TrinaryLogic;
511:
512: public function isNumericString(): TrinaryLogic;
513:
514: /**
515: * When isDecimalIntegerString() returns yes(), the type
516: * is guaranteed to be cast to an integer in an array key.
517: * Examples of constant values covered by this type: "0", "1", "1234", "-1"
518: *
519: * When isDecimalIntegerString() returns no(), the type represents strings containing non-decimal integers and other text.
520: * These are guaranteed to stay as string in an array key.
521: * Examples of constant values covered by this type: "+1", "00", "18E+3", "1.2", "1,3", "foo"
522: */
523: public function isDecimalIntegerString(): TrinaryLogic;
524:
525: public function isNonEmptyString(): TrinaryLogic;
526:
527: /**
528: * Non-falsy string is a non-empty string that is also not '0'.
529: * Stricter subset of non-empty-string.
530: */
531: public function isNonFalsyString(): TrinaryLogic;
532:
533: /**
534: * A literal-string is a string composed entirely from string literals
535: * in the source code (not from user input). Used for SQL injection prevention.
536: */
537: public function isLiteralString(): TrinaryLogic;
538:
539: public function isLowercaseString(): TrinaryLogic;
540:
541: public function isUppercaseString(): TrinaryLogic;
542:
543: public function isClassString(): TrinaryLogic;
544:
545: public function isVoid(): TrinaryLogic;
546:
547: public function isScalar(): TrinaryLogic;
548:
549: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType;
550:
551: /**
552: * Type narrowing methods for comparison operators.
553: * For example, for ConstantIntegerType(5), getSmallerType() returns int<min, 4>.
554: */
555: public function getSmallerType(PhpVersion $phpVersion): Type;
556:
557: public function getSmallerOrEqualType(PhpVersion $phpVersion): Type;
558:
559: public function getGreaterType(PhpVersion $phpVersion): Type;
560:
561: public function getGreaterOrEqualType(PhpVersion $phpVersion): Type;
562:
563: /**
564: * Returns actual template type for a given object.
565: *
566: * Example:
567: *
568: * @-template T
569: * class Foo {}
570: *
571: * // $fooType is Foo<int>
572: * $t = $fooType->getTemplateType(Foo::class, 'T');
573: * $t->isInteger(); // yes
574: *
575: * Returns ErrorType in case of a missing type.
576: *
577: * @param class-string $ancestorClassName
578: */
579: public function getTemplateType(string $ancestorClassName, string $templateTypeName): Type;
580:
581: /**
582: * Infers the real types of TemplateTypes found in $this, based on
583: * the received Type. E.g. if $this is array<T> and $receivedType
584: * is array<int>, infers T = int.
585: */
586: public function inferTemplateTypes(Type $receivedType): TemplateTypeMap;
587:
588: /**
589: * Returns the template types referenced by this Type, recursively.
590: *
591: * The return value is a list of TemplateTypeReferences, who contain the
592: * referenced template type as well as the variance position in which it was
593: * found.
594: *
595: * For example, calling this on array<Foo<T>,Bar> (with T a template type)
596: * will return one TemplateTypeReference for the type T.
597: *
598: * @param TemplateTypeVariance $positionVariance The variance position in
599: * which the receiver type was
600: * found.
601: *
602: * @return list<TemplateTypeReference>
603: */
604: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array;
605:
606: /** Models abs(). */
607: public function toAbsoluteNumber(): Type;
608:
609: /**
610: * Returns a new instance with all inner types mapped through $cb.
611: * Returns the same instance if inner types did not change.
612: *
613: * Not used directly — use TypeTraverser::map() instead.
614: *
615: * @param callable(Type):Type $cb
616: */
617: public function traverse(callable $cb): Type;
618:
619: /**
620: * Like traverse(), but walks two types simultaneously.
621: *
622: * Not used directly — use SimultaneousTypeTraverser::map() instead.
623: *
624: * @param callable(Type $left, Type $right): Type $cb
625: */
626: public function traverseSimultaneously(Type $right, callable $cb): Type;
627:
628: public function toPhpDocNode(): TypeNode;
629:
630: /** @see TypeCombinator::remove() */
631: public function tryRemove(Type $typeToRemove): ?Type;
632:
633: /**
634: * Removes constant value information. E.g. 'foo' -> string, 1 -> int.
635: * Used when types become too complex to track precisely (e.g. loop iterations).
636: */
637: public function generalize(GeneralizePrecision $precision): Type;
638:
639: /**
640: * Performance optimization to skip template resolution when no templates are present.
641: */
642: public function hasTemplateOrLateResolvableType(): bool;
643:
644: }
645: