← All results

isset($array[$key]) on a possibly-null element

Group: Arrays and shapes · Category: phpdoc · File: arrays_isset_nullable_fetch.php

PHP isset is false when the key is missing *or* the value is null, so after a successful isset the element is the non-null half of T|null. Inside isset, reading a key that may be absent is the condition, not an undefined-offset error (vimeo/psalm#10756). References: - psalm#10756

Analyzer results

AnalyzerVersionResultDiagnostics
phan 6.0.11Phan 6.0.11 php-ast version 1.1.3 PHP version used to run Phan: 8.5.11 Fail
arrays_isset_nullable_fetch.php:29: Argument 1 ($value) is $array['key'] of type ?int but \Conformance\Tests\ArraysIssetNullableFetch\takesInt() takes int defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/arrays_isset_nullable_fetch.php:19 (expected type to be non-nullable) [PhanTypeMismatchArgumentNullable]
Expectation diff
Line 29: Expected silence (quiet probe), got ["Argument 1 ($value) is $array['key'] of type ?int but \\Conformance\\Tests\\ArraysIssetNullableFetch\\takesInt() takes int defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/arrays_isset_nullable_fetch.php:19 (expected type to be non-nullable) [PhanTypeMismatchArgumentNullable]"]
phpstan 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 Pass reported Lv.4+
arrays_isset_nullable_fetch.php:30: Strict comparison using === between int and null will always evaluate to false. [reported-from-level=4]
With strict-rules
arrays_isset_nullable_fetch.php:30: Strict comparison using === between int and null will always evaluate to false.
psalm 6.20.9Psalm 6.20.9@f67170ac3c3b931f5088681e21d6c8babc601c08
next: 7.0.4
Pass (pzoom≠)
arrays_isset_nullable_fetch.php:30: int does not contain null [DocblockTypeContradiction]
arrays_isset_nullable_fetch.php:30: Docblock-defined type int for $array['key'] is never null [RedundantConditionGivenDocblockType]
pzoom (Psalm port)

No diagnostics from pzoom.

mago 1.59.0mago 1.59.0 Pass
arrays_isset_nullable_fetch.php:30: Redundant `===` comparison: left-hand side is never identical to right-hand side. [redundant-comparison]
arrays_isset_nullable_fetch.php:30: Redundant ternary operator: condition is always falsy. [impossible-condition]
mir 0.91.0mir 0.91.0 Fail
arrays_isset_nullable_fetch.php:29: PossiblyNullArgument: Argument $value of takesInt() might be null [MIR0104]
Expectation diff
Line 29: Expected silence (quiet probe), got ["PossiblyNullArgument: Argument $value of takesInt() might be null [MIR0104]"]
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.19490phpy 1.0.19490 Pass

No diagnostics reported.

qodana 262.11879.104PhpStorm 262.11879.104 Pass
arrays_isset_nullable_fetch.php:30: Condition is always 'false' because 'isset($array['key'])' is already 'true' at this point [PhpConditionAlreadyCheckedInspection]
noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Fail
arrays_isset_nullable_fetch.php:26: param $array miss matched with phpdoc type <<array<string, int|null>>> [funcParamTypeMissMatch]
arrays_isset_nullable_fetch.php:29: potential null array access in parameter value of function Conformance\Tests\ArraysIssetNullableFetch\takesInt [notNullSafetyFunctionArgumentArrayDimFetch]
arrays_isset_nullable_fetch.php:37: param $array miss matched with phpdoc type <<array<string, int>>> [funcParamTypeMissMatch]
arrays_isset_nullable_fetch.php:40: potential null array access in parameter value of function Conformance\Tests\ArraysIssetNullableFetch\takesInt [notNullSafetyFunctionArgumentArrayDimFetch]
Expectation diff
Line 29: Expected silence (quiet probe), got ["potential null array access in parameter value of function Conformance\\Tests\\ArraysIssetNullableFetch\\takesInt [notNullSafetyFunctionArgumentArrayDimFetch]"]
Line 40: Expected valid value to be accepted, got ["potential null array access in parameter value of function Conformance\\Tests\\ArraysIssetNullableFetch\\takesInt [notNullSafetyFunctionArgumentArrayDimFetch]"]
Line 26: Unexpected errors ["param $array miss matched with phpdoc type <<array<string, int|null>>> [funcParamTypeMissMatch]"]
Line 37: Unexpected errors ["param $array miss matched with phpdoc type <<array<string, int>>> [funcParamTypeMissMatch]"]
steins 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) Pass

No diagnostics reported.

phpstan-strict 2.3.5PHPStan - PHP Static Analysis Tool 2.3.5 Pass
arrays_isset_nullable_fetch.php:30: Strict comparison using === between int and null will always evaluate to false.
psalm-next 7.0.4Psalm 7.0.4@fa5b1f6e665cb743838ffd865b8fc96207f9409f Pass
arrays_isset_nullable_fetch.php:30: int does not contain null [DocblockTypeContradiction]
arrays_isset_nullable_fetch.php:30: Docblock-defined type int for $array['key'] is never null [RedundantConditionGivenDocblockType]

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\ArraysIssetNullableFetch;

/**
 * `isset($array[$key])` on a possibly-null element.
 *
 * PHP `isset` is false when the key is missing *or* the value is `null`, so
 * after a successful `isset` the element is the non-null half of `T|null`.
 * Inside `isset`, reading a key that may be absent is the condition, not an
 * undefined-offset error (vimeo/psalm#10756).
 *
 * References:
 * - https://github.com/vimeo/psalm/pull/10756
 */

function takesInt(int $value): void
{
}

/**
 * @param array<string, int|null> $array
 */
function afterIsset(array $array): void
{
    if (isset($array['key'])) {
        takesInt($array['key']); // Q: isset excludes null
        echo $array['key'] === null ? 'null' : 'int'; // E?: after isset, === null is always false
    }
}

/**
 * @param array<string, int> $array
 */
function insideIsset(array $array): void
{
    if (isset($array['missing'])) { // Q: offset access inside isset is not an undefined-key error
        takesInt($array['missing']); // V
    }
}