[PHP 8.6] Override on class constants, __debugInfo on enums, attribute API audit - #232
Merged
Conversation
…() on enums PHP 8.6 allows the #[\Override] attribute on class, interface and enum constants and the __debugInfo() magic method on enums. Both constructs are parsed by PHP-Parser on any runtime, so static reflection works everywhere, while their stub files may only be included by a PHP 8.6+ runtime (they are compile errors before that). - add tests/Stub/FileWithOverrideConstants86.php and tests/Stub/FileWithDebuggableEnums86.php, resolved through a dedicated CallableLocator instead of the parity data providers, so that they are never included on PHP 8.5 - add tests/Php86MiscParityTest.php with static assertions for every runtime and native reflection parity assertions guarded by PHP_VERSION_ID >= 80600 - implement ReflectionAttribute::getShortName(), getNamespaceName() and inNamespace(): they were added to the native reflection in 8.6, but their internal implementation works on the attribute structure that is never initialized for the parsed reflection and fatals otherwise - mirror the new \ReflectionConstant::inNamespace() method in the parsed ReflectionConstant - keep named attribute arguments keyed by their names in getArguments(), like native reflection does, instead of collapsing them into a positional list - print the doc comment of a class constant in ReflectionClassConstant::__toString(), which also makes four previously skipped __toString parity cases pass Audit of the 8.6 attribute API: ReflectionAttribute::getCurrent() does not exist in 8.6.0beta1, so there is nothing to emulate for it yet; the new test marks itself incomplete once the method appears in a runtime. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #225
What is done
1.
#[\Override]on class constantsNew stub
tests/Stub/FileWithOverrideConstants86.phpcovers the attribute on a class constant inherited from an abstract parent, on an interface constant re-declared by an extending interface, on an interface constant implemented by a class, and on enum constants — alone, combined with a userland attribute inside the same attribute group, and combined with a repeated userland attribute in separate groups.AttributeResolverTraitalready resolved constant attributes, so no engine change was needed for the resolution itself; the newtests/Php86MiscParityTest.phpasserts names, arguments,isRepeated(), filtering by name and the AST node of every attribute, plus full parity with native reflection on 8.6.2.
__debugInfo()on enumsNew stub
tests/Stub/FileWithDebuggableEnums86.phpdeclares a pure enum and a backed enum with__debugInfo()(plus one enum without it as a negative case). The parsedReflectionClass/ReflectionEnumreport it as an ordinary method (hasMethod(),getMethods(), visibility, return type, declaring class) and match native reflection method-for-method on 8.6.3. Attribute API additions (audit + implementation)
Diffing
get_class_methods()/class constants of everyReflection*class between the localphp(8.5.9) andphp8.6(8.6.0beta1) binaries gives the complete list of 8.6 reflection additions:ReflectionAttributegetShortName(),getNamespaceName(),inNamespace()src/ReflectionAttribute.phpReflectionConstantinNamespace()src/ReflectionConstant.phpReflectionParametergetDocComment()ReflectionPropertyisReadable(),isWritable()No new
Reflection*classes and no new class constants appear in the beta.The three
ReflectionAttributemethods cannot simply be inherited: the internal implementations read the attribute structure that is never initialized for a parsed attribute, so on 8.6 they fatal today:They are now resolved from the attribute name itself (through
getName(), so the anonymous subclass used byReflectionConstantkeeps working) and are asserted equal to the native results on 8.6.ReflectionAttribute::getCurrent()does not exist in 8.6.0beta1 —php8.6 -r 'var_dump(method_exists(ReflectionAttribute::class, "getCurrent"));'printsbool(false)— so nothing is implemented for it.testAttributeApiAdditionsAreEitherImplementedOrAbsent()marks itself incomplete as soon as a runtime ships the method, and otherwise asserts that every nativeReflectionAttributemethod is implemented by the parsed one.Two more verified behaviours worth recording:
#[\Override]on a constant is validated at link time, exactly like on methods:Foo::BAR has #[\Override] attribute, but no matching parent constant exists— the stub therefore always declares a real parent/interface constant.\Overrideattribute itself now declares the targets28(TARGET_METHOD | TARGET_PROPERTY | TARGET_CLASS_CONSTANT) instead of12on 8.5, where loading such a constant fails withAttribute "Override" cannot target class constant.Parity fixes required by the above
AttributeResolverTrait,ReflectionAttribute::getNode()andReflectionConstant::getAttributes(); native reflection keys them by name (['tag' => 'weight', 'priority' => 5]). They are now keyed the same way, which is what the new 8.6 parity assertions compare against. Two expectations inReflectionConstantTestthat documented the old shape were updated.ReflectionClassConstant::__toString()did not print the doc comment of the constant, while native reflection does. It does now — this also turns four previously skipped__toStringparity data sets green (ClassWithPhp56ArrayConstants::A/B,ClassWithPhp56ComplexConstantsAndInheritance::K/L... skipped count drops from 133 to 129 on 8.6).Stub loading convention
Both stubs parse on any runtime but only compile on 8.6, so they are resolved through a
CallableLocatorin the new test (likeFileWithPropertyHooks84.php) and are never yielded byAbstractTestCase::getFilesToAnalyze(), whose providersinclude_onceevery stub. Every assertion that needs the stub loaded is guarded byPHP_VERSION_ID >= 80600.Local results
vendor/bin/phpuniton PHP 8.5.9 —OK, but there were issues!Tests: 13752, Assertions: 15474, Skipped: 131, Incomplete: 2(no failures; baseline on master: 13743 tests, 133 skipped, 2 incomplete)php8.6 vendor/bin/phpuniton PHP 8.6.0beta1 —OK, but there were issues!Tests: 13752, Assertions: 15582, Skipped: 129, Incomplete: 3(no failures; the three incompletes are the pre-existing Inherited methods should contain inherits section for the method __toString #55, [PHP8.6] ReflectionParameter::getDocComment() — parameter doc comments #221 and [PHP8.6] ReflectionProperty::isReadable() / isWriteable() emulation #222 ones)vendor/bin/phpstan analyse src --no-progress—[OK] No errors(level 10)🤖 Generated with Claude Code
https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
Generated by Claude Code