Skip to content

Fix #13596: False positive: first class callable protected by method_exists still reports callable.nonNativeMethod error.#4935

Open
phpstan-bot wants to merge 1 commit into2.1.xfrom
create-pull-request/patch-saorwku
Open

Fix #13596: False positive: first class callable protected by method_exists still reports callable.nonNativeMethod error.#4935
phpstan-bot wants to merge 1 commit into2.1.xfrom
create-pull-request/patch-saorwku

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When creating a first-class callable guarded by method_exists() or is_callable(), PHPStan incorrectly reported callable.nonNativeMethod even though the method's existence was verified at runtime. This fix prevents the false positive by detecting when the method reflection was resolved through HasMethodType (the accessory type added by method_exists() narrowing).

Changes

  • src/Rules/Methods/MethodCallableRule.php: Added check to skip callable.nonNativeMethod error when the method's declaring class is stdClass and doesn't actually have the method — indicating a DummyMethodReflection from HasMethodType resolution
  • src/Rules/Methods/StaticMethodCallableRule.php: Applied the same fix for static method callables
  • tests/PHPStan/Rules/Methods/MethodCallableRuleTest.php: Added testBug13596 test method
  • tests/PHPStan/Rules/Methods/data/bug-13596.php: Regression test covering method_exists() and is_callable() guards with first-class callable syntax

Root cause

When method_exists($this, 'myCallable') narrows the type, HasMethodType('myCallable') is added as an accessory type. HasMethodType::getMethod() returns a DummyMethodReflection whose declaring class is stdClass. The MethodCallableRule then checks $declaringClass->hasNativeMethod('myCallable') on stdClass, which returns false, triggering the callable.nonNativeMethod error. The fix detects this dummy case by checking if the declaring class is stdClass without the method, and skips the error since the method's existence was runtime-verified.

Test

Added regression test tests/PHPStan/Rules/Methods/data/bug-13596.php with a base class that uses method_exists() and is_callable() guards before creating first-class callables. The test expects no errors.

Fixes phpstan/phpstan#13596

…s guard

- Skip callable.nonNativeMethod error when method was resolved through
  HasMethodType (from method_exists/is_callable guard)
- Detect this by checking if declaring class is stdClass with no such
  method, which indicates a DummyMethodReflection from HasMethodType
- Applied same fix to both MethodCallableRule and StaticMethodCallableRule
- Added regression test in tests/PHPStan/Rules/Methods/data/bug-13596.php
return $errors;
}

if ($declaringClass->getName() === stdClass::class && !$declaringClass->hasMethod($methodNameName)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is isDummy() method on DummyPropertyReflection/ExtendedPropertyReflection

I feel like it's better to have a isDummy() method on DummyMethodReflection/ExtendedMethodReflection too rather than explicitly checking stdClass.

WDYT ?

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.

3 participants