← All results

$b === false on a ternary must not leak its arm-narrowing out of the if

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

$b = !$a ? $n : 0 then if ($b === false) proves $a is false *inside* that branch (the : 0 arm is never false). After the if, $a is still bool. PHPStan 2.2.15 compared the arms by truthiness in exact contexts, so $a stayed false on the fall-through. Source: phpstan/phpstan#15307, phpstan/phpstan-src#6580 (2.2.16). References: - phpstan#15307 - phpstan-src#6580

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 Fail

No diagnostics reported.

Expectation diff
Line 38: Expected 1 error(s)
phpstan 2.3.1PHPStan - PHP Static Analysis Tool 2.3.1 Pass reported Lv.5+
regressions_ternary_exact_narrowing.php:38: Parameter #1 $value of function Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse expects false, bool given. [reported-from-level=5]
With strict-rules
regressions_ternary_exact_narrowing.php:38: Parameter #1 $value of function Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse expects false, bool given.
psalm 6.20.0Psalm 6.20.0@804f03c0c183d149fcd4b8c7c0c9b3fbf836d34c
next: 7.0.0-rc2
Pass
regressions_ternary_exact_narrowing.php:35: Argument 1 of Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse expects false, but bool provided [InvalidArgument]
regressions_ternary_exact_narrowing.php:38: Argument 1 of Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse expects false, but bool provided [InvalidArgument]
mago 1.57.0mago 1.57.0 Pass
regressions_ternary_exact_narrowing.php:35: Possible argument type mismatch for argument #1 of `Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse`: expected `false`, but possibly received `bool`. [possibly-invalid-argument]
regressions_ternary_exact_narrowing.php:38: Possible argument type mismatch for argument #1 of `Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse`: expected `false`, but possibly received `bool`. [possibly-invalid-argument]
mir 0.87.0mir 0.87.0 Fail

No diagnostics reported.

Expectation diff
Line 38: Expected 1 error(s)
phpantom 0.11.1phpantom_lsp 0.11.1 Fail

No diagnostics reported.

Expectation diff
Line 38: Expected 1 error(s)
intelephense 1.18.5intelephense 1.18.5 Pass
regressions_ternary_exact_narrowing.php:35: Expected type 'false'. Found 'bool'. [P1006]
regressions_ternary_exact_narrowing.php:38: Expected type 'false'. Found 'bool'. [P1006]
phpactor 2026.10.07.0Phpactor 2026.10.07.0 Fail

No diagnostics reported.

Expectation diff
Line 38: Expected 1 error(s)
phpy 1.0.19409phpy 1.0.19409 Fail

No diagnostics reported.

Expectation diff
Line 38: Expected 1 error(s)
qodana 262.10968.76PhpStorm 262.10968.76 Fail

No diagnostics reported.

Expectation diff
Line 38: Expected 1 error(s)
noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Fail
regressions_ternary_exact_narrowing.php:35: potentially not safe call in function Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse signature of param value [notSafeCall]
regressions_ternary_exact_narrowing.php:38: potentially not safe call in function Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse signature of param value [notSafeCall]
regressions_ternary_exact_narrowing.php:39: potentially not safe call in function Conformance\Tests\RegressionsTernaryExactNarrowing\takesBool signature of param value [notSafeCall]
Expectation diff
Line 39: Expected valid value to be accepted, got ["potentially not safe call in function Conformance\\Tests\\RegressionsTernaryExactNarrowing\\takesBool signature of param value [notSafeCall]"]
steins 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) Fail

No diagnostics reported.

Expectation diff
Line 38: Expected 1 error(s)
phpstan-strict 2.3.1PHPStan - PHP Static Analysis Tool 2.3.1 Pass
regressions_ternary_exact_narrowing.php:38: Parameter #1 $value of function Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse expects false, bool given.
psalm-next 7.0.0-rc2Psalm 7.0.0-rc2@20339e3fe5b8d03bcd8061030eb2a83ad50e5783 Pass
regressions_ternary_exact_narrowing.php:35: Argument 1 of Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse expects false, but bool provided [InvalidArgument]
regressions_ternary_exact_narrowing.php:38: Argument 1 of Conformance\Tests\RegressionsTernaryExactNarrowing\takesFalse expects false, but bool provided [InvalidArgument]

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\RegressionsTernaryExactNarrowing;

/**
 * `$b === false` on a ternary must not leak its arm-narrowing out of the `if`.
 *
 * `$b = !$a ? $n : 0` then `if ($b === false)` proves `$a` is false *inside*
 * that branch (the `: 0` arm is never false). After the `if`, `$a` is still
 * `bool`. PHPStan 2.2.15 compared the arms by truthiness in exact contexts, so
 * `$a` stayed `false` on the fall-through.
 *
 * Source: phpstan/phpstan#15307, phpstan/phpstan-src#6580 (2.2.16).
 *
 * References:
 * - https://github.com/phpstan/phpstan/issues/15307
 * - https://github.com/phpstan/phpstan-src/pull/6580
 */

function takesBool(bool $value): void
{
}

function takesFalse(false $value): void
{
}

function afterExactCheck(bool $a, int|false $n): void
{
    $b = !$a ? $n : 0;

    if ($b === false) {
        takesFalse($a); // Q?: inside the branch, the `: 0` arm is impossible, so $a is false
    }

    takesFalse($a); // E: after the if, $a is still bool
    takesBool($a); // V
}