Repository navigation
fix(#25): treat a forward into a PHP internal function as a use - #27
Merged
Merged
Conversation
A method whose only touch of a parameter is handing it to a built-in (trim, str_replace, parse_url, array_map, …) was scored as a pass-through hop, with the chain terminating `external`. But the value cannot travel further inside user code once it enters a built-in: that method is the consumer. Counting it as a hop made every chain ending in a built-in one hop too long. CallResolver now returns a new InternalFunctionUse resolution when a function is absent from the index *and* PHP-internal (function_exists + ReflectionFunction::isInternal). A function that is absent but not internal (vendor code, an unseen user helper) still terminates as external, since the value may forward on there. ChainTraversal treats an InternalFunctionUse forward as a `used` terminal at the current method rather than a scored hop; an origin that only feeds its own parameter to a built-in is not tramp data and is dropped. Callback-taking built-ins (array_map, usort, call_user_func) are internal too, so the conservative "argument is consumed" choice falls out for free. Frozen-semantics flag: a forward into a PHP internal function is a use, one step stronger than the existing "external targets terminate" rule. Recorded by the internal-function-use fixture. The lone escaped diff-scoped mutant (removing the guard `continue`) is a verified equivalent mutant: the fall-through builds a discarded hop and calls follow() with a resolution it ignores, so output is byte-identical — confirmed by running the full suite with the mutation applied. MSI 95%.
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.
Closes #25
What & why
A method whose only touch of a parameter is handing it to a PHP built-in (
trim,str_replace,parse_url,array_map, …) was scored as a pass-through hop, with the chain terminatingexternal. But the value cannot travel any further inside user code once it enters a built-in — that method is the consumer. Counting it as a hop made every chain ending in a built-in one hop too long (see #25 for the measured impact).The fix, in two layers:
CallResolverreturns a newInternalFunctionUseresolution when a function is absent from the index and PHP-internal (function_exists+ReflectionFunction::isInternal). A function that is absent but not internal (vendor code, an unseen user helper) still terminates asexternal, since the value may forward on there.ChainTraversaltreats anInternalFunctionUseforward as ausedterminal at the current method rather than a scored hop. An origin that only feeds its own parameter to a built-in is not tramp data and is dropped (no 0-hop findings).Callback-taking built-ins (
array_map,usort,call_user_func) are internal too, so the conservative "the argument is consumed" choice from #25 falls out for free.Frozen-semantics flag: a forward into a PHP internal function is a use — one step stronger than the existing "external targets terminate" rule. Recorded by the
internal-function-usefixture.Real-world validation
On
simple-feed-reader/backend(the codebase #25 measured), with itsphptramp.dist.json(limit 4, warnLimit 3, minClasses 2):develop)Same result as
--exclude-terminal external(13) — but by shortening chains, not dropping them. Verified every remaining finding's origin exists in the baseline: zero new chains introduced; the fix only shortens or removes.Checklist
composer checkis green (cs + stan + md + test)composer infection:diffis green (MSI 95%; the one escaped diff-scoped mutant is a verified equivalent mutant — removing the guardcontinueleaves output byte-identical, confirmed by running the full suite with the mutation applied)tests/fixtures/(internal-function-use)develop(nevermain)