array-keyarray-key is the union of the types PHP accepts as an array key,
int|string. Unlike the other pseudo-types in this group it refines no
single native type: both int and string satisfy it, while an array-key
value satisfies neither int nor string on its own. Analyzers that model
it reject a value PHP cannot use as a key; others fall back to mixed and
accept everything.
The reverse direction separates the analyzers further. PHPStan resolves the
keyword to a *benevolent* union, which is deliberately accepted wherever
either member is expected, so passing an array-key to a native string
parameter stays silent there. Analyzers that resolve it to an ordinary
int|string — or to a dedicated array-key atom — report the half of the
union that does not fit.
References:
- PHPStan TypeNodeResolver array-key resolves to BenevolentUnionType(int|string)
- Psalm docs, Type syntax / Scalar types: array-key| 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 | Partly enforced (3/5) | phpdoc_advanced_fallback_array_key.php:58: Argument 1 ($value) is 1.5 of type float (value: 1.5) but \Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey() takes int|string (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_array_key.php:48 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal] phpdoc_advanced_fallback_array_key.php:59: Argument 1 ($value) is true of type true but \Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey() takes int|string (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_array_key.php:48 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal] phpdoc_advanced_fallback_array_key.php:60: Argument 1 ($value) is null of type null but \Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey() takes int|string (no real type) defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_array_key.php:48 (the inferred real argument type has nothing in common with the parameter's phpdoc type) [PhanTypeMismatchArgumentProbablyReal]
|
| phpstan | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Partly enforced (3/5) reported Lv.5+ | phpdoc_advanced_fallback_array_key.php:58: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), float given. [identifier=argument.type] [reported-from-level=5] phpdoc_advanced_fallback_array_key.php:59: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), true given. [identifier=argument.type] [reported-from-level=5] phpdoc_advanced_fallback_array_key.php:60: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), null given. [identifier=argument.type] [reported-from-level=5] With strict-rulesphpdoc_advanced_fallback_array_key.php:58: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), float given. [identifier=argument.type] phpdoc_advanced_fallback_array_key.php:59: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), true given. [identifier=argument.type] phpdoc_advanced_fallback_array_key.php:60: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), null given. [identifier=argument.type]
|
| psalm | 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac next: 7.0.0-beta19 |
Enforced | phpdoc_advanced_fallback_array_key.php:58: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects array-key, but float(1.5) provided [InvalidArgument] phpdoc_advanced_fallback_array_key.php:59: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects array-key, but true provided [InvalidArgument] phpdoc_advanced_fallback_array_key.php:60: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey cannot be null, null value provided to parameter with type array-key [NullArgument] phpdoc_advanced_fallback_array_key.php:65: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsInt expects int, but parent type array-key provided [MixedArgumentTypeCoercion] phpdoc_advanced_fallback_array_key.php:66: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsString expects string, but parent type array-key provided [MixedArgumentTypeCoercion]
|
| mago | 1.46.0mago 1.46.0 | Enforced | phpdoc_advanced_fallback_array_key.php:58: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey`: expected `array-key`, but found `float(1.5)`. [invalid-argument] phpdoc_advanced_fallback_array_key.php:59: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey`: expected `array-key`, but found `true`. [invalid-argument] phpdoc_advanced_fallback_array_key.php:60: Argument #1 of function `Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey` is `null`, but parameter type `array-key` does not accept it. [null-argument] phpdoc_advanced_fallback_array_key.php:65: Argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsInt`: expected `int`, but provided type `array-key` is less specific. [less-specific-argument] phpdoc_advanced_fallback_array_key.php:66: Argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsString`: expected `string`, but provided type `array-key` is less specific. [less-specific-argument]
|
| mir | 0.70.1mir 0.70.1 | Partly enforced (3/5) | phpdoc_advanced_fallback_array_key.php:58: InvalidArgument: Argument $value of acceptsArrayKey() expects 'int|string', got '1.5' [MIR0201] phpdoc_advanced_fallback_array_key.php:59: InvalidArgument: Argument $value of acceptsArrayKey() expects 'int|string', got 'true' [MIR0201] phpdoc_advanced_fallback_array_key.php:60: NullArgument: Argument $value of acceptsArrayKey() cannot be null [MIR0100]
|
| phpantom | 0.9.0phpantom_lsp 0.9.0 | Enforced | phpdoc_advanced_fallback_array_key.php:58: Argument 1 ($value) expects array-key, got 1.5 [type_mismatch_argument] phpdoc_advanced_fallback_array_key.php:59: Argument 1 ($value) expects array-key, got bool [type_mismatch_argument] phpdoc_advanced_fallback_array_key.php:60: Argument 1 ($value) expects array-key, got null [type_mismatch_argument] phpdoc_advanced_fallback_array_key.php:65: Argument 1 ($value) expects int, got array-key [type_mismatch_argument] phpdoc_advanced_fallback_array_key.php:66: Argument 1 ($value) expects string, got array-key [type_mismatch_argument]
|
| intelephense | 1.18.5intelephense 1.18.5 | Partly enforced (2/5) | phpdoc_advanced_fallback_array_key.php:65: Expected type 'int'. Found 'string|int'. [P1006] phpdoc_advanced_fallback_array_key.php:66: Expected type 'string'. Found 'string|int'. [P1006]
|
| phpy | 0.2.0phpy 0.2.0 | Partly enforced (3/5) | phpdoc_advanced_fallback_array_key.php:58: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey() is expected to be of type int|string, float given phpdoc_advanced_fallback_array_key.php:59: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey() is expected to be of type int|string, bool given phpdoc_advanced_fallback_array_key.php:60: Argument '1' passed to Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey() is expected to be of type int|string, null given
|
| qodana | 262.8665.325Qodana 262.8665.325 | Partly enforced (2/5) | phpdoc_advanced_fallback_array_key.php:58: Parameter '1.5' type is not compatible with declaration when using strict type matching [PhpParamsInspection] phpdoc_advanced_fallback_array_key.php:59: Parameter 'true' type is not compatible with declaration when using strict type matching [PhpParamsInspection]
|
| noverify | 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 | Partly enforced (2/5) | phpdoc_advanced_fallback_array_key.php:59: potentially not safe access in parameter value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey [notSafeCall] phpdoc_advanced_fallback_array_key.php:60: null passed to non-nullable parameter value in function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey [notNullSafetyFunctionArgumentConstFetch]
|
| steins | 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) | Partly enforced (3/5) | phpdoc_advanced_fallback_array_key.php:58: argument 1.5 to acceptsArrayKey() violates declared @param array-key $value — declared contract violation [phpdoc.param-mismatch] phpdoc_advanced_fallback_array_key.php:59: argument true to acceptsArrayKey() violates declared @param array-key $value — declared contract violation [phpdoc.param-mismatch] phpdoc_advanced_fallback_array_key.php:60: argument null to acceptsArrayKey() violates declared @param array-key $value — declared contract violation [phpdoc.param-mismatch]
Notes: The 2 residual lines (65-66: forwarding returnsArrayKey()'s declared int|string return straight into acceptsInt()/acceptsString()) are an honest gap, not a Maybe: nothing in steins-infer propagates a callee's declared @return contract into the caller's argument-fact lane for a bare call-expression argument, so no verdict at all is computed there. The 3 direct-value lines (58-60) do fire correctly. |
| phpstan-strict | 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 | Partly enforced (3/5) | phpdoc_advanced_fallback_array_key.php:58: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), float given. [identifier=argument.type] phpdoc_advanced_fallback_array_key.php:59: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), true given. [identifier=argument.type] phpdoc_advanced_fallback_array_key.php:60: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects (int|string), null given. [identifier=argument.type]
|
| psalm-next | 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 | Enforced | phpdoc_advanced_fallback_array_key.php:58: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects array-key, but float(1.5) provided [InvalidArgument] phpdoc_advanced_fallback_array_key.php:59: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey expects array-key, but true provided [InvalidArgument] phpdoc_advanced_fallback_array_key.php:60: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsArrayKey cannot be null, null value provided to parameter with type array-key [NullArgument] phpdoc_advanced_fallback_array_key.php:65: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsInt expects int, but parent type array-key provided [MixedArgumentTypeCoercion] phpdoc_advanced_fallback_array_key.php:66: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackArrayKey\acceptsString expects string, but parent type array-key provided [MixedArgumentTypeCoercion]
|
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedFallbackArrayKey;
/**
* `array-key`
*
* `array-key` is the union of the types PHP accepts as an array key,
* `int|string`. Unlike the other pseudo-types in this group it refines no
* single native type: both `int` and `string` satisfy it, while an `array-key`
* value satisfies neither `int` nor `string` on its own. Analyzers that model
* it reject a value PHP cannot use as a key; others fall back to `mixed` and
* accept everything.
*
* The reverse direction separates the analyzers further. PHPStan resolves the
* keyword to a *benevolent* union, which is deliberately accepted wherever
* either member is expected, so passing an `array-key` to a native `string`
* parameter stays silent there. Analyzers that resolve it to an ordinary
* `int|string` — or to a dedicated array-key atom — report the half of the
* union that does not fit.
*
* References:
* - PHPStan TypeNodeResolver `array-key` resolves to BenevolentUnionType(int|string)
* - Psalm docs, Type syntax / Scalar types: `array-key`
*/
/**
* @return array-key
*/
function returnsArrayKey() // T: array-key
{
return \random_int(0, 1) === 1 ? 1 : 'key'; // V
}
function acceptsInt(int $value): void
{
}
function acceptsString(string $value): void
{
}
/**
* @param array-key $value
*/
function acceptsArrayKey($value): void // T: array-key
{
}
// Both members of the union satisfy the parameter.
acceptsArrayKey(1); // V
acceptsArrayKey('key'); // V
// Values PHP cannot use as an array key: analyzers that model `array-key`
// reject them, others fall back to `mixed` and accept them.
acceptsArrayKey(1.5); // E?: a float is not an array-key (int|string)
acceptsArrayKey(true); // E?: a bool is not an array-key (int|string)
acceptsArrayKey(null); // E?: null is not an array-key (int|string)
// The reverse direction: an `array-key` is not narrowed to either member, so a
// parameter that wants only one of them is not satisfied. PHPStan's benevolent
// union stays silent here by design.
acceptsInt(returnsArrayKey()); // E?: an array-key may be a string
acceptsString(returnsArrayKey()); // E?: an array-key may be an int