From d4920e21b0a1e0f564b003aabf03fff89694961c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Tvrd=C3=ADk?= Date: Wed, 26 Aug 2026 11:08:42 +0200 Subject: [PATCH] Make BackedEnum generic `BackedEnum` now carries the backing type: `@template-covariant T of int|string`. `BackedEnum::$value` is `string` instead of `int|string`, and `value-of` for `@template T of BackedEnum` resolves to `string`. `from()` and `tryFrom()` take a method-level `@template TValue of T` instead of `@param T`. `T` stays out of contravariant position, which the variance check forbids for a covariant type parameter, while `BackedEnum::from('a')` is still reported. `@throws` tags from phpstorm-stubs are preserved so that the stub does not silence result-unused and dead-catch analysis around `from()`. Three things keep this backwards compatible: * `T` defaults to `int|string`, so a bare `BackedEnum` in PHPDoc keeps its old meaning and is not reported as missing type arguments on level 6 and above. * `T` is covariant, so every backed enum stays assignable to a bare `BackedEnum`. * PHP implements `BackedEnum` implicitly, so there is no `@implements` tag that could carry the type argument. `ClassReflection::getImmediateInterfaces()` fills it in from the enum backing type instead. Two supporting changes were needed for that last point: * `ClassReflection::getInterfaces()` and `ObjectType::getAncestorWithClassName()` now prefer the interface as resolved on the class itself over the same interface reached through one of its ancestors. Without this, an enum that implements an interface which extends `BackedEnum` resolved to the ancestor type arguments of that interface. * A generic ancestor with no `@implements` or `@extends` tag now uses the declared template defaults instead of error types, when every template type has a default. This is what makes `interface Foo extends BackedEnum` resolve to `BackedEnum` rather than `BackedEnum<*ERROR*>`. Because the interface is implemented implicitly, `EnumAncestorsRule` now accepts an `@implements BackedEnum` tag on a backed enum instead of reporting that the enum "does not implement any interface", and reports `enum.implementsBackingType` when the tag contradicts the actual backing type. This gives downstream code that annotated backed enums this way (e.g. via shipmonk/phpstan-rules' BackedEnumGenericsRule) a working migration path. Co-authored-by: Claude Code Claude-Session: https://claude.ai/code/session_01QCdFyyKvMdEaU4bywFUist Amp-Thread-ID: https://ampcode.com/threads/T-01a19219-874b-7614-bea5-a5f416095107 --- conf/config.neon | 1 + src/Reflection/ClassReflection.php | 43 ++- .../Php/PhpClassReflectionExtension.php | 10 +- src/Rules/Generics/EnumAncestorsRule.php | 37 +- src/Type/ObjectType.php | 10 + src/Type/ValueOfType.php | 28 ++ stubs/BackedEnum.stub | 28 ++ .../Analyser/nsrt/generic-backed-enum.php | 119 +++++++ .../CallToFunctionParametersRuleTest.php | 15 + ...ssingFunctionParameterTypehintRuleTest.php | 7 + .../data/generic-backed-enum-acceptance.php | 64 ++++ .../data/generic-backed-enum-typehints.php | 21 ++ .../Rules/Generics/EnumAncestorsRuleTest.php | 8 + .../Rules/Generics/data/enum-ancestors.php | 24 ++ .../Methods/CallStaticMethodsRuleTest.php | 20 ++ .../Methods/data/generic-backed-enum.php | 44 +++ turbo-ext/src/ClassReflection.cpp | 88 ++++- turbo-ext/src/ObjectType.cpp | 14 + turbo-ext/src/PhpClassReflectionExtension.cpp | 17 +- turbo-ext/src/ValueOfType.cpp | 73 +++- turbo-ext/src/generated/ClassReflection.h | 329 +++++++++--------- .../tests/php-class-reflection-family.php | 6 +- 22 files changed, 832 insertions(+), 174 deletions(-) create mode 100644 stubs/BackedEnum.stub create mode 100644 tests/PHPStan/Analyser/nsrt/generic-backed-enum.php create mode 100644 tests/PHPStan/Rules/Functions/data/generic-backed-enum-acceptance.php create mode 100644 tests/PHPStan/Rules/Functions/data/generic-backed-enum-typehints.php create mode 100644 tests/PHPStan/Rules/Methods/data/generic-backed-enum.php diff --git a/conf/config.neon b/conf/config.neon index 0eb6c0c764b..75b54637285 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -166,6 +166,7 @@ parameters: - ../stubs/ArrayObject.stub - ../stubs/WeakReference.stub - ../stubs/SensitiveParameterValue.stub + - ../stubs/BackedEnum.stub - ../stubs/ImagickPixel.stub - ../stubs/PDOStatement.stub - ../stubs/date.stub diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index adae02c119c..df8ac1cf61b 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -1194,6 +1194,12 @@ public function getInterfaces(): array } } + // An interface resolved on this class itself is more specific than the same + // interface reached through one of its ancestors. + foreach ($immediateInterfaces as $name => $immediateInterface) { + $interfaces[$name] = $immediateInterface; + } + $this->cachedInterfaces = $interfaces; return $interfaces; @@ -1280,7 +1286,7 @@ public function getImmediateInterfaces(): array if ($immediateInterface->isGeneric()) { $immediateInterfaces[$immediateInterface->getName()] = $immediateInterface->withTypes( - array_values($immediateInterface->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes()), + $this->getUnspecifiedAncestorTypes($immediateInterface), ); continue; } @@ -1288,9 +1294,44 @@ public function getImmediateInterfaces(): array $immediateInterfaces[$immediateInterface->getName()] = $immediateInterface; } + // PHP implicitly implements BackedEnum on backed enums, so there is no + // @implements tag that could carry the backing type. + $backedEnumType = $this->getBackedEnumType(); + if ($backedEnumType !== null && $this->reflectionProvider->hasClass('BackedEnum')) { + $immediateInterfaces['BackedEnum'] = $this->reflectionProvider->getClass('BackedEnum') + ->withTypes([$backedEnumType]); + } + return $immediateInterfaces; } + /** + * Type arguments for a generic ancestor that is not described by an @implements + * or @extends tag. Declared template defaults are used when the ancestor has + * one for every template type, otherwise the type arguments stay erroneous. + * + * @return list + */ + private function getUnspecifiedAncestorTypes(ClassReflection $ancestor): array + { + $defaults = []; + foreach ($ancestor->getTemplateTags() as $templateTag) { + $default = $templateTag->getDefault(); + if ($default === null) { + $defaults = null; + break; + } + + $defaults[] = $default; + } + + if ($defaults !== null) { + return $defaults; + } + + return array_values($ancestor->getTemplateTypeMap()->map(static fn (): Type => new ErrorType())->getTypes()); + } + /** * @return array */ diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php index 55dbd02acae..74e96f00acb 100644 --- a/src/Reflection/Php/PhpClassReflectionExtension.php +++ b/src/Reflection/Php/PhpClassReflectionExtension.php @@ -61,6 +61,7 @@ use PHPStan\Type\Generic\TemplateTypeHelper; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Generic\TemplateTypeVariance; +use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\NeverType; use PHPStan\Type\StringType; @@ -805,10 +806,15 @@ private function createMethod( $this->findMethodTrait($methodReflection), ); - if ($this->backedEnumFromParameterType && in_array(strtolower($methodReflection->getName()), ['from', 'tryfrom'], true)) { + if (in_array(strtolower($methodReflection->getName()), ['from', 'tryfrom'], true)) { $backedEnumType = $declaringClass->getBackedEnumType(); if ($backedEnumType !== null) { - return new BackedEnumFromMethodReflection($method, $backedEnumType); + // Without the toggle the parameter stays the int|string reflection + // declares, not the TValue of T the generic BackedEnum stub gives it. + return new BackedEnumFromMethodReflection( + $method, + $this->backedEnumFromParameterType ? $backedEnumType : new UnionType([new IntegerType(), new StringType()]), + ); } } diff --git a/src/Rules/Generics/EnumAncestorsRule.php b/src/Rules/Generics/EnumAncestorsRule.php index d85b8c51721..0651f360405 100644 --- a/src/Rules/Generics/EnumAncestorsRule.php +++ b/src/Rules/Generics/EnumAncestorsRule.php @@ -11,7 +11,11 @@ use PHPStan\PhpDoc\Tag\ExtendsTag; use PHPStan\PhpDoc\Tag\ImplementsTag; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\Generic\GenericObjectType; +use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use PHPStan\Type\VerbosityLevel; use function array_map; use function array_merge; use function sprintf; @@ -64,8 +68,15 @@ public function processNode(Node $node, Scope $scope): array '', ); + $implementsNames = $originalNode->implements; + if ($classReflection->isBackedEnum()) { + // PHP implicitly implements BackedEnum on backed enums, so an + // @implements tag for it has no counterpart in the declared list. + $implementsNames[] = new Node\Name\FullyQualified('BackedEnum'); + } + $implementsErrors = $this->genericAncestorsCheck->check( - $originalNode->implements, + $implementsNames, array_map(static fn (ImplementsTag $tag): Type => $tag->getType(), $classReflection->getImplementsTags()), sprintf('Enum %s @implements tag contains incompatible type %%s.', $escapedEnumName), sprintf('Enum %s @implements tag contains unresolvable type.', $enumName), @@ -81,6 +92,30 @@ public function processNode(Node $node, Scope $scope): array sprintf('in implemented type %%s of enum %s', $escapedEnumName), ); + $backedEnumType = $classReflection->getBackedEnumType(); + if ($backedEnumType !== null) { + $expectedTagType = new GenericObjectType('BackedEnum', [$backedEnumType]); + $bareTagType = new ObjectType('BackedEnum'); + foreach ($classReflection->getImplementsTags() as $implementsTag) { + $implementsTagType = $implementsTag->getType(); + if ($implementsTagType->getObjectClassNames() !== ['BackedEnum']) { + continue; + } + if ($implementsTagType->equals($bareTagType) || $implementsTagType->equals($expectedTagType)) { + continue; + } + + $implementsErrors[] = RuleErrorBuilder::message(sprintf( + 'The @implements tag of enum %s specifies %s but the enum is backed by %s.', + $enumName, + $implementsTagType->describe(VerbosityLevel::typeOnly()), + $backedEnumType->describe(VerbosityLevel::typeOnly()), + )) + ->identifier('enum.implementsBackingType') + ->build(); + } + } + foreach ($this->crossCheckInterfacesHelper->check($classReflection) as $error) { $implementsErrors[] = $error; } diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index b358bf41754..0638951d6fb 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -2013,6 +2013,16 @@ public function getAncestorWithClassName(string $className): ?self return self::$ancestors[$description][$className] = $this->currentAncestors[$className] = $this; } + // An interface resolved on this class itself is more specific than the same + // interface reached through one of its ancestors. + foreach ($this->getInterfaces() as $interface) { + if ($interface->getClassName() !== $className) { + continue; + } + + return self::$ancestors[$description][$className] = $this->currentAncestors[$className] = $interface; + } + foreach ($this->getInterfaces() as $interface) { $ancestor = $interface->getAncestorWithClassName($className); if ($ancestor !== null) { diff --git a/src/Type/ValueOfType.php b/src/Type/ValueOfType.php index d1bb043e6e8..aa30a114e65 100644 --- a/src/Type/ValueOfType.php +++ b/src/Type/ValueOfType.php @@ -10,6 +10,7 @@ use PHPStan\Type\Generic\TemplateTypeVariance; use PHPStan\Type\Traits\LateResolvableTypeTrait; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; +use function array_values; use function count; use function sprintf; @@ -60,6 +61,33 @@ protected function getResult(): Type && $this->type instanceof TemplateType && (new ObjectType('BackedEnum'))->isSuperTypeOf($this->type->getBound())->yes() ) { + $backingTypes = []; + foreach ($this->type->getBound()->getObjectClassReflections() as $classReflection) { + $ancestor = $classReflection->getAncestorWithClassName('BackedEnum'); + if ($ancestor === null) { + $backingTypes = []; + break; + } + + $ancestorTypes = $ancestor->getActiveTemplateTypeMap()->getTypes(); + if (count($ancestorTypes) !== 1) { + $backingTypes = []; + break; + } + + $backingType = array_values($ancestorTypes)[0]; + if ($backingType instanceof ErrorType) { + $backingTypes = []; + break; + } + + $backingTypes[] = $backingType; + } + + if ($backingTypes !== []) { + return TypeCombinator::union(...$backingTypes); + } + return new UnionType([new IntegerType(), new StringType()]); } diff --git a/stubs/BackedEnum.stub b/stubs/BackedEnum.stub new file mode 100644 index 00000000000..99b156fac15 --- /dev/null +++ b/stubs/BackedEnum.stub @@ -0,0 +1,28 @@ += 8.1 + +declare(strict_types = 1); + +namespace GenericBackedEnum; + +use BackedEnum; +use UnitEnum; +use function PHPStan\Testing\assertType; + +enum StringEnum: string +{ + + case A = 'a'; + case B = 'b'; + +} + +enum IntEnum: int +{ + + case One = 1; + +} + +enum PureEnum +{ + + case X; + +} + +interface HasLabel extends BackedEnum +{ + +} + +/** + * @extends BackedEnum + */ +interface StringBackedInterface extends BackedEnum +{ + +} + +enum ViaInterface: string implements HasLabel +{ + + case A = 'a'; + +} + +enum ViaStringInterface: string implements StringBackedInterface +{ + + case A = 'a'; + +} + +function bare(BackedEnum $e): void +{ + assertType('int|string', $e->value); + assertType('non-decimal-int-string&non-falsy-string', $e->name); +} + +/** + * @param BackedEnum $e + * @param BackedEnum $i + */ +function withTypes(BackedEnum $e, BackedEnum $i): void +{ + assertType('string', $e->value); + assertType('int', $i->value); + assertType('BackedEnum', $e::from('a')); + assertType('BackedEnum|null', $e::tryFrom('a')); +} + +function unresolvedInterface(HasLabel $e): void +{ + assertType('int|string', $e->value); +} + +function resolvedInterface(StringBackedInterface $e): void +{ + assertType('string', $e->value); +} + +/** + * @template T of BackedEnum + * @param T $e + * @return value-of + */ +function valueOfAny(BackedEnum $e) +{ + return $e->value; +} + +/** + * @template T of BackedEnum + * @param class-string $className + * @return T + */ +function fromString(string $className, string $value): BackedEnum +{ + return $className::from($value); +} + +function templates(): void +{ + assertType("'a'", valueOfAny(StringEnum::A)); + assertType('1', valueOfAny(IntEnum::One)); + assertType('GenericBackedEnum\StringEnum', fromString(StringEnum::class, 'a')); +} + +function unitEnumIsNotGeneric(UnitEnum $e, PureEnum $p): void +{ + assertType('non-decimal-int-string&non-falsy-string', $e->name); + assertType("'X'", $p->name); +} diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 67b5837fe6d..2117ddcb94b 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -3394,4 +3394,19 @@ private function getBug15487Errors(): array ]; } + #[RequiresPhp('>= 8.1.0')] + public function testGenericBackedEnumAcceptance(): void + { + $this->analyse([__DIR__ . '/data/generic-backed-enum-acceptance.php'], [ + [ + 'Parameter #1 $e of function GenericBackedEnumAcceptance\acceptsStringBacked expects BackedEnum, GenericBackedEnumAcceptance\IntEnum given.', + 62, + ], + [ + 'Parameter #1 $e of function GenericBackedEnumAcceptance\acceptsStringBacked expects BackedEnum, GenericBackedEnumAcceptance\HasLabel given.', + 63, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php index 7d43d80c8de..13cd363668a 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php @@ -5,6 +5,7 @@ use PHPStan\Rules\MissingTypehintCheck; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; +use PHPUnit\Framework\Attributes\RequiresPhp; /** * @extends RuleTestCase @@ -96,4 +97,10 @@ public function testRule(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testGenericBackedEnum(): void + { + $this->analyse([__DIR__ . '/data/generic-backed-enum-typehints.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/generic-backed-enum-acceptance.php b/tests/PHPStan/Rules/Functions/data/generic-backed-enum-acceptance.php new file mode 100644 index 00000000000..1c55b680609 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/generic-backed-enum-acceptance.php @@ -0,0 +1,64 @@ += 8.1 + +declare(strict_types = 1); + +namespace GenericBackedEnumAcceptance; + +use BackedEnum; + +enum StringEnum: string +{ + + case A = 'a'; + +} + +enum IntEnum: int +{ + + case One = 1; + +} + +interface HasLabel extends BackedEnum +{ + +} + +/** + * @extends BackedEnum + */ +interface StringBackedInterface extends BackedEnum +{ + +} + +enum ViaInterface: string implements HasLabel +{ + + case A = 'a'; + +} + +enum ViaStringInterface: string implements StringBackedInterface +{ + + case A = 'a'; + +} + +/** + * @param BackedEnum $e + */ +function acceptsStringBacked(BackedEnum $e): void +{ +} + +function test(StringEnum $a, ViaInterface $b, ViaStringInterface $c, IntEnum $d, HasLabel $e): void +{ + acceptsStringBacked($a); + acceptsStringBacked($b); + acceptsStringBacked($c); + acceptsStringBacked($d); + acceptsStringBacked($e); +} diff --git a/tests/PHPStan/Rules/Functions/data/generic-backed-enum-typehints.php b/tests/PHPStan/Rules/Functions/data/generic-backed-enum-typehints.php new file mode 100644 index 00000000000..3571e7eca5f --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/generic-backed-enum-typehints.php @@ -0,0 +1,21 @@ += 8.1 + +declare(strict_types = 1); + +namespace GenericBackedEnumTypehints; + +use BackedEnum; + +function bareIsNotMissingTypes(BackedEnum $e): BackedEnum +{ + return $e; +} + +/** + * @param BackedEnum $e + * @return BackedEnum + */ +function withTypes(BackedEnum $e): BackedEnum +{ + return $e; +} diff --git a/tests/PHPStan/Rules/Generics/EnumAncestorsRuleTest.php b/tests/PHPStan/Rules/Generics/EnumAncestorsRuleTest.php index 101bca4eb3c..d2b5e9075fa 100644 --- a/tests/PHPStan/Rules/Generics/EnumAncestorsRuleTest.php +++ b/tests/PHPStan/Rules/Generics/EnumAncestorsRuleTest.php @@ -56,6 +56,14 @@ public function testRule(): void 'Call-site variance annotation of covariant EnumGenericAncestors\NonGeneric in generic type EnumGenericAncestors\Generic in PHPDoc tag @implements is not allowed.', 93, ], + [ + 'The @implements tag of enum EnumGenericAncestors\BackedEnumWrongTag specifies BackedEnum but the enum is backed by int.', + 122, + ], + [ + 'Enum EnumGenericAncestors\BackedEnumTagOnPureEnum has @implements tag, but does not implement any interface.', + 130, + ], ]); } diff --git a/tests/PHPStan/Rules/Generics/data/enum-ancestors.php b/tests/PHPStan/Rules/Generics/data/enum-ancestors.php index 1cda1bcbcd4..11df5ad5a57 100644 --- a/tests/PHPStan/Rules/Generics/data/enum-ancestors.php +++ b/tests/PHPStan/Rules/Generics/data/enum-ancestors.php @@ -107,3 +107,27 @@ enum Foo9 implements GenericDefault { } + +/** + * @implements \BackedEnum + */ +enum BackedEnumRightTag: string +{ + +} + +/** + * @implements \BackedEnum + */ +enum BackedEnumWrongTag: int +{ + +} + +/** + * @implements \BackedEnum + */ +enum BackedEnumTagOnPureEnum +{ + +} diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index 33f7859302b..9df61ac689a 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -1156,4 +1156,24 @@ public function testBug11421(): void ]); } + #[RequiresPhp('>= 8.1.0')] + public function testGenericBackedEnum(): void + { + $this->checkThisOnly = false; + $this->analyse([__DIR__ . '/data/generic-backed-enum.php'], [ + [ + 'Parameter #1 $value of static method BackedEnum::from() expects TValue of int, string given.', + 23, + ], + [ + 'Parameter #1 $value of static method BackedEnum::tryFrom() expects TValue of string, int given.', + 24, + ], + [ + 'Parameter #1 $value of static method BackedEnum::from() expects TValue of string, int given.', + 36, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/generic-backed-enum.php b/tests/PHPStan/Rules/Methods/data/generic-backed-enum.php new file mode 100644 index 00000000000..d9e595dbcd2 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/generic-backed-enum.php @@ -0,0 +1,44 @@ += 8.1 + +declare(strict_types = 1); + +namespace GenericBackedEnumStaticCall; + +use BackedEnum; + +enum StringEnum: string +{ + + case A = 'a'; + +} + +/** + * @param BackedEnum $intBacked + * @param BackedEnum $stringBacked + */ +function backedEnumInterface(BackedEnum $intBacked, BackedEnum $stringBacked, BackedEnum $bare, int $i, string $s): void +{ + $intBacked::from($i); + $intBacked::from($s); + $stringBacked::tryFrom($i); + $stringBacked::tryFrom($s); + $bare::from($i); + $bare::from($s); +} + +/** + * @template T of BackedEnum + * @param class-string $className + */ +function stringBackedClassString(string $className, int $i, string $s): void +{ + $className::from($i); + $className::from($s); +} + +function concreteEnum(string $s): void +{ + StringEnum::from($s); + StringEnum::tryFrom($s); +} diff --git a/turbo-ext/src/ClassReflection.cpp b/turbo-ext/src/ClassReflection.cpp index 805e7c5c378..f87df71eb8b 100644 --- a/turbo-ext/src/ClassReflection.cpp +++ b/turbo-ext/src/ClassReflection.cpp @@ -464,6 +464,7 @@ class ClassReflection static zv::Val crGetFileName(zv::Ref cr) { return crCall(cr, PT_LC("getfilename"), [](ClassReflection other) { return other.getFileName(); }); } static zv::Val crGetAncestors(zv::Ref cr) { return crCall(cr, PT_LC("getancestors"), [](ClassReflection other) { return other.getAncestors(); }); } static zv::Val crGetTypeAliases(zv::Ref cr) { return crCall(cr, PT_LC("gettypealiases"), [](ClassReflection other) { return other.getTypeAliases(); }); } + static zv::Val crGetTemplateTags(zv::Ref cr) { return crCall(cr, PT_LC("gettemplatetags"), [](ClassReflection other) { return other.getTemplateTags(); }); } static zv::Val crGetTemplateTypeMap(zv::Ref cr) { return crCall(cr, PT_LC("gettemplatetypemap"), [](ClassReflection other) { return other.getTemplateTypeMap(); }); } static zv::Val crGetActiveTemplateTypeMap(zv::Ref cr) { return crCall(cr, PT_LC("getactivetemplatetypemap"), [](ClassReflection other) { return other.getActiveTemplateTypeMap(); }); } static zv::Val crGetCallSiteVarianceMap(zv::Ref cr) { return crCall(cr, PT_LC("getcallsitevariancemap"), [](ClassReflection other) { return other.getCallSiteVarianceMap(); }); } @@ -542,6 +543,56 @@ class ClassReflection return crWithTypes(cr, list.ref()); } + /* list — type arguments for a generic ancestor that is not + * described by an @implements or @extends tag: the declared template + * defaults when every template type has one, otherwise one fresh + * ErrorType per template type */ + static zv::Val getUnspecifiedAncestorTypes(zv::Ref ancestor) + { + zv::Val templateTags = crGetTemplateTags(ancestor); + if (UNEXPECTED(templateTags.isUndef())) return zv::Val(); + zv::Arr defaults = zv::Arr::create(countOf(templateTags.ref())); + bool allDefaults = true; + if (templateTags.ref().isArray()) { + for (auto entry : zv::ArrRef(templateTags.raw())) { + zv::Ref templateTag = entry.value().deref(); + if (UNEXPECTED(!templateTag.isObject())) { + zend_throw_error(NULL, "Call to a member function getDefault() on %s", zend_zval_value_name(templateTag.raw())); + return zv::Val(); + } + zv::Val defaultType = pt_type_call(templateTag.asObject(), PT_LC("getdefault"), 0, NULL); + if (UNEXPECTED(defaultType.isUndef())) return zv::Val(); + if (defaultType.isNull()) { + allDefaults = false; + break; + } + defaults.push(std::move(defaultType)); + } + } + if (allDefaults) return zv::Val(std::move(defaults)); + + zv::Val map = crGetTemplateTypeMap(ancestor); + if (UNEXPECTED(map.isUndef())) return zv::Val(); + zv::Val types = callOn(map.ref(), PT_LC("gettypes"), 0, NULL); + if (UNEXPECTED(types.isUndef())) return zv::Val(); + uint32_t count = countOf(types.ref()); + zv::Arr errorTypes = zv::Arr::create(count); + for (uint32_t i = 0; i < count; i++) { + zv::Val errorType = pt_type_new_error_type(); + if (UNEXPECTED(errorType.isUndef())) return zv::Val(); + errorTypes.push(std::move(errorType)); + } + return zv::Val(std::move(errorTypes)); + } + + /* $cr->withTypes($this->getUnspecifiedAncestorTypes($cr)) */ + static zv::Val crWithUnspecifiedAncestorTypes(zv::Ref cr) + { + zv::Val types = getUnspecifiedAncestorTypes(cr); + if (UNEXPECTED(types.isUndef())) return zv::Val(); + return crWithTypes(cr, types.ref()); + } + /* }}} */ /* {{{ the Type kernel */ @@ -2470,6 +2521,12 @@ class ClassReflection for (auto entry : zv::ArrRef(immediateInterfaces.raw())) { if (UNEXPECTED(!addCollectedInterfaces(interfaces, entry.value()))) return zv::Val(); } + + /* An interface resolved on this class itself is more specific + * than the same interface reached through one of its ancestors. */ + for (auto entry : zv::ArrRef(immediateInterfaces.raw())) { + setAtKey(interfaces, entry, zv::Val::copyOf(entry.value())); + } } writeSlot(PT_CR_PROP_CACHED_INTERFACES, zv::Val::copyOf(interfaces.ref())); @@ -2617,15 +2674,34 @@ class ClassReflection bool immediateGeneric; if (UNEXPECTED(!crIsGeneric(immediateInterface.ref(), immediateGeneric))) return zv::Val(); if (immediateGeneric) { - zv::Val withErrorTypes = crWithErrorTypes(immediateInterface.ref()); - if (UNEXPECTED(withErrorTypes.isUndef())) return zv::Val(); - immediateInterfaces.set(immediateNameStr, std::move(withErrorTypes)); + zv::Val withDefaults = crWithUnspecifiedAncestorTypes(immediateInterface.ref()); + if (UNEXPECTED(withDefaults.isUndef())) return zv::Val(); + immediateInterfaces.set(immediateNameStr, std::move(withDefaults)); continue; } immediateInterfaces.set(immediateNameStr, std::move(immediateInterface)); } + /* PHP implicitly implements BackedEnum on backed enums, so there is + * no @implements tag that could carry the backing type. */ + zv::Val backedEnumType = getBackedEnumType(); + if (UNEXPECTED(backedEnumType.isUndef())) return zv::Val(); + if (!backedEnumType.isNull()) { + zv::Str backedEnumName = zv::Str::adopt(zend_string_init(PT_LC("BackedEnum"), 0)); + bool has; + if (UNEXPECTED(!providerHasClass(backedEnumName.get(), has))) return zv::Val(); + if (has) { + zv::Val backedEnum = providerGetClass(backedEnumName.get()); + if (UNEXPECTED(backedEnum.isUndef())) return zv::Val(); + zv::Arr types = zv::Arr::create(1); + types.push(std::move(backedEnumType)); + zv::Val withTypes = crWithTypes(backedEnum.ref(), types.ref()); + if (UNEXPECTED(withTypes.isUndef())) return zv::Val(); + immediateInterfaces.set(backedEnumName.get(), std::move(withTypes)); + } + } + return zv::Val(std::move(immediateInterfaces)); } @@ -4994,6 +5070,12 @@ PT_MINIT_REGISTRATION(pt_register_class_reflection) cls.method<&ClassReflection::getImmediateInterfaces>(sigs::getImmediateInterfaces); + cls.method(sigs::getUnspecifiedAncestorTypes, [](INTERNAL_FUNCTION_PARAMETERS) { + zval *ancestor; + if (!zp::parse(execute_data, ancestor)) RETURN_THROWS(); + PT_RETURN_VAL(ClassReflection::getUnspecifiedAncestorTypes(zv::Ref(ancestor))); + }); + cls.method(sigs::getTraits, [](INTERNAL_FUNCTION_PARAMETERS) { bool recursive = false; if (!zp::parse>(execute_data, recursive)) RETURN_THROWS(); diff --git a/turbo-ext/src/ObjectType.cpp b/turbo-ext/src/ObjectType.cpp index 37c204f98ed..075483d72ce 100644 --- a/turbo-ext/src/ObjectType.cpp +++ b/turbo-ext/src/ObjectType.cpp @@ -2618,6 +2618,20 @@ class ObjectType zv::Val interfaces = getInterfaces(); if (UNEXPECTED(interfaces.isUndef())) return zv::Val(); + /* An interface resolved on this class itself is more specific than + * the same interface reached through one of its ancestors. */ + for (zv::ArrayEntry entry : zv::ArrRef(interfaces.raw())) { + zv::Ref interface = entry.value().deref(); + if (UNEXPECTED(!interface.isObject())) { + zend_type_error("phpstan_turbo: getInterfaces() must return objects"); + return zv::Val(); + } + zv::Val interfaceClassName = pt_type_call(interface.asObject(), PT_LC("getclassname"), 0, NULL); + if (UNEXPECTED(interfaceClassName.isUndef())) return zv::Val(); + if (!zend_is_identical(interfaceClassName.raw(), className)) continue; + return storeAncestor(descriptionStr, classNameStr, zv::Val::copyOf(interface)); + } + for (zv::ArrayEntry entry : zv::ArrRef(interfaces.raw())) { zv::Ref interface = entry.value().deref(); if (UNEXPECTED(!interface.isObject())) { diff --git a/turbo-ext/src/PhpClassReflectionExtension.cpp b/turbo-ext/src/PhpClassReflectionExtension.cpp index 89b432c2f41..447d3bdffbd 100644 --- a/turbo-ext/src/PhpClassReflectionExtension.cpp +++ b/turbo-ext/src/PhpClassReflectionExtension.cpp @@ -1578,13 +1578,26 @@ class PhpClassReflectionExtension zv::Val method = createUserlandMethodReflection(declaringClass, declaringClass, methodReflection, methodTrait.raw()); if (UNEXPECTED(method.isUndef())) return zv::Val(); - if (Z_TYPE_P(slot(slots::backedEnumFromParameterType)) != IS_TRUE) return method; - zv::Str loweredName = zv::Str::adopt(zend_string_tolower(methodNameStr.get())); if (zend_string_equals_literal(loweredName.get(), "from") || zend_string_equals_literal(loweredName.get(), "tryfrom")) { zv::Val backedEnumType = call(declaringClass, PT_LC("getbackedenumtype")); if (UNEXPECTED(backedEnumType.isUndef())) return zv::Val(); if (Z_TYPE_P(backedEnumType.raw()) != IS_NULL) { + /* without the toggle the parameter stays the int|string + * reflection declares, not the TValue of T the generic + * BackedEnum stub gives it */ + if (Z_TYPE_P(slot(slots::backedEnumFromParameterType)) != IS_TRUE) { + zval integerRaw, stringRaw; + if (UNEXPECTED(!pt_integer_type_new(&integerRaw))) return zv::Val(); + zv::Val integer = zv::Val::adopt(integerRaw); + if (UNEXPECTED(!pt_string_type_new(&stringRaw))) return zv::Val(); + zv::Val string = zv::Val::adopt(stringRaw); + zv::Arr types = zv::Arr::create(2); + types.push(std::move(integer)); + types.push(std::move(string)); + backedEnumType = pt_type_new_union(std::move(types)); + if (UNEXPECTED(backedEnumType.isUndef())) return zv::Val(); + } zv::Args args{method.raw(), backedEnumType.raw()}; return pt_type_new(PT_CLASS_BACKED_ENUM_FROM_METHOD_REFLECTION, 2, args); } diff --git a/turbo-ext/src/ValueOfType.cpp b/turbo-ext/src/ValueOfType.cpp index e454582d9aa..ff9f78e8502 100644 --- a/turbo-ext/src/ValueOfType.cpp +++ b/turbo-ext/src/ValueOfType.cpp @@ -131,7 +131,12 @@ class ValueOfType if (isTemplate) { zend_long backedEnumBound = boundIsBackedEnum(t); if (UNEXPECTED(backedEnumBound < 0)) return zv::Val(); - if (backedEnumBound == 1) return intOrString(); + if (backedEnumBound == 1) { + zv::Val backingType = boundBackingType(t); + if (UNEXPECTED(backingType.isUndef())) return zv::Val(); + if (!backingType.isNull()) return backingType; + return intOrString(); + } } } zv::Arr valueTypes = zv::Arr::create(zend_hash_num_elements(cases)); @@ -219,6 +224,72 @@ class ValueOfType return value == PT_TRI_YES ? 1 : 0; } + /* the union of the BackedEnum type arguments of every class of the + * template type's bound; null when one of them has none (or an error + * type), UNDEF = pending exception */ + static zv::Val boundBackingType(zval *templateType) + { + zv::Val bound = pt_type_call(Z_OBJ_P(templateType), PT_LC("getbound"), 0, NULL); + if (UNEXPECTED(bound.isUndef())) return zv::Val(); + if (UNEXPECTED(!zv::Ref(bound.raw()).isObject())) { + zend_type_error("phpstan_turbo: getBound() must return %s", ptcls::type); + return zv::Val(); + } + zv::Val classReflections = pt_type_call(Z_OBJ_P(bound.raw()), PT_LC("getobjectclassreflections"), 0, NULL); + if (UNEXPECTED(classReflections.isUndef())) return zv::Val(); + if (!zv::Ref(classReflections.raw()).isArray()) return zv::Val::null(); + + zval backedEnumName; + ZVAL_STR(&backedEnumName, zend_string_init(PT_LC("BackedEnum"), 0)); + zv::Val backedEnumNameOwned = zv::Val::adopt(backedEnumName); + zv::Arr backingTypes = zv::Arr::create(zend_hash_num_elements(Z_ARRVAL_P(classReflections.raw()))); + for (zv::ArrayEntry entry : zv::ArrRef(classReflections.raw())) { + zv::Ref classReflection = entry.value().deref(); + if (UNEXPECTED(!classReflection.isObject())) { + zend_type_error("phpstan_turbo: getObjectClassReflections() must return a list of ClassReflection"); + return zv::Val(); + } + zv::Val ancestor = pt_type_call(classReflection.asObject(), PT_LC("getancestorwithclassname"), 1, backedEnumNameOwned.raw()); + if (UNEXPECTED(ancestor.isUndef())) return zv::Val(); + if (!zv::Ref(ancestor.raw()).isObject()) return zv::Val::null(); + + zv::Val map = pt_type_call(Z_OBJ_P(ancestor.raw()), PT_LC("getactivetemplatetypemap"), 0, NULL); + if (UNEXPECTED(map.isUndef())) return zv::Val(); + if (UNEXPECTED(!zv::Ref(map.raw()).isObject())) { + zend_type_error("phpstan_turbo: getActiveTemplateTypeMap() must return a TemplateTypeMap"); + return zv::Val(); + } + zv::Val ancestorTypes = pt_type_call(Z_OBJ_P(map.raw()), PT_LC("gettypes"), 0, NULL); + if (UNEXPECTED(ancestorTypes.isUndef())) return zv::Val(); + if (!zv::Ref(ancestorTypes.raw()).isArray() || zend_hash_num_elements(Z_ARRVAL_P(ancestorTypes.raw())) != 1) return zv::Val::null(); + + zv::Val backingType; + for (zv::ArrayEntry typeEntry : zv::ArrRef(ancestorTypes.raw())) { + backingType = zv::Val::copyOf(typeEntry.value().deref()); + } + if (UNEXPECTED(!zv::Ref(backingType.raw()).isObject())) { + zend_type_error("phpstan_turbo: TemplateTypeMap::getTypes() must return a list of %s", ptcls::type); + return zv::Val(); + } + bool isError; + if (UNEXPECTED(!pt_type_instanceof_ce(backingType.raw(), pt_ce_error_type, isError))) return zv::Val(); + if (isError) return zv::Val::null(); + + backingTypes.push(std::move(backingType)); + } + + uint32_t count = zend_hash_num_elements(backingTypes.table()); + if (count == 0) return zv::Val::null(); + zval *argv = (zval *) safe_emalloc(count, sizeof(zval), 0); + uint32_t i = 0; + for (zv::ArrayEntry typeEntry : zv::ArrRef(backingTypes.raw())) { + ZVAL_COPY_VALUE(&argv[i++], typeEntry.value().raw()); + } + zv::Val union_ = pt_type_combinator_union(count, argv); + efree(argv); + return union_; + } + /* new UnionType([new IntegerType(), new StringType()]) */ static zv::Val intOrString() { diff --git a/turbo-ext/src/generated/ClassReflection.h b/turbo-ext/src/generated/ClassReflection.h index ee4d8bc5ff7..5c5bbecbe56 100644 --- a/turbo-ext/src/generated/ClassReflection.h +++ b/turbo-ext/src/generated/ClassReflection.h @@ -248,86 +248,88 @@ inline constexpr char strings[] = "interface\0" /* 2342 */ "collectInterfaces\0" /* 2352 */ "getImmediateInterfaces\0" /* 2370 */ - "recursive\0" /* 2393 */ - "false\0" /* 2403 */ - "getTraits\0" /* 2409 */ - "getParentClassesNames\0" /* 2419 */ - "hasConstant\0" /* 2441 */ - "PHPStan\\Reflection\\ClassConstantReflection\0" /* 2453 */ - "getConstant\0" /* 2496 */ - "getConstantPhpDocType\0" /* 2508 */ - "reflectionConstant\0" /* 2530 */ - "PHPStan\\BetterReflection\\Reflection\\Adapter\\ReflectionClassConstant\0" /* 2549 */ - "PHPStan\\PhpDoc\\ResolvedPhpDocBlock\0" /* 2617 */ - "findConstantResolvedPhpDoc\0" /* 2652 */ - "resolvedPhpDoc\0" /* 2679 */ - "nativeType\0" /* 2694 */ - "declaringClass\0" /* 2705 */ - "resolveConstantVarPhpDocType\0" /* 2720 */ - "traitName\0" /* 2749 */ - "hasTraitUse\0" /* 2759 */ - "getTraitNames\0" /* 2771 */ - "getTypeAliases\0" /* 2785 */ - "getDeprecatedDescription\0" /* 2800 */ - "isDeprecated\0" /* 2825 */ - "resolveDeprecation\0" /* 2838 */ - "isBuiltin\0" /* 2857 */ - "isInternal\0" /* 2867 */ - "isFinal\0" /* 2878 */ - "isImmutable\0" /* 2886 */ - "hasConsistentConstructor\0" /* 2898 */ - "acceptsNamedArguments\0" /* 2923 */ - "hasFinalByKeywordOverride\0" /* 2945 */ - "isFinalByKeyword\0" /* 2971 */ - "isAttributeClass\0" /* 2988 */ - "findAttributeFlags\0" /* 3005 */ - "getAttributes\0" /* 3024 */ - "getAttributeClassFlags\0" /* 3038 */ - "PHPStan\\Type\\ObjectType\0" /* 3061 */ - "getObjectType\0" /* 3085 */ - "getTemplateTypeMap\0" /* 3099 */ - "getActiveTemplateTypeMap\0" /* 3118 */ - "getPossiblyIncompleteActiveTemplateTypeMap\0" /* 3143 */ - "getActiveTemplateTypeMapForAncestorResolution\0" /* 3186 */ - "getDefaultCallSiteVarianceMap\0" /* 3232 */ - "getCallSiteVarianceMap\0" /* 3262 */ - "isGeneric\0" /* 3285 */ - "types\0" /* 3295 */ - "typeMapFromList\0" /* 3301 */ - "variances\0" /* 3317 */ - "varianceMapFromList\0" /* 3327 */ - "typeMap\0" /* 3347 */ - "typeMapToList\0" /* 3355 */ - "varianceMap\0" /* 3369 */ - "varianceMapToList\0" /* 3381 */ - "withTypes\0" /* 3399 */ - "withVariances\0" /* 3409 */ - "asFinal\0" /* 3423 */ - "withoutFinalByKeywordOverride\0" /* 3431 */ - "removeFinalKeywordOverride\0" /* 3461 */ - "getResolvedPhpDoc\0" /* 3488 */ - "implementingClass\0" /* 3506 */ - "getTraitContextResolvedPhpDoc\0" /* 3524 */ - "PHPStan\\PhpDoc\\Tag\\ExtendsTag\0" /* 3554 */ - "getFirstExtendsTag\0" /* 3584 */ - "getExtendsTags\0" /* 3603 */ - "getImplementsTags\0" /* 3618 */ - "getTemplateTags\0" /* 3636 */ - "getAncestors\0" /* 3652 */ - "ancestors\0" /* 3665 */ - "collectAncestors\0" /* 3675 */ - "getAncestorWithClassName\0" /* 3692 */ - "type\0" /* 3717 */ - "ancestorClasses\0" /* 3722 */ - "isValidAncestorType\0" /* 3738 */ - "getMixinTags\0" /* 3758 */ - "getRequireExtendsTags\0" /* 3771 */ - "getRequireImplementsTags\0" /* 3793 */ - "getSealedTags\0" /* 3818 */ - "getPropertyTags\0" /* 3832 */ - "getMethodTags\0" /* 3848 */ - "getResolvedMixinTypes\0" /* 3862 */ - "getAllowedSubTypes"; /* 3884 */ + "ancestor\0" /* 2393 */ + "getUnspecifiedAncestorTypes\0" /* 2402 */ + "recursive\0" /* 2430 */ + "false\0" /* 2440 */ + "getTraits\0" /* 2446 */ + "getParentClassesNames\0" /* 2456 */ + "hasConstant\0" /* 2478 */ + "PHPStan\\Reflection\\ClassConstantReflection\0" /* 2490 */ + "getConstant\0" /* 2533 */ + "getConstantPhpDocType\0" /* 2545 */ + "reflectionConstant\0" /* 2567 */ + "PHPStan\\BetterReflection\\Reflection\\Adapter\\ReflectionClassConstant\0" /* 2586 */ + "PHPStan\\PhpDoc\\ResolvedPhpDocBlock\0" /* 2654 */ + "findConstantResolvedPhpDoc\0" /* 2689 */ + "resolvedPhpDoc\0" /* 2716 */ + "nativeType\0" /* 2731 */ + "declaringClass\0" /* 2742 */ + "resolveConstantVarPhpDocType\0" /* 2757 */ + "traitName\0" /* 2786 */ + "hasTraitUse\0" /* 2796 */ + "getTraitNames\0" /* 2808 */ + "getTypeAliases\0" /* 2822 */ + "getDeprecatedDescription\0" /* 2837 */ + "isDeprecated\0" /* 2862 */ + "resolveDeprecation\0" /* 2875 */ + "isBuiltin\0" /* 2894 */ + "isInternal\0" /* 2904 */ + "isFinal\0" /* 2915 */ + "isImmutable\0" /* 2923 */ + "hasConsistentConstructor\0" /* 2935 */ + "acceptsNamedArguments\0" /* 2960 */ + "hasFinalByKeywordOverride\0" /* 2982 */ + "isFinalByKeyword\0" /* 3008 */ + "isAttributeClass\0" /* 3025 */ + "findAttributeFlags\0" /* 3042 */ + "getAttributes\0" /* 3061 */ + "getAttributeClassFlags\0" /* 3075 */ + "PHPStan\\Type\\ObjectType\0" /* 3098 */ + "getObjectType\0" /* 3122 */ + "getTemplateTypeMap\0" /* 3136 */ + "getActiveTemplateTypeMap\0" /* 3155 */ + "getPossiblyIncompleteActiveTemplateTypeMap\0" /* 3180 */ + "getActiveTemplateTypeMapForAncestorResolution\0" /* 3223 */ + "getDefaultCallSiteVarianceMap\0" /* 3269 */ + "getCallSiteVarianceMap\0" /* 3299 */ + "isGeneric\0" /* 3322 */ + "types\0" /* 3332 */ + "typeMapFromList\0" /* 3338 */ + "variances\0" /* 3354 */ + "varianceMapFromList\0" /* 3364 */ + "typeMap\0" /* 3384 */ + "typeMapToList\0" /* 3392 */ + "varianceMap\0" /* 3406 */ + "varianceMapToList\0" /* 3418 */ + "withTypes\0" /* 3436 */ + "withVariances\0" /* 3446 */ + "asFinal\0" /* 3460 */ + "withoutFinalByKeywordOverride\0" /* 3468 */ + "removeFinalKeywordOverride\0" /* 3498 */ + "getResolvedPhpDoc\0" /* 3525 */ + "implementingClass\0" /* 3543 */ + "getTraitContextResolvedPhpDoc\0" /* 3561 */ + "PHPStan\\PhpDoc\\Tag\\ExtendsTag\0" /* 3591 */ + "getFirstExtendsTag\0" /* 3621 */ + "getExtendsTags\0" /* 3640 */ + "getImplementsTags\0" /* 3655 */ + "getTemplateTags\0" /* 3673 */ + "getAncestors\0" /* 3689 */ + "ancestors\0" /* 3702 */ + "collectAncestors\0" /* 3712 */ + "getAncestorWithClassName\0" /* 3729 */ + "type\0" /* 3754 */ + "ancestorClasses\0" /* 3759 */ + "isValidAncestorType\0" /* 3775 */ + "getMixinTags\0" /* 3795 */ + "getRequireExtendsTags\0" /* 3808 */ + "getRequireImplementsTags\0" /* 3830 */ + "getSealedTags\0" /* 3855 */ + "getPropertyTags\0" /* 3869 */ + "getMethodTags\0" /* 3885 */ + "getResolvedMixinTypes\0" /* 3899 */ + "getAllowedSubTypes"; /* 3921 */ inline constexpr reg::PackedArg args[] = { reg::packed(0, 0, 23), /* __construct $classReflectionFactory */ reg::packed(65, 0, 84), /* __construct $reflectionProvider */ @@ -425,22 +427,24 @@ inline constexpr reg::PackedArg args[] = { reg::packed(2342, 0, 1162), /* collectInterfaces $interface */ reg::packed(1010, MAY_BE_ARRAY), /* collectInterfaces return */ reg::packed(1010, MAY_BE_ARRAY), /* getImmediateInterfaces return */ - reg::packed(2393, MAY_BE_BOOL, reg::NoString, false, false, 2403), /* getTraits $recursive */ + reg::packed(2393, 0, 1162), /* getUnspecifiedAncestorTypes $ancestor */ + reg::packed(1010, MAY_BE_ARRAY), /* getUnspecifiedAncestorTypes return */ + reg::packed(2430, MAY_BE_BOOL, reg::NoString, false, false, 2440), /* getTraits $recursive */ reg::packed(1010, MAY_BE_ARRAY), /* getTraits return */ reg::packed(1010, MAY_BE_ARRAY), /* getParentClassesNames return */ reg::packed(2153, MAY_BE_STRING), /* hasConstant $name */ reg::packed(1010, MAY_BE_BOOL), /* hasConstant return */ reg::packed(2153, MAY_BE_STRING), /* getConstant $name */ - reg::packed(1010, 0, 2453), /* getConstant return */ + reg::packed(1010, 0, 2490), /* getConstant return */ reg::packed(2153, MAY_BE_STRING), /* getConstantPhpDocType $name */ reg::packed(1010, MAY_BE_NULL, 2117), /* getConstantPhpDocType return */ - reg::packed(2530, 0, 2549), /* findConstantResolvedPhpDoc $reflectionConstant */ - reg::packed(1010, MAY_BE_NULL, 2617), /* findConstantResolvedPhpDoc return */ - reg::packed(2679, 0, 2617), /* resolveConstantVarPhpDocType $resolvedPhpDoc */ - reg::packed(2694, MAY_BE_NULL, 2117), /* resolveConstantVarPhpDocType $nativeType */ - reg::packed(2705, 0, 1162), /* resolveConstantVarPhpDocType $declaringClass */ + reg::packed(2567, 0, 2586), /* findConstantResolvedPhpDoc $reflectionConstant */ + reg::packed(1010, MAY_BE_NULL, 2654), /* findConstantResolvedPhpDoc return */ + reg::packed(2716, 0, 2654), /* resolveConstantVarPhpDocType $resolvedPhpDoc */ + reg::packed(2731, MAY_BE_NULL, 2117), /* resolveConstantVarPhpDocType $nativeType */ + reg::packed(2742, 0, 1162), /* resolveConstantVarPhpDocType $declaringClass */ reg::packed(1010, MAY_BE_NULL, 2117), /* resolveConstantVarPhpDocType return */ - reg::packed(2749, MAY_BE_STRING), /* hasTraitUse $traitName */ + reg::packed(2786, MAY_BE_STRING), /* hasTraitUse $traitName */ reg::packed(1010, MAY_BE_BOOL), /* hasTraitUse return */ reg::packed(1010, MAY_BE_ARRAY), /* getTraitNames return */ reg::packed(1010, MAY_BE_ARRAY), /* getTypeAliases return */ @@ -459,7 +463,7 @@ inline constexpr reg::PackedArg args[] = { reg::packed(1010, MAY_BE_NULL | MAY_BE_LONG), /* findAttributeFlags return */ reg::packed(1010, MAY_BE_ARRAY), /* getAttributes return */ reg::packed(1010, MAY_BE_LONG), /* getAttributeClassFlags return */ - reg::packed(1010, 0, 3061), /* getObjectType return */ + reg::packed(1010, 0, 3098), /* getObjectType return */ reg::packed(1010, 0, 814), /* getTemplateTypeMap return */ reg::packed(1010, 0, 814), /* getActiveTemplateTypeMap return */ reg::packed(1010, 0, 814), /* getPossiblyIncompleteActiveTemplateTypeMap return */ @@ -467,35 +471,35 @@ inline constexpr reg::PackedArg args[] = { reg::packed(1010, 0, 930), /* getDefaultCallSiteVarianceMap return */ reg::packed(1010, 0, 930), /* getCallSiteVarianceMap return */ reg::packed(1010, MAY_BE_BOOL), /* isGeneric return */ - reg::packed(3295, MAY_BE_ARRAY), /* typeMapFromList $types */ + reg::packed(3332, MAY_BE_ARRAY), /* typeMapFromList $types */ reg::packed(1010, 0, 814), /* typeMapFromList return */ - reg::packed(3317, MAY_BE_ARRAY), /* varianceMapFromList $variances */ + reg::packed(3354, MAY_BE_ARRAY), /* varianceMapFromList $variances */ reg::packed(1010, 0, 930), /* varianceMapFromList return */ - reg::packed(3347, 0, 814), /* typeMapToList $typeMap */ + reg::packed(3384, 0, 814), /* typeMapToList $typeMap */ reg::packed(1010, MAY_BE_ARRAY), /* typeMapToList return */ - reg::packed(3369, 0, 930), /* varianceMapToList $varianceMap */ + reg::packed(3406, 0, 930), /* varianceMapToList $varianceMap */ reg::packed(1010, MAY_BE_ARRAY), /* varianceMapToList return */ - reg::packed(3295, MAY_BE_ARRAY), /* withTypes $types */ + reg::packed(3332, MAY_BE_ARRAY), /* withTypes $types */ reg::packed(1010, 0, 1162), /* withTypes return */ - reg::packed(3317, MAY_BE_ARRAY), /* withVariances $variances */ + reg::packed(3354, MAY_BE_ARRAY), /* withVariances $variances */ reg::packed(1010, 0, 1162), /* withVariances return */ reg::packed(1010, 0, 1162), /* asFinal return */ reg::packed(1010, 0, 1162), /* withoutFinalByKeywordOverride return */ reg::packed(1010, 0, 1162), /* removeFinalKeywordOverride return */ - reg::packed(1010, MAY_BE_NULL, 2617), /* getResolvedPhpDoc return */ - reg::packed(3506, 0, 1162), /* getTraitContextResolvedPhpDoc $implementingClass */ - reg::packed(1010, MAY_BE_NULL, 2617), /* getTraitContextResolvedPhpDoc return */ - reg::packed(1010, MAY_BE_NULL, 3554), /* getFirstExtendsTag return */ + reg::packed(1010, MAY_BE_NULL, 2654), /* getResolvedPhpDoc return */ + reg::packed(3543, 0, 1162), /* getTraitContextResolvedPhpDoc $implementingClass */ + reg::packed(1010, MAY_BE_NULL, 2654), /* getTraitContextResolvedPhpDoc return */ + reg::packed(1010, MAY_BE_NULL, 3591), /* getFirstExtendsTag return */ reg::packed(1010, MAY_BE_ARRAY), /* getExtendsTags return */ reg::packed(1010, MAY_BE_ARRAY), /* getImplementsTags return */ reg::packed(1010, MAY_BE_ARRAY), /* getTemplateTags return */ reg::packed(1010, MAY_BE_ARRAY), /* getAncestors return */ - reg::packed(3665, MAY_BE_ARRAY, reg::NoString, true, false), /* collectAncestors $ancestors */ + reg::packed(3702, MAY_BE_ARRAY, reg::NoString, true, false), /* collectAncestors $ancestors */ reg::packed(1010, MAY_BE_VOID), /* collectAncestors return */ reg::packed(2253, MAY_BE_STRING), /* getAncestorWithClassName $className */ reg::packed(1010, MAY_BE_NULL, 1162), /* getAncestorWithClassName return */ - reg::packed(3717, 0, 2117), /* isValidAncestorType $type */ - reg::packed(3722, MAY_BE_ARRAY), /* isValidAncestorType $ancestorClasses */ + reg::packed(3754, 0, 2117), /* isValidAncestorType $type */ + reg::packed(3759, MAY_BE_ARRAY), /* isValidAncestorType $ancestorClasses */ reg::packed(1010, MAY_BE_BOOL), /* isValidAncestorType return */ reg::packed(1010, MAY_BE_ARRAY), /* getMixinTags return */ reg::packed(1010, MAY_BE_ARRAY), /* getRequireExtendsTags return */ @@ -561,66 +565,67 @@ inline constexpr sigtab::Sig getParents = { { 2317 /* getParents */, 0, 91, 0, 9 inline constexpr sigtab::Sig getInterfaces = { { 2328 /* getInterfaces */, 0, 92, 0, 92, ZEND_ACC_PUBLIC } }; inline constexpr sigtab::Sig collectInterfaces = { { 2352 /* collectInterfaces */, 1, 93, 1, 94, ZEND_ACC_PRIVATE } }; inline constexpr sigtab::Sig getImmediateInterfaces = { { 2370 /* getImmediateInterfaces */, 0, 95, 0, 95, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getTraits = { { 2409 /* getTraits */, 0, 96, 1, 97, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getParentClassesNames = { { 2419 /* getParentClassesNames */, 0, 98, 0, 98, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasConstant = { { 2441 /* hasConstant */, 1, 99, 1, 100, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getConstant = { { 2496 /* getConstant */, 1, 101, 1, 102, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getConstantPhpDocType = { { 2508 /* getConstantPhpDocType */, 1, 103, 1, 104, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig findConstantResolvedPhpDoc = { { 2652 /* findConstantResolvedPhpDoc */, 1, 105, 1, 106, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig resolveConstantVarPhpDocType = { { 2720 /* resolveConstantVarPhpDocType */, 3, 107, 3, 110, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; -inline constexpr sigtab::Sig hasTraitUse = { { 2759 /* hasTraitUse */, 1, 111, 1, 112, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getTraitNames = { { 2771 /* getTraitNames */, 0, 113, 0, 113, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getTypeAliases = { { 2785 /* getTypeAliases */, 0, 114, 0, 114, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getDeprecatedDescription = { { 2800 /* getDeprecatedDescription */, 0, 115, 0, 115, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isDeprecated = { { 2825 /* isDeprecated */, 0, 116, 0, 116, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig resolveDeprecation = { { 2838 /* resolveDeprecation */, 0, 117, 0, 117, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig isBuiltin = { { 2857 /* isBuiltin */, 0, 118, 0, 118, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isInternal = { { 2867 /* isInternal */, 0, 119, 0, 119, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isFinal = { { 2878 /* isFinal */, 0, 120, 0, 120, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isImmutable = { { 2886 /* isImmutable */, 0, 121, 0, 121, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasConsistentConstructor = { { 2898 /* hasConsistentConstructor */, 0, 122, 0, 122, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig acceptsNamedArguments = { { 2923 /* acceptsNamedArguments */, 0, 123, 0, 123, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig hasFinalByKeywordOverride = { { 2945 /* hasFinalByKeywordOverride */, 0, 124, 0, 124, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isFinalByKeyword = { { 2971 /* isFinalByKeyword */, 0, 125, 0, 125, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isAttributeClass = { { 2988 /* isAttributeClass */, 0, 126, 0, 126, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig findAttributeFlags = { { 3005 /* findAttributeFlags */, 0, 127, 0, 127, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getAttributes = { { 3024 /* getAttributes */, 0, 128, 0, 128, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAttributeClassFlags = { { 3038 /* getAttributeClassFlags */, 0, 129, 0, 129, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getObjectType = { { 3085 /* getObjectType */, 0, 130, 0, 130, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getTemplateTypeMap = { { 3099 /* getTemplateTypeMap */, 0, 131, 0, 131, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getActiveTemplateTypeMap = { { 3118 /* getActiveTemplateTypeMap */, 0, 132, 0, 132, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getPossiblyIncompleteActiveTemplateTypeMap = { { 3143 /* getPossiblyIncompleteActiveTemplateTypeMap */, 0, 133, 0, 133, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getActiveTemplateTypeMapForAncestorResolution = { { 3186 /* getActiveTemplateTypeMapForAncestorResolution */, 0, 134, 0, 134, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getDefaultCallSiteVarianceMap = { { 3232 /* getDefaultCallSiteVarianceMap */, 0, 135, 0, 135, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getCallSiteVarianceMap = { { 3262 /* getCallSiteVarianceMap */, 0, 136, 0, 136, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isGeneric = { { 3285 /* isGeneric */, 0, 137, 0, 137, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig typeMapFromList = { { 3301 /* typeMapFromList */, 1, 138, 1, 139, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig varianceMapFromList = { { 3327 /* varianceMapFromList */, 1, 140, 1, 141, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig typeMapToList = { { 3355 /* typeMapToList */, 1, 142, 1, 143, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig varianceMapToList = { { 3381 /* varianceMapToList */, 1, 144, 1, 145, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig withTypes = { { 3399 /* withTypes */, 1, 146, 1, 147, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig withVariances = { { 3409 /* withVariances */, 1, 148, 1, 149, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig asFinal = { { 3423 /* asFinal */, 0, 150, 0, 150, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig withoutFinalByKeywordOverride = { { 3431 /* withoutFinalByKeywordOverride */, 0, 151, 0, 151, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig removeFinalKeywordOverride = { { 3461 /* removeFinalKeywordOverride */, 0, 152, 0, 152, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getResolvedPhpDoc = { { 3488 /* getResolvedPhpDoc */, 0, 153, 0, 153, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getTraitContextResolvedPhpDoc = { { 3524 /* getTraitContextResolvedPhpDoc */, 1, 154, 1, 155, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getFirstExtendsTag = { { 3584 /* getFirstExtendsTag */, 0, 156, 0, 156, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getExtendsTags = { { 3603 /* getExtendsTags */, 0, 157, 0, 157, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getImplementsTags = { { 3618 /* getImplementsTags */, 0, 158, 0, 158, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getTemplateTags = { { 3636 /* getTemplateTags */, 0, 159, 0, 159, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAncestors = { { 3652 /* getAncestors */, 0, 160, 0, 160, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig collectAncestors = { { 3675 /* collectAncestors */, 1, 161, 1, 162, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getAncestorWithClassName = { { 3692 /* getAncestorWithClassName */, 1, 163, 1, 164, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig isValidAncestorType = { { 3738 /* isValidAncestorType */, 2, 165, 2, 167, ZEND_ACC_PRIVATE } }; -inline constexpr sigtab::Sig getMixinTags = { { 3758 /* getMixinTags */, 0, 168, 0, 168, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getRequireExtendsTags = { { 3771 /* getRequireExtendsTags */, 0, 169, 0, 169, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getRequireImplementsTags = { { 3793 /* getRequireImplementsTags */, 0, 170, 0, 170, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getSealedTags = { { 3818 /* getSealedTags */, 0, 171, 0, 171, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getPropertyTags = { { 3832 /* getPropertyTags */, 0, 172, 0, 172, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getMethodTags = { { 3848 /* getMethodTags */, 0, 173, 0, 173, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getResolvedMixinTypes = { { 3862 /* getResolvedMixinTypes */, 0, 174, 0, 174, ZEND_ACC_PUBLIC } }; -inline constexpr sigtab::Sig getAllowedSubTypes = { { 3884 /* getAllowedSubTypes */, 0, 175, 0, 175, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getUnspecifiedAncestorTypes = { { 2402 /* getUnspecifiedAncestorTypes */, 1, 96, 1, 97, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getTraits = { { 2446 /* getTraits */, 0, 98, 1, 99, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getParentClassesNames = { { 2456 /* getParentClassesNames */, 0, 100, 0, 100, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasConstant = { { 2478 /* hasConstant */, 1, 101, 1, 102, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getConstant = { { 2533 /* getConstant */, 1, 103, 1, 104, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getConstantPhpDocType = { { 2545 /* getConstantPhpDocType */, 1, 105, 1, 106, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig findConstantResolvedPhpDoc = { { 2689 /* findConstantResolvedPhpDoc */, 1, 107, 1, 108, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig resolveConstantVarPhpDocType = { { 2757 /* resolveConstantVarPhpDocType */, 3, 109, 3, 112, ZEND_ACC_PRIVATE | ZEND_ACC_STATIC } }; +inline constexpr sigtab::Sig hasTraitUse = { { 2796 /* hasTraitUse */, 1, 113, 1, 114, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getTraitNames = { { 2808 /* getTraitNames */, 0, 115, 0, 115, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getTypeAliases = { { 2822 /* getTypeAliases */, 0, 116, 0, 116, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getDeprecatedDescription = { { 2837 /* getDeprecatedDescription */, 0, 117, 0, 117, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isDeprecated = { { 2862 /* isDeprecated */, 0, 118, 0, 118, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig resolveDeprecation = { { 2875 /* resolveDeprecation */, 0, 119, 0, 119, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig isBuiltin = { { 2894 /* isBuiltin */, 0, 120, 0, 120, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isInternal = { { 2904 /* isInternal */, 0, 121, 0, 121, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isFinal = { { 2915 /* isFinal */, 0, 122, 0, 122, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isImmutable = { { 2923 /* isImmutable */, 0, 123, 0, 123, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasConsistentConstructor = { { 2935 /* hasConsistentConstructor */, 0, 124, 0, 124, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig acceptsNamedArguments = { { 2960 /* acceptsNamedArguments */, 0, 125, 0, 125, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig hasFinalByKeywordOverride = { { 2982 /* hasFinalByKeywordOverride */, 0, 126, 0, 126, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isFinalByKeyword = { { 3008 /* isFinalByKeyword */, 0, 127, 0, 127, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isAttributeClass = { { 3025 /* isAttributeClass */, 0, 128, 0, 128, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig findAttributeFlags = { { 3042 /* findAttributeFlags */, 0, 129, 0, 129, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getAttributes = { { 3061 /* getAttributes */, 0, 130, 0, 130, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAttributeClassFlags = { { 3075 /* getAttributeClassFlags */, 0, 131, 0, 131, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getObjectType = { { 3122 /* getObjectType */, 0, 132, 0, 132, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getTemplateTypeMap = { { 3136 /* getTemplateTypeMap */, 0, 133, 0, 133, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getActiveTemplateTypeMap = { { 3155 /* getActiveTemplateTypeMap */, 0, 134, 0, 134, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPossiblyIncompleteActiveTemplateTypeMap = { { 3180 /* getPossiblyIncompleteActiveTemplateTypeMap */, 0, 135, 0, 135, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getActiveTemplateTypeMapForAncestorResolution = { { 3223 /* getActiveTemplateTypeMapForAncestorResolution */, 0, 136, 0, 136, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getDefaultCallSiteVarianceMap = { { 3269 /* getDefaultCallSiteVarianceMap */, 0, 137, 0, 137, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getCallSiteVarianceMap = { { 3299 /* getCallSiteVarianceMap */, 0, 138, 0, 138, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isGeneric = { { 3322 /* isGeneric */, 0, 139, 0, 139, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig typeMapFromList = { { 3338 /* typeMapFromList */, 1, 140, 1, 141, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig varianceMapFromList = { { 3364 /* varianceMapFromList */, 1, 142, 1, 143, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig typeMapToList = { { 3392 /* typeMapToList */, 1, 144, 1, 145, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig varianceMapToList = { { 3418 /* varianceMapToList */, 1, 146, 1, 147, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig withTypes = { { 3436 /* withTypes */, 1, 148, 1, 149, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig withVariances = { { 3446 /* withVariances */, 1, 150, 1, 151, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig asFinal = { { 3460 /* asFinal */, 0, 152, 0, 152, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig withoutFinalByKeywordOverride = { { 3468 /* withoutFinalByKeywordOverride */, 0, 153, 0, 153, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig removeFinalKeywordOverride = { { 3498 /* removeFinalKeywordOverride */, 0, 154, 0, 154, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getResolvedPhpDoc = { { 3525 /* getResolvedPhpDoc */, 0, 155, 0, 155, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getTraitContextResolvedPhpDoc = { { 3561 /* getTraitContextResolvedPhpDoc */, 1, 156, 1, 157, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getFirstExtendsTag = { { 3621 /* getFirstExtendsTag */, 0, 158, 0, 158, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getExtendsTags = { { 3640 /* getExtendsTags */, 0, 159, 0, 159, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getImplementsTags = { { 3655 /* getImplementsTags */, 0, 160, 0, 160, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getTemplateTags = { { 3673 /* getTemplateTags */, 0, 161, 0, 161, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAncestors = { { 3689 /* getAncestors */, 0, 162, 0, 162, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig collectAncestors = { { 3712 /* collectAncestors */, 1, 163, 1, 164, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getAncestorWithClassName = { { 3729 /* getAncestorWithClassName */, 1, 165, 1, 166, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig isValidAncestorType = { { 3775 /* isValidAncestorType */, 2, 167, 2, 169, ZEND_ACC_PRIVATE } }; +inline constexpr sigtab::Sig getMixinTags = { { 3795 /* getMixinTags */, 0, 170, 0, 170, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getRequireExtendsTags = { { 3808 /* getRequireExtendsTags */, 0, 171, 0, 171, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getRequireImplementsTags = { { 3830 /* getRequireImplementsTags */, 0, 172, 0, 172, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getSealedTags = { { 3855 /* getSealedTags */, 0, 173, 0, 173, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getPropertyTags = { { 3869 /* getPropertyTags */, 0, 174, 0, 174, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getMethodTags = { { 3885 /* getMethodTags */, 0, 175, 0, 175, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getResolvedMixinTypes = { { 3899 /* getResolvedMixinTypes */, 0, 176, 0, 176, ZEND_ACC_PUBLIC } }; +inline constexpr sigtab::Sig getAllowedSubTypes = { { 3921 /* getAllowedSubTypes */, 0, 177, 0, 177, ZEND_ACC_PUBLIC } }; } // namespace sig } // namespace ptdecl::ClassReflection diff --git a/turbo-ext/tests/php-class-reflection-family.php b/turbo-ext/tests/php-class-reflection-family.php index 24407ff3f54..fdcc65a596f 100644 --- a/turbo-ext/tests/php-class-reflection-family.php +++ b/turbo-ext/tests/php-class-reflection-family.php @@ -510,9 +510,11 @@ public function normalizeClass(string $class): string $pcreDifference = $pcreFirstDifference($pcreExpected, $pcreBackedEnumFromOffObservations['native'][$pcreKey] ?? '(missing)'); check($pcreDifference === null, 'PhpClassReflectionExtension backedEnumFromParameterType off ' . $pcreKey . ' ' . ($pcreDifference ?? '')); } +[$pcrePhpSide] = $pcreBuildSides(true, 4096, null, false); +$pcreOffFromParameterType = $pcrePhpSide->getMethod($pcreReflectionProvider->getClass('PhpClassReflectionFamilyFixture\FixtureBackedEnum'), 'from')->getOnlyVariant()->getParameters()[0]->getType()->describe(\PHPStan\Type\VerbosityLevel::precise()); check( - $pcreBackedEnumFromOffObservations['php']['getMethod from']['class'] !== $pcreObservations['php']['PhpClassReflectionFamilyFixture\FixtureBackedEnum::from']['getMethod']['class'], - 'PhpClassReflectionExtension backedEnumFromParameterType off: from() is not wrapped', + $pcreOffFromParameterType === 'int|string', + 'PhpClassReflectionExtension backedEnumFromParameterType off: from() takes int|string (' . $pcreOffFromParameterType . ')', ); // }}}