|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace OrConditionTernaryNarrowing; |
| 4 | + |
| 5 | +use function PHPStan\Testing\assertType; |
| 6 | + |
| 7 | +class A {} |
| 8 | +class B {} |
| 9 | + |
| 10 | +/** |
| 11 | + * Mirrors the shape used in TypeCombinator::intersect() where |
| 12 | + * `$constArrayIsI` / `$constArrayIsJ` pair up with `$constArray` / |
| 13 | + * `$otherArray` picked via ternaries inside a nested for-loop over an |
| 14 | + * `$types` list that gets spliced during iteration. |
| 15 | + * |
| 16 | + * @param list<A|B> $types |
| 17 | + */ |
| 18 | +function loopWithTernary(array $types): void |
| 19 | +{ |
| 20 | + $typesCount = count($types); |
| 21 | + for ($i = 0; $i < $typesCount; $i++) { |
| 22 | + for ($j = $i + 1; $j < $typesCount; $j++) { |
| 23 | + $iIsA = $types[$i] instanceof A && ($types[$j] instanceof A || $types[$j] instanceof B); |
| 24 | + $jIsA = $types[$j] instanceof A && ($types[$i] instanceof A || $types[$i] instanceof B); |
| 25 | + |
| 26 | + if ($iIsA || $jIsA) { |
| 27 | + $a = $iIsA ? $types[$i] : $types[$j]; |
| 28 | + |
| 29 | + // `$a` is definitely A: when `$iIsA` holds, the ternary picks |
| 30 | + // `$types[$i]` which is A; when `$iIsA` is false, the outer |
| 31 | + // OR forces `$jIsA` so the ternary picks `$types[$j]` which |
| 32 | + // is A. |
| 33 | + assertType(A::class, $a); |
| 34 | + } |
| 35 | + } |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +/** |
| 40 | + * Same pattern but with plain variables — each `$xIsA`/`$yIsA` records two |
| 41 | + * narrowings (both `$x` and `$y`). The earlier holder (from the first |
| 42 | + * assignment) must survive the second assignment so that inside the |
| 43 | + * outer `if ($xIsA || $yIsA)` the nested `if ($xIsA)` can still fire the |
| 44 | + * conditional holders attached to `$xIsA`. |
| 45 | + * |
| 46 | + * @param A|B $x |
| 47 | + * @param A|B $y |
| 48 | + */ |
| 49 | +function twoStoredAndNarrowings($x, $y): void |
| 50 | +{ |
| 51 | + $xBothA = $x instanceof A && $y instanceof A; |
| 52 | + $yBothA = $y instanceof A && $x instanceof A; |
| 53 | + |
| 54 | + if ($xBothA || $yBothA) { |
| 55 | + if ($xBothA) { |
| 56 | + assertType(A::class, $x); |
| 57 | + assertType(A::class, $y); |
| 58 | + } else { |
| 59 | + assertType(A::class, $x); |
| 60 | + assertType(A::class, $y); |
| 61 | + } |
| 62 | + } |
| 63 | +} |
0 commit comments