CONSTANT[T]key-of<> bound has to admit the literal argument, and the offset has to be
taken per key rather than over the table as a whole.
The two values are deliberately of different native types, so the probes
need nothing more exotic than int and string to state. A tool that
resolves the offset only as far as the union of the table's values fails the
calls that should pass, the same way one that ignores the docblock and keeps
the native int|string does. Both readings surface as complaints about the
three calls meant to pass rather than as silence.
References:
- PHPStan phpdoc-types: offset access on constants
- phpdoc_advanced_fallback_key_of_constant for the bound on its own| 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_fallback_constant_offset_template_key.php:44: Doc-block of lookUp contains declared return type \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE which is incompatible with the return type int|string declared in the signature [PhanTypeMismatchDeclaredReturn]
phpdoc_advanced_fallback_constant_offset_template_key.php:44: Saw a token Phan may have failed to parse after '* @return ID_TABLE[T]': after ID_TABLE, saw '[' [PhanUnextractableAnnotationSuffix]
phpdoc_advanced_fallback_constant_offset_template_key.php:46: Template type T not used in return value of function/method lookUp() [PhanTemplateTypeNotUsedInFunctionReturn]
phpdoc_advanced_fallback_constant_offset_template_key.php:46: Return type of lookUp() is undeclared type \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE [PhanUndeclaredTypeReturnType]
phpdoc_advanced_fallback_constant_offset_template_key.php:48: When fetching an array index from a value of type array{immutable:1,mutable:'two'}, found an array index of type T, but expected the index to be of type string [PhanTypeMismatchDimFetch]
phpdoc_advanced_fallback_constant_offset_template_key.php:53: Template type T of \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() must be compatible with array-key, but 'immutable' was provided in call to \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() [PhanTemplateTypeConstraintViolation]
phpdoc_advanced_fallback_constant_offset_template_key.php:54: Template type T of \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() must be compatible with array-key, but 'mutable' was provided in call to \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() [PhanTemplateTypeConstraintViolation]
phpdoc_advanced_fallback_constant_offset_template_key.php:57: Template type T of \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() must be compatible with array-key, but 'immutable' was provided in call to \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() [PhanTemplateTypeConstraintViolation]
phpdoc_advanced_fallback_constant_offset_template_key.php:58: Template type T of \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() must be compatible with array-key, but 'mutable' was provided in call to \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp() [PhanTemplateTypeConstraintViolation]
Expectation diffLine 48: Expected valid value to be accepted, got ["When fetching an array index from a value of type array{immutable:1,mutable:'two'}, found an array index of type T, but expected the index to be of type string [PhanTypeMismatchDimFetch]"]
Line 53: Expected valid value to be accepted, got ["Template type T of \\Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\lookUp() must be compatible with array-key, but 'immutable' was provided in call to \\Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\lookUp() [PhanTemplateTypeConstraintViolation]"]
Line 54: Expected valid value to be accepted, got ["Template type T of \\Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\lookUp() must be compatible with array-key, but 'mutable' was provided in call to \\Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\lookUp() [PhanTemplateTypeConstraintViolation]"]Notes: Rejects every literal argument against the `key-of<ID_TABLE>` bound (`'immutable' ... must be compatible with array-key`), so the two probes are hit for a reason that has nothing to do with the offset. The `@return ID_TABLE[T]` annotation is also unparseable after `ID_TABLE`. |
| phpstan | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Enforced reported Lv.5+ | phpdoc_advanced_fallback_constant_offset_template_key.php:57: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString expects string, int given. [identifier=argument.type] [reported-from-level=5] phpdoc_advanced_fallback_constant_offset_template_key.php:58: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, string given. [identifier=argument.type] [reported-from-level=5] With strict-rulesphpdoc_advanced_fallback_constant_offset_template_key.php:57: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString expects string, int given. [identifier=argument.type] phpdoc_advanced_fallback_constant_offset_template_key.php:58: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, string given. [identifier=argument.type]
|
| psalm | 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac next: 7.0.0-beta19 |
Unrecognized (pzoom≠) | phpdoc_advanced_fallback_constant_offset_template_key.php:46: Template T has invalid as type - Untemplated key-of param Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE should be an array [InvalidDocblock] phpdoc_advanced_fallback_constant_offset_template_key.php:46: Unrecognised template param Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE in docblock for Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp [InvalidDocblock] phpdoc_advanced_fallback_constant_offset_template_key.php:52: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:53: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:54: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString expects string, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:57: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString expects string, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:58: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, but int|string provided [InvalidScalarArgument] pzoom (Psalm port)phpdoc_advanced_fallback_constant_offset_template_key.php:44: Docblock class Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE does not exist [UndefinedDocblockClass] phpdoc_advanced_fallback_constant_offset_template_key.php:44: Docblock class Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access does not exist [UndefinedDocblockClass] phpdoc_advanced_fallback_constant_offset_template_key.php:46: Docblock has incorrect return type 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access<Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE, array-key>', should be 'int|string' [MismatchingDocblockReturnType] phpdoc_advanced_fallback_constant_offset_template_key.php:48: The type 'two'|1 does not match the declared return type Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access<Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE, T:Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp as array-key> [InvalidReturnStatement] phpdoc_advanced_fallback_constant_offset_template_key.php:48: Coercion from array offset type 'T:Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp as array-key' to the expected type ''immutable'|'mutable'' [MixedArrayTypeCoercion] phpdoc_advanced_fallback_constant_offset_template_key.php:52: Argument 1 of takesInt expects int, but Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access<Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE, string> provided [InvalidArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:53: Argument 1 of takesInt expects int, but Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access<Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE, string> provided [InvalidArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:54: Argument 1 of takesString expects string, but Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access<Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE, string> provided [InvalidArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:57: Argument 1 of takesString expects string, but Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access<Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE, string> provided [InvalidArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:58: Argument 1 of takesInt expects int, but Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\pzoom_indexed_access<Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE, string> provided [InvalidArgument]
Expectation diffLine 52: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesInt expects int, but int|string provided [InvalidScalarArgument]"] Line 53: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesInt expects int, but int|string provided [InvalidScalarArgument]"] Line 54: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesString expects string, but int|string provided [InvalidScalarArgument]"] Notes: Falls back to the native `int|string`. The `key-of<>` bound over a global constant is rejected as an invalid template bound, so the offset never gets an operand and every call is reported as a coercion. |
| mago | 1.46.0mago 1.46.0 | Unrecognized | phpdoc_advanced_fallback_constant_offset_template_key.php:43: Cannot find class, interface, enum, or type alias `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE`. [non-existent-class-like]
phpdoc_advanced_fallback_constant_offset_template_key.php:44: Cannot find class, interface, enum, or type alias `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE`. [non-existent-class-like]
phpdoc_advanced_fallback_constant_offset_template_key.php:44: Cannot find class, interface, enum, or type alias `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE`. [non-existent-class-like]
phpdoc_advanced_fallback_constant_offset_template_key.php:46: Docblock return type `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)[('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)]` is incompatible with native return type `int|string`. [docblock-type-mismatch]
phpdoc_advanced_fallback_constant_offset_template_key.php:46: Docblock type `('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)` for parameter `$type` is incompatible with native type `string`. [docblock-type-mismatch]
phpdoc_advanced_fallback_constant_offset_template_key.php:48: Invalid index type `('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)` used for array access on `array{'immutable': int(1), 'mutable': string('two')}`. [invalid-array-index]
phpdoc_advanced_fallback_constant_offset_template_key.php:48: Invalid return type for function `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp`: expected `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)[('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)]`, but found `int(1)|string('two')`. [invalid-return-statement]
phpdoc_advanced_fallback_constant_offset_template_key.php:52: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt`: expected `int`, but found `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)[key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>]`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:53: Argument type mismatch for template `T`. [template-constraint-violation]
phpdoc_advanced_fallback_constant_offset_template_key.php:53: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp`: expected `('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)`, but found `string('immutable')`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:53: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt`: expected `int`, but found `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)[key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>]`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:54: Argument type mismatch for template `T`. [template-constraint-violation]
phpdoc_advanced_fallback_constant_offset_template_key.php:54: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp`: expected `('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)`, but found `string('mutable')`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:54: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString`: expected `string`, but found `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)[key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>]`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:57: Argument type mismatch for template `T`. [template-constraint-violation]
phpdoc_advanced_fallback_constant_offset_template_key.php:57: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp`: expected `('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)`, but found `string('immutable')`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:57: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString`: expected `string`, but found `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)[key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>]`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:58: Argument type mismatch for template `T`. [template-constraint-violation]
phpdoc_advanced_fallback_constant_offset_template_key.php:58: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp`: expected `('T.conformance\tests\phpdocadvancedfallbackconstantoffsettemplatekey\lookup() extends key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>)`, but found `string('mutable')`. [invalid-argument]
phpdoc_advanced_fallback_constant_offset_template_key.php:58: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt`: expected `int`, but found `unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)[key-of<unknown-ref(Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE)>]`. [invalid-argument]
Expectation diffLine 48: Expected valid value to be accepted, got ["Invalid index type `('T.conformance\\tests\\phpdocadvancedfallbackconstantoffsettemplatekey\\lookup() extends key-of<unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)>)` used for array access on `array{'immutable': int(1), 'mutable': string('two')}`. [invalid-array-index]","Invalid return type for function `Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\lookUp`: expected `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)[('T.conformance\\tests\\phpdocadvancedfallbackconstantoffsettemplatekey\\lookup() extends key-of<unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)>)]`, but found `int(1)|string('two')`. [invalid-return-statement]"]
Line 52: Expected valid value to be accepted, got ["Invalid argument type for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesInt`: expected `int`, but found `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)[key-of<unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)>]`. [invalid-argument]"]
Line 53: Expected valid value to be accepted, got ["Argument type mismatch for template `T`. [template-constraint-violation]","Invalid argument type for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\lookUp`: expected `('T.conformance\\tests\\phpdocadvancedfallbackconstantoffsettemplatekey\\lookup() extends key-of<unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)>)`, but found `string('immutable')`. [invalid-argument]","Invalid argument type for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesInt`: expected `int`, but found `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)[key-of<unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)>]`. [invalid-argument]"]
Line 54: Expected valid value to be accepted, got ["Argument type mismatch for template `T`. [template-constraint-violation]","Invalid argument type for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\lookUp`: expected `('T.conformance\\tests\\phpdocadvancedfallbackconstantoffsettemplatekey\\lookup() extends key-of<unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)>)`, but found `string('mutable')`. [invalid-argument]","Invalid argument type for argument #1 of `Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesString`: expected `string`, but found `unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)[key-of<unknown-ref(Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE)>]`. [invalid-argument]"]Notes: Same blocker as the `new<>` constant-map row: `ID_TABLE` is unresolvable in type position, so neither the bound nor the offset ever gets an operand. |
| mir | 0.70.1mir 0.70.1 | Incidental (2/2) | phpdoc_advanced_fallback_constant_offset_template_key.php:52: InvalidArgument: Argument $value of takesInt() expects 'int', got 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE[T]' [MIR0201] phpdoc_advanced_fallback_constant_offset_template_key.php:53: InvalidArgument: Argument $value of takesInt() expects 'int', got 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE[T]' [MIR0201] phpdoc_advanced_fallback_constant_offset_template_key.php:54: InvalidArgument: Argument $value of takesString() expects 'string', got 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE[T]' [MIR0201] phpdoc_advanced_fallback_constant_offset_template_key.php:57: InvalidArgument: Argument $value of takesString() expects 'string', got 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE[T]' [MIR0201] phpdoc_advanced_fallback_constant_offset_template_key.php:58: InvalidArgument: Argument $value of takesInt() expects 'int', got 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE[T]' [MIR0201]
Expectation diffLine 52: Expected valid value to be accepted, got ["InvalidArgument: Argument $value of takesInt() expects 'int', got 'Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE[T]' [MIR0201]"] Line 53: Expected valid value to be accepted, got ["InvalidArgument: Argument $value of takesInt() expects 'int', got 'Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE[T]' [MIR0201]"] Line 54: Expected valid value to be accepted, got ["InvalidArgument: Argument $value of takesString() expects 'string', got 'Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\ID_TABLE[T]' [MIR0201]"] Notes: Accepts the spelling and then carries `ID_TABLE[T]` around unevaluated, handing it to every call site: all five calls are rejected, the three correct ones included. |
| phpantom | 0.9.0phpantom_lsp 0.9.0 | Unrecognized | phpdoc_advanced_fallback_constant_offset_template_key.php:42: Class 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE' not found [unknown_class] phpdoc_advanced_fallback_constant_offset_template_key.php:44: Class 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE' not found [unknown_class]
|
| intelephense | 1.18.5intelephense 1.18.5 | Unrecognized | phpdoc_advanced_fallback_constant_offset_template_key.php:42: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE'. [P1133] phpdoc_advanced_fallback_constant_offset_template_key.php:44: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE'. [P1133] phpdoc_advanced_fallback_constant_offset_template_key.php:48: Expected type 'int|string'. Found 'mixed'. [P1006] phpdoc_advanced_fallback_constant_offset_template_key.php:57: Expected type 'string'. Found '1'. [P1006] phpdoc_advanced_fallback_constant_offset_template_key.php:58: Expected type 'int'. Found ''two''. [P1006]
Expectation diffLine 48: Expected valid value to be accepted, got ["Expected type 'int|string'. Found 'mixed'. [P1006]"] Notes: Resolves the offset per key at the call sites: `'immutable'` comes back as `1` and `'mutable'` as `'two'`, and the three correct calls pass untouched. What it reports is the type name -- `ID_TABLE` is an undefined PHPDoc type on the declaration -- and the value going to `mixed` inside the body. Apart from PHPStan the only analyzer here that answers the question itself correctly. |
| phpy | 0.2.0phpy 0.2.0 | Unrecognized | phpdoc_advanced_fallback_constant_offset_template_key.php:42: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE' phpdoc_advanced_fallback_constant_offset_template_key.php:44: Use of unknown class: 'Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE'
Notes: Reports `ID_TABLE` as an unknown class on the declaration and then checks no call site, so nothing is enforced in either direction. |
| qodana | 262.8665.325Qodana 262.8665.325 | Unrecognized | phpdoc_advanced_fallback_constant_offset_template_key.php:42: Undefined class 'ID_TABLE' [PhpUndefinedClassInspection] phpdoc_advanced_fallback_constant_offset_template_key.php:43: Argument type does not match the declared [PhpDocSignatureInspection] phpdoc_advanced_fallback_constant_offset_template_key.php:44: Return type does not match the declared [PhpDocSignatureInspection] phpdoc_advanced_fallback_constant_offset_template_key.php:44: Undefined class 'ID_TABLE' [PhpUndefinedClassInspection]
Notes: Reports `ID_TABLE` as an undefined class and the docblock as disagreeing with the signature, then checks no call site. Intelephense, the other IDE-side analyzer here, resolves the same offset per key. |
| 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_constant_offset_template_key.php:44: Class or interface named \Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE does not exist [undefinedClass] phpdoc_advanced_fallback_constant_offset_template_key.php:46: param $type miss matched with phpdoc type <<T>> [funcParamTypeMissMatch]
Notes: Reports `ID_TABLE` as an undefined class and the parameter as mismatching its docblock type, then checks no call site. The template parameter draws nothing under the name `T`, where the earlier `TName` was reported as an undefined class too, so NoVerify's docblock reader takes some template names for classes and not others. |
| steins | 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) | Not enforced | No diagnostics reported.
Notes: Silent everywhere, probes included. The native `int|string` alone would have made the three correct calls fail, so this is not a widening to it either. |
| phpstan-strict | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Enforced | phpdoc_advanced_fallback_constant_offset_template_key.php:57: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString expects string, int given. [identifier=argument.type] phpdoc_advanced_fallback_constant_offset_template_key.php:58: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, string given. [identifier=argument.type]
|
| psalm-next | 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 | Unrecognized | phpdoc_advanced_fallback_constant_offset_template_key.php:46: Template T has invalid as type - Untemplated key-of param Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE should be an array [InvalidDocblock] phpdoc_advanced_fallback_constant_offset_template_key.php:46: Unrecognised template param Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\ID_TABLE in docblock for Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\lookUp [InvalidDocblock] phpdoc_advanced_fallback_constant_offset_template_key.php:52: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:53: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:54: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString expects string, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:57: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesString expects string, but int|string provided [InvalidScalarArgument] phpdoc_advanced_fallback_constant_offset_template_key.php:58: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey\takesInt expects int, but int|string provided [InvalidScalarArgument]
Expectation diffLine 52: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesInt expects int, but int|string provided [InvalidScalarArgument]"] Line 53: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesInt expects int, but int|string provided [InvalidScalarArgument]"] Line 54: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedFallbackConstantOffsetTemplateKey\\takesString expects string, but int|string provided [InvalidScalarArgument]"] Notes: Falls back to the native `int|string`. The `key-of<>` bound over a global constant is rejected as an invalid template bound, so the offset never gets an operand and every call is reported as a coercion. |
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedFallbackConstantOffsetTemplateKey;
/**
* `CONSTANT[T]`
*
* An offset into a constant array where the key is a template parameter, so
* the return type is decided by the argument the caller passed. Resolving it
* takes three steps: the constant has to be reachable in type position, the
* `key-of<>` bound has to admit the literal argument, and the offset has to be
* taken per key rather than over the table as a whole.
*
* The two values are deliberately of different native types, so the probes
* need nothing more exotic than `int` and `string` to state. A tool that
* resolves the offset only as far as the union of the table's values fails the
* calls that should pass, the same way one that ignores the docblock and keeps
* the native `int|string` does. Both readings surface as complaints about the
* three calls meant to pass rather than as silence.
*
* References:
* - PHPStan phpdoc-types: offset access on constants
* - phpdoc_advanced_fallback_key_of_constant for the bound on its own
*/
const ID_TABLE = [
'immutable' => 1,
'mutable' => 'two',
];
function takesInt(int $value): void
{
}
function takesString(string $value): void
{
}
/**
* @template T of key-of<ID_TABLE>
* @param T $type
* @return ID_TABLE[T]
*/
function lookUp(string $type = 'immutable'): int|string // T: ID_TABLE[T]
{
return ID_TABLE[$type]; // V
}
// Each key resolves to its own value, the default argument included.
takesInt(lookUp()); // V
takesInt(lookUp('immutable')); // V
takesString(lookUp('mutable')); // V
// Reading the table as a whole instead of per key would let these through.
takesString(lookUp('immutable')); // E?: 'immutable' maps to int, not string
takesInt(lookUp('mutable')); // E?: 'mutable' maps to string, not int