callable-object__invoke(), which the engine will accept anywhere a callable
is expected. A Closure qualifies without declaring the method. Analyzers
that model the refinement reject an object without it; others fall back to
plain object and accept it.
References:
- PHPStan TypeNodeResolver callable-object resolves to object&CallableType| Analyzer | Version | Result | Diagnostics |
|---|---|---|---|
| phan | 6.0.7Phan 6.0.7 php-ast version 1.1.3 PHP version used to run Phan: 8.5.9 | Enforced | phpdoc_advanced_fallback_callable_object.php:57: Argument 1 ($value) is new NotInvokable() of type \Conformance\Tests\PhpdocAdvancedFallbackCallableObject\NotInvokable but \Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject() takes callable-object defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_callable_object.php:45 [PhanTypeMismatchArgument]
|
| phpstan | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Enforced reported Lv.5+ | phpdoc_advanced_fallback_callable_object.php:57: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject expects callable-object, Conformance\Tests\PhpdocAdvancedFallbackCallableObject\NotInvokable given. [identifier=argument.type] [reported-from-level=5] With strict-rulesphpdoc_advanced_fallback_callable_object.php:57: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject expects callable-object, Conformance\Tests\PhpdocAdvancedFallbackCallableObject\NotInvokable given. [identifier=argument.type]
|
| psalm | 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac next: 7.0.0-beta19 |
Not enforced | No diagnostics reported.
|
| mago | 1.46.0mago 1.46.0 | Unrecognized | phpdoc_advanced_fallback_callable_object.php:31: Cannot find class, interface, enum, or type alias `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object`. [non-existent-class-like] phpdoc_advanced_fallback_callable_object.php:35: Invalid return type for function `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\returnsCallableObject`: expected `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object)`, but found `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\Invokable`. [invalid-return-statement] phpdoc_advanced_fallback_callable_object.php:43: Cannot find class, interface, enum, or type alias `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object`. [non-existent-class-like] phpdoc_advanced_fallback_callable_object.php:50: Possible argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsObject`: expected `object`, but possibly received `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object)`. [possibly-invalid-argument] phpdoc_advanced_fallback_callable_object.php:53: Possible argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject`: expected `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object)`, but possibly received `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\Invokable`. [possibly-invalid-argument] phpdoc_advanced_fallback_callable_object.php:54: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject`: expected `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object)`, but found `(closure(): int(1))`. [invalid-argument] phpdoc_advanced_fallback_callable_object.php:57: Possible argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject`: expected `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object)`, but possibly received `Conformance\Tests\PhpdocAdvancedFallbackCallableObject\NotInvokable`. [possibly-invalid-argument]
Expectation diffLine 35: Expected valid value to be accepted, got ["Invalid return type for function `Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\returnsCallableObject`: expected `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object)`, but found `Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\Invokable`. [invalid-return-statement]"] Line 50: Expected valid value to be accepted, got ["Possible argument type mismatch for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\acceptsObject`: expected `object`, but possibly received `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object)`. [possibly-invalid-argument]"] Line 53: Expected valid value to be accepted, got ["Possible argument type mismatch for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\acceptsCallableObject`: expected `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object)`, but possibly received `Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\Invokable`. [possibly-invalid-argument]"] Line 54: Expected valid value to be accepted, got ["Invalid argument type for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\acceptsCallableObject`: expected `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object)`, but found `(closure(): int(1))`. [invalid-argument]"] |
| mir | 0.70.1mir 0.70.1 | Incidental (0/1) | phpdoc_advanced_fallback_callable_object.php:54: InvalidArgument: Argument $value of acceptsCallableObject() expects 'Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object', got 'Closure(): int' [MIR0201]
Expectation diffLine 54: Expected valid value to be accepted, got ["InvalidArgument: Argument $value of acceptsCallableObject() expects 'Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object', got 'Closure(): int' [MIR0201]"] |
| phpantom | 0.9.0phpantom_lsp 0.9.0 | Not enforced | No diagnostics reported.
|
| intelephense | 1.18.5intelephense 1.18.5 | Not enforced | No diagnostics reported.
|
| phpy | 0.2.0phpy 0.2.0 | Unrecognized | phpdoc_advanced_fallback_callable_object.php:31: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object' phpdoc_advanced_fallback_callable_object.php:35: Return value of Conformance\Tests\PhpdocAdvancedFallbackCallableObject\returnsCallableObject() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object, Conformance\Tests\PhpdocAdvancedFallbackCallableObject\Invokable returned phpdoc_advanced_fallback_callable_object.php:43: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object' phpdoc_advanced_fallback_callable_object.php:53: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object, Conformance\Tests\PhpdocAdvancedFallbackCallableObject\Invokable given phpdoc_advanced_fallback_callable_object.php:54: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object, callable given phpdoc_advanced_fallback_callable_object.php:57: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject() is expected to be of type Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object, Conformance\Tests\PhpdocAdvancedFallbackCallableObject\NotInvokable given
Expectation diffLine 35: Expected valid value to be accepted, got ["Return value of Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\returnsCallableObject() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object, Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\Invokable returned"] Line 53: Expected valid value to be accepted, got ["Argument '1' passed to Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\acceptsCallableObject() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object, Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\Invokable given"] Line 54: Expected valid value to be accepted, got ["Argument '1' passed to Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\acceptsCallableObject() is expected to be of type Conformance\\Tests\\PhpdocAdvancedFallbackCallableObject\\callable-object, callable given"] |
| qodana | 262.8665.325Qodana 262.8665.325 | Unrecognized | phpdoc_advanced_fallback_callable_object.php:31: Return type in PHPDoc does not match the actual return type [PhpReturnDocTypeMismatchInspection] phpdoc_advanced_fallback_callable_object.php:31: Undefined class 'callable-object' [PhpUndefinedClassInspection] phpdoc_advanced_fallback_callable_object.php:35: Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection] phpdoc_advanced_fallback_callable_object.php:43: Undefined class 'callable-object' [PhpUndefinedClassInspection] phpdoc_advanced_fallback_callable_object.php:53: Parameter 'new Invokable()' type is not compatible with declaration [PhpParamsInspection] phpdoc_advanced_fallback_callable_object.php:54: Parameter 'static fn (): int => 1' type is not compatible with declaration [PhpParamsInspection] phpdoc_advanced_fallback_callable_object.php:57: Parameter 'new NotInvokable()' type is not compatible with declaration [PhpParamsInspection]
Expectation diffLine 35: Expected valid value to be accepted, got ["Return value type is not compatible with declared [PhpIncompatibleReturnTypeInspection]"] Line 53: Expected valid value to be accepted, got ["Parameter 'new Invokable()' type is not compatible with declaration [PhpParamsInspection]"] Line 54: Expected valid value to be accepted, got ["Parameter 'static fn (): int => 1' type is not compatible with declaration [PhpParamsInspection]"] |
| noverify | 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 | Unrecognized | phpdoc_advanced_fallback_callable_object.php:31: Class or interface named \Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object does not exist [undefinedClass] phpdoc_advanced_fallback_callable_object.php:43: Class or interface named \Conformance\Tests\PhpdocAdvancedFallbackCallableObject\callable-object does not exist [undefinedClass]
|
| steins | 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) | Not enforced | No diagnostics reported.
|
| phpstan-strict | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Enforced | phpdoc_advanced_fallback_callable_object.php:57: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackCallableObject\acceptsCallableObject expects callable-object, Conformance\Tests\PhpdocAdvancedFallbackCallableObject\NotInvokable given. [identifier=argument.type]
|
| psalm-next | 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 | Not enforced | No diagnostics reported.
|
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedFallbackCallableObject;
/**
* `callable-object`
*
* An object with `__invoke()`, which the engine will accept anywhere a callable
* is expected. A `Closure` qualifies without declaring the method. Analyzers
* that model the refinement reject an object without it; others fall back to
* plain `object` and accept it.
*
* References:
* - PHPStan TypeNodeResolver `callable-object` resolves to object&CallableType
*/
final class Invokable
{
public function __invoke(): void
{
}
}
final class NotInvokable
{
}
/**
* @return callable-object
*/
function returnsCallableObject() // T: callable-object
{
return new Invokable(); // V
}
function acceptsObject(object $value): void
{
}
/**
* @param callable-object $value
*/
function acceptsCallableObject($value): void // T: callable-object
{
}
// A `callable-object` value always satisfies a native `object` parameter.
acceptsObject(returnsCallableObject()); // V
// An object with __invoke() satisfies the parameter, and so does a Closure.
acceptsCallableObject(new Invokable()); // V
acceptsCallableObject(static fn (): int => 1); // V
// An object without __invoke() does not.
acceptsCallableObject(new NotInvokable()); // E?: an object without __invoke() is not a callable-object