← All results

CONSTANT[T]

Group: Advanced PHPDoc types · Category: ecosystem · File: phpdoc_advanced_fallback_constant_offset_template_key.php

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

Analyzer results

AnalyzerVersionResultDiagnostics
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]
  • Recognition: spelling not resolved — reported on declaration line(s) 44, 46
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 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-rules
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]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
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]
  • Recognition: spelling not resolved — reported on declaration line(s) 46
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 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]
  • Recognition: spelling not resolved — reported on declaration line(s) 43, 44, 46
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 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]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported — incidental, since values the type admits were also rejected on line(s) 52, 53, 54
Expectation diff
Line 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]
  • Recognition: spelling not resolved — reported on declaration line(s) 42, 44
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved
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]
  • Recognition: spelling not resolved — reported on declaration line(s) 42, 44
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 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'
  • Recognition: spelling not resolved — reported on declaration line(s) 42, 44
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved

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]
  • Recognition: spelling not resolved — reported on declaration line(s) 42, 43, 44
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved

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]
  • Recognition: spelling not resolved — reported on declaration line(s) 44, 46
  • Enforcement: 0/2 of the expected violations reported — incidental, since the spelling was not resolved

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.

  • Recognition: spelling resolved
  • Enforcement: 0/2 of the expected violations 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]
  • Recognition: spelling resolved
  • Enforcement: 2/2 of the expected violations reported
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]
  • Recognition: spelling not resolved — reported on declaration line(s) 46
  • Enforcement: 2/2 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 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.

Source

<?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