← All results

Optional extra non-list key stays list-compatible

Group: Cross-analyzer regressions · Category: regression · File: regressions_optional_extra_key_is_list.php

array{0: int, a?: string} admits the value [0 => 1] (the optional string key a is absent), and array_is_list([0 => 1]) === true. So array_is_list($b) is maybe-true, not "always false", and the list branch is reachable. This is the second reproduction from phpstan#14938 — the optional-extra-key variant of the optional-key family (see regressions_optional_key_shape_is_list.php for the base case). References: - PHPStan phpstan#14938 (fix: phpstan-src#6025) - Cross-tool survey phpstan#14939

Analyzer results

AnalyzerVersionResultDiagnostics
phan 6.0.10Phan 6.0.10 php-ast version 1.1.3 PHP version used to run Phan: 8.5.11 Pass

No diagnostics reported.

phpstan 2.3.1PHPStan - PHP Static Analysis Tool 2.3.1 Pass

No diagnostics reported.

psalm 6.20.0Psalm 6.20.0@804f03c0c183d149fcd4b8c7c0c9b3fbf836d34c
next: 7.0.0-rc2
Pass
regressions_optional_extra_key_is_list.php:24: Type array{0: int, a?: string} for $b is never list<mixed> [TypeDoesNotContainType]
mago 1.57.0mago 1.57.0 Pass

No diagnostics reported.

mir 0.87.0mir 0.87.0 Pass
regressions_optional_extra_key_is_list.php:24: RedundantCondition: Condition is always false, so the then branch is never reached [MIR0400]
phpantom 0.11.1phpantom_lsp 0.11.1 Pass

No diagnostics reported.

intelephense 1.18.5intelephense 1.18.5 Pass

No diagnostics reported.

phpactor 2026.10.07.0Phpactor 2026.10.07.0 Pass

No diagnostics reported.

phpy 1.0.19409phpy 1.0.19409 Pass

No diagnostics reported.

qodana 262.10968.76PhpStorm 262.10968.76 Pass

No diagnostics reported.

noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Pass
regressions_optional_extra_key_is_list.php:22: param $b miss matched with phpdoc type <<array{0: int, a?: string}>> [funcParamTypeMissMatch]
steins 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) Pass

No diagnostics reported.

phpstan-strict 2.3.1PHPStan - PHP Static Analysis Tool 2.3.1 Pass

No diagnostics reported.

psalm-next 7.0.0-rc2Psalm 7.0.0-rc2@20339e3fe5b8d03bcd8061030eb2a83ad50e5783 Pass
regressions_optional_extra_key_is_list.php:24: Type array{0: int, a?: string} for $b is never list<mixed> [TypeDoesNotContainType]

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\RegressionsOptionalExtraKeyIsList;

/**
 * Optional extra non-list key stays list-compatible.
 *
 * `array{0: int, a?: string}` admits the value `[0 => 1]` (the optional string key `a` is
 * absent), and `array_is_list([0 => 1]) === true`. So `array_is_list($b)` is maybe-true,
 * not "always false", and the list branch is reachable. This is the second reproduction
 * from phpstan#14938 — the optional-extra-key variant of the optional-key family (see
 * regressions_optional_key_shape_is_list.php for the base case).
 *
 * References:
 * - PHPStan  https://github.com/phpstan/phpstan/issues/14938  (fix: phpstan-src#6025)
 * - Cross-tool survey https://github.com/phpstan/phpstan/discussions/14939
 */

/** @param array{0: int, a?: string} $b */
function optionalExtraKeyIsList(array $b): void // E<noverify>: NoVerify does not parse array shape syntax and flags the annotation itself
{
    if (array_is_list($b)) { // E<psalm>: TypeDoesNotContainType, reported as never list // E<pzoom>: same TypeDoesNotContainType as Psalm // E<mir>: RedundantCondition — collapses the check. PHPStan reported always-false through 2.2.8 (#14938) and is silent since 2.2.9. Mago reported impossible-type-comparison in 1.19 and is silent since 1.43
        echo "reached at runtime when \$b === [0 => 1]\n";
    }
}

optionalExtraKeyIsList([0 => 1]);