perf(reflection): mint instances via native reflection and drop redundant casts on hot paths - #196
Merged
lisachenko merged 2 commits intoAug 13, 2026
Conversation
…dant casts Three hot-path reductions in the reflection layer, no behavior change. Instance minting: inside namespace ZEngine\Reflection the unqualified name ReflectionClass resolves to z-engine's own class, whose constructor runs the native parent constructor, a class-table find() and initLowLevelStructures() (four HashTable wrappers) - all discarded when the instance only serves newInstanceWithoutConstructor(). The ten such sites in ReflectionMethod, ReflectionProperty, ReflectionClassConstant and ReflectionFunction now use the aliased native reflector, matching ReflectionClass::fromCData() and the Type\* wrappers. ReflectionMethod::fromHookCData() keeps the z-engine class for the publication board, which genuinely needs getMethodTable(). Class-table walk: FunctionBodySwap::countPublishedShares() built a full ReflectionClass per engine class only to read one type byte and look up one method bucket. Both answers now come off the entry through the owning class via new @internal ReflectionClass::entryIsUserDefined() and ReflectionClass::entryMethodTable() helpers, so no call site pokes the raw zend_class_entry itself. Counting semantics (alias dedup, user-class filter, address match, floor of 1) are unchanged. Redundant casts: four sites passed an already-typed zend_class_entry * through Core::cast('zend_class_entry *', ...), paying a count() probe, a caught FFI exception and a type lookup per call. Each value is a pointer field (zend_object.ce, zend_function_common.scope), never an array needing decay, so the cast is dropped as in ReflectionProperty::getDeclaringClass(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnoZ7wuGepCTzsmFQ5sKxG
Owner
Author
|
Drop B. it is against the idea of framework |
countPublishedShares() reflects each class again: the framework's model is that consumers hold Reflection objects, and entry-level static utilities on ReflectionClass cut against that. The redundant zend_class_entry* cast removal in applyStaticVariableDefaults() stays. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnoZ7wuGepCTzsmFQ5sKxG
Owner
Author
|
Done — Part B is reverted in 8100b30: the phpstan (level max) and php-cs-fixer re-verified clean after the revert. Generated by Claude Code |
lisachenko
marked this pull request as ready for review
August 13, 2026 19:36
lisachenko
deleted the
claude/php-repo-modernization-6pfu7y-reflection-fastpath
branch
August 13, 2026 19:41
lisachenko
pushed a commit
that referenced
this pull request
Aug 13, 2026
Re-bases the CI signal on the true merged state: 8.4 gained the exception-factory classes (#201) that also touch OpCodeHook's install-time guards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnoZ7wuGepCTzsmFQ5sKxG
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.
Two independent, behavior-preserving reductions on reflection hot paths. No public API changes.
A. Stop building full z-engine
ReflectionClassinstances just to bypass constructorsInside
namespace ZEngine\Reflectionthe unqualified nameReflectionClassresolves to z-engine's own class, whose constructor runs the nativeparent::__construct(), a class-tablefind()andinitLowLevelStructures()(fourHashTablewrappers) — every bit of it discarded when the instance exists only to servenewInstanceWithoutConstructor().All ten such sites now use
ReflectionClass as NativeReflectionClass, the alias already used byReflectionClass::fromCData()(src/Reflection/ReflectionClass.php:183) and thesrc/Type/*wrappers:src/Reflection/ReflectionMethod.php—fromCData(),fromHookCData(),fromRawEntry(),fromClosureEntry()src/Reflection/ReflectionProperty.php—fromCData(),fromRawEntry()src/Reflection/ReflectionClassConstant.php—fromCData(),fromRawEntry()src/Reflection/ReflectionFunction.php—fromCData(),fromClosureEntry()Deliberately left unchanged:
src/Reflection/ReflectionMethod.php:152,(new ReflectionClass($boardName))->getMethodTable()— that one genuinely needs the z-engine class, sincegetMethodTable()only exists onceinitLowLevelStructures()has run. Every other site was checked the same way.B. Remove redundant
zend_class_entry *castsEach of these passed an already-typed
zend_class_entry *throughCore::cast('zend_class_entry *', …), which costs acount()array probe, a caughtFFI\Exception, and a type-name lookup per call. The producing expression was checked at every site — all four are pointer struct fields (zend_object.ce,zend_function_common.scope), never a C array needing decay — so the cast is dropped, matching the existing direct form inReflectionProperty::getDeclaringClass()andReflectionClassConstant::getDeclaringClass():src/Type/ObjectEntry.php:131—$this->pointer->ce,zend_object.ceis?zend_class_entryin the stubssrc/Reflection/ReflectionMethod.php:332—$this->getCommonPointer()->scopesrc/Reflection/FunctionLikeTrait.php:284—$entryScope, fed from$this->getCommonPointer()->scopeat line 198src/Reflection/FunctionBodySwap.php:406—$entryFunction->getCommonPointer()->scopeNo site had to be left in place for array decay.
Merge note
ReflectionMethod::fromHookCData()is also touched by the concurrently-open exception-safety PR #197. The hunks are different and roughly ten lines apart, so a textual proximity conflict is possible but the resolution is trivial in either merge order — keep both edits.Validation
vendor/bin/phpstan analyse(level max) — clean, no baseline changes needed (verified via a temporaryreportUnmatchedIgnoredErrors: truerun on both trees).PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix— 0 of 296 files changed;--dry-runclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01RnoZ7wuGepCTzsmFQ5sKxG