stringable-object is a Psalm-only object refinementstringable-object matches any object exposing __toString(). Psalm models
it structurally; other analyzers do not recognize the keyword and fall back
to accepting any object (or report an unknown type).
References:
- Psalm stringable-object maps to an object-with-__toString shape| 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 | Unrecognized | phpdoc_advanced_psalm_stringable_object.php:31: Saw possibly unextractable annotation for a fragment of comment '* @param stringable-object $obj': after stringable, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName] phpdoc_advanced_psalm_stringable_object.php:31: Saw a token Phan may have failed to parse after '* @param stringable-object $obj': after stringable, saw '-' [PhanUnextractableAnnotationSuffix] phpdoc_advanced_psalm_stringable_object.php:33: Parameter $obj has undeclared type \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable (Did you mean interface \Stringable) [PhanUndeclaredTypeParameter] phpdoc_advanced_psalm_stringable_object.php:38: Argument 1 ($obj) is new WithToString() of type \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithToString|\Stringable but \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable() takes \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_psalm_stringable_object.php:33 [PhanTypeMismatchArgument] phpdoc_advanced_psalm_stringable_object.php:41: Argument 1 ($obj) is new WithoutToString() of type \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithoutToString but \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable() takes \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_psalm_stringable_object.php:33 [PhanTypeMismatchArgument]
Expectation diffLine 38: Expected valid value to be accepted, got ["Argument 1 ($obj) is new WithToString() of type \\Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\WithToString|\\Stringable but \\Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\acceptsStringable() takes \\Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\stringable defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_psalm_stringable_object.php:33 [PhanTypeMismatchArgument]"] |
| phpstan | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Unrecognized | phpdoc_advanced_psalm_stringable_object.php:33: Function Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable() has parameter $obj with no type specified. [identifier=missingType.parameter] [reported-from-level=6] phpdoc_advanced_psalm_stringable_object.php:33: PHPDoc tag @param for parameter $obj contains unresolvable type. [identifier=parameter.unresolvableType] [reported-from-level=2] With strict-rulesphpdoc_advanced_psalm_stringable_object.php:33: Function Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable() has parameter $obj with no type specified. [identifier=missingType.parameter] phpdoc_advanced_psalm_stringable_object.php:33: PHPDoc tag @param for parameter $obj contains unresolvable type. [identifier=parameter.unresolvableType]
|
| psalm | 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac next: 7.0.0-beta19 |
Enforced | phpdoc_advanced_psalm_stringable_object.php:41: Argument 1 of Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable expects object{__tostring()}, but Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithoutToString provided [InvalidArgument]
|
| mago | 1.46.0mago 1.46.0 | Unrecognized | phpdoc_advanced_psalm_stringable_object.php:31: Could not resolve the type for the @param tag. [invalid-param-tag]
|
| mir | 0.70.1mir 0.70.1 | Not enforced | No diagnostics reported.
|
| phpantom | 0.9.0phpantom_lsp 0.9.0 | Incidental (1/1) | phpdoc_advanced_psalm_stringable_object.php:38: Argument 1 ($obj) expects Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable-object, got Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithToString [type_mismatch_argument] phpdoc_advanced_psalm_stringable_object.php:41: Argument 1 ($obj) expects Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable-object, got Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithoutToString [type_mismatch_argument]
Expectation diffLine 38: Expected valid value to be accepted, got ["Argument 1 ($obj) expects Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\stringable-object, got Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\WithToString [type_mismatch_argument]"] |
| intelephense | 1.18.5intelephense 1.18.5 | Not enforced | No diagnostics reported.
|
| phpy | 0.2.0phpy 0.2.0 | Unrecognized | phpdoc_advanced_psalm_stringable_object.php:31: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable-object' phpdoc_advanced_psalm_stringable_object.php:38: Argument '1' passed to Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable() is expected to be of type Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable-object, Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithToString given phpdoc_advanced_psalm_stringable_object.php:41: Argument '1' passed to Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable() is expected to be of type Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable-object, Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithoutToString given
Expectation diffLine 38: Expected valid value to be accepted, got ["Argument '1' passed to Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\acceptsStringable() is expected to be of type Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\stringable-object, Conformance\\Tests\\PhpdocAdvancedPsalmStringableObject\\WithToString given"] |
| qodana | 262.8665.325Qodana 262.8665.325 | Unrecognized | phpdoc_advanced_psalm_stringable_object.php:31: Undefined class 'stringable-object' [PhpUndefinedClassInspection] phpdoc_advanced_psalm_stringable_object.php:38: Parameter 'new WithToString()' type is not compatible with declaration [PhpParamsInspection] phpdoc_advanced_psalm_stringable_object.php:41: Parameter 'new WithoutToString()' type is not compatible with declaration [PhpParamsInspection]
Expectation diffLine 38: Expected valid value to be accepted, got ["Parameter 'new WithToString()' 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_psalm_stringable_object.php:31: Class or interface named \Conformance\Tests\PhpdocAdvancedPsalmStringableObject\stringable-object does not exist [undefinedClass]
|
| steins | 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) | Widened to a nonexistent-class reference | No diagnostics reported.
Notes: `stringable-object` has no `lower_identifier` entry, so it lowers through the catch-all to `ContractTy::Class("stringable-object")` — Steins has no `__toString`-presence fact to check against. |
| phpstan-strict | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Unrecognized | phpdoc_advanced_psalm_stringable_object.php:33: Function Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable() has parameter $obj with no type specified. [identifier=missingType.parameter] phpdoc_advanced_psalm_stringable_object.php:33: PHPDoc tag @param for parameter $obj contains unresolvable type. [identifier=parameter.unresolvableType]
|
| psalm-next | 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 | Enforced | phpdoc_advanced_psalm_stringable_object.php:41: Argument 1 of Conformance\Tests\PhpdocAdvancedPsalmStringableObject\acceptsStringable expects object{__tostring()}, but Conformance\Tests\PhpdocAdvancedPsalmStringableObject\WithoutToString provided [InvalidArgument]
|
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedPsalmStringableObject;
/**
* `stringable-object` is a Psalm-only object refinement.
*
* `stringable-object` matches any object exposing `__toString()`. Psalm models
* it structurally; other analyzers do not recognize the keyword and fall back
* to accepting any object (or report an unknown type).
*
* References:
* - Psalm `stringable-object` maps to an object-with-__toString shape
*/
final class WithToString
{
public function __toString(): string
{
return 'x';
}
}
final class WithoutToString
{
}
/**
* @param stringable-object $obj
*/
function acceptsStringable($obj): void // T: stringable-object
{
}
// An object with __toString satisfies the parameter.
acceptsStringable(new WithToString()); // V
// An object without __toString is rejected by analyzers that model it.
acceptsStringable(new WithoutToString()); // E?: object without __toString is not a stringable-object