Skip to content

Do not attach the positional-arguments fix to the callback call synthesized from call_user_func() / call_user_func_array() - #6966

Open
phpstan-bot wants to merge 1 commit into
phpstan:2.3.xfrom
phpstan-bot:create-pull-request/patch-t43cm4k
Open

phpstan-bot wants to merge 1 commit into
phpstan:2.3.xfrom
phpstan-bot:create-pull-request/patch-t43cm4k

Conversation

@phpstan-bot

Copy link
Copy Markdown
Collaborator

Summary

Analysing \call_user_func_array([Foo::class, 'detect'], ['processTimeout' => 300]) with a @no-named-arguments callee crashed with Internal error: clone(): Argument #1 ($object) must be of type object, null given. The crash came from the fix that rewrites named arguments as positional ones. That fix was applied to the real call_user_func_array() call instead of the callback call PHPStan builds internally. With this change, no fix is attached to that internal call. The error is still reported.

Changes

  • src/Rules/FunctionCallParametersCheck.php: add the NOT_FIXABLE_CALL_ATTRIBUTE constant. createPositionalArgumentsFix() returns false for calls that carry it, the same way it already does for calls built from attributes.
  • src/Rules/Functions/CallUserFuncRule.php: set that attribute on the call built by ArgumentsNormalizer::reorderCallUserFuncArguments() / reorderCallUserFuncArrayArguments().
  • I checked the other callers of FunctionCallParametersCheck::check():
    • CallToFunctionParametersRule, CallMethodsRule, CallStaticMethodsRule, InstantiationRule and CallCallablesRule pass real nodes from the file, so they are fine.
    • AttributesCheck already opts out with isAttribute.
    • The positional-arguments fix is the only fix this check attaches.

Root cause

ArgumentsNormalizer builds the callback call with the attributes of the original call_user_func(_array)() node, including its file positions, so errors get the right line. ReplacingNodeVisitor matches the nodes to fix by class and position. So the fix closure was applied to the real call_user_func(_array)() node, which has different arguments:

  • For call_user_func_array(), the argument indexes the fix expected did not exist, so clone null crashed.
  • For call_user_func(), the callback argument is shifted out, so the fix would quietly produce a wrong call.

The argument lists of these internal calls do not match the source code, so they must not be fixed.

Test

CallUserFuncRuleTest::testBug15664 runs the fixer on tests/PHPStan/Rules/Functions/data/bug-15664.php. The file contains the calls from the issue (call_user_func_array with named keys in different orders), plus call_user_func with named arguments. The test asserts the file stays unchanged. Without the fix it crashes with __clone method called on non-object.

Fixes phpstan/phpstan#15664

…esized from `call_user_func()` / `call_user_func_array()`

- CallUserFuncRule checks a FuncCall built by ArgumentsNormalizer. That node keeps the
  start/end positions of the original call_user_func(_array)() call, but its arguments are
  different: the callback argument is dropped, or the arguments come from the array items.
- ReplacingNodeVisitor matches nodes by class and position, so the fix for a named argument
  under @no-named-arguments was applied to the real call_user_func(_array)() node. Its argument
  indexes did not match, which crashed with "clone(): Argument phpstan#1 ($object) must be of type
  object, null given" (or quietly rewrote call_user_func() into a broken call).
- CallUserFuncRule now marks the synthesized call with
  FunctionCallParametersCheck::NOT_FIXABLE_CALL_ATTRIBUTE, and createPositionalArgumentsFix()
  does not create a fix for such calls, like it already does for attribute calls. The error is
  still reported.
- Also checked the other FunctionCallParametersCheck::check() callers (CallToFunctionParametersRule,
  CallMethodsRule, CallStaticMethodsRule, InstantiationRule, CallCallablesRule): they pass real
  nodes from the file. AttributesCheck already opts out with `isAttribute`. The positional-arguments
  fix is the only fix this check attaches.
@staabm
staabm force-pushed the create-pull-request/patch-t43cm4k branch from a10acaf to fc3200a Compare November 6, 2026 15:19

@clxmstaab clxmstaab left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just testing the phpstan-bot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Internal error: clone(): Argument #1 ($object) must be of type object, null given while analysing file (...)

2 participants