From 515ab71e9403ecee4846d2c4173f7a993a9910e2 Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Wed, 18 Mar 2026 06:39:00 +0000 Subject: [PATCH 01/10] Fix phpstan/phpstan#14319: Prevent exponential type growth in array dim fetch narrowing - Added ARRAY_DIM_FETCH_UNION_TYPE_LIMIT (8) to MutatingScope to cap parent type narrowing - When array dim fetch narrows a parent variable type that is already a UnionType with more than 8 members, skip propagating HasOffsetValueType intersections to the parent - This prevents 2^N exponential growth when N consecutive if-blocks check different array offsets on an array|object typed variable - New regression test in tests/PHPStan/Analyser/nsrt/bug-14319.php - New benchmark test in tests/bench/data/bug-14319.php --- src/Analyser/MutatingScope.php | 21 ++++++----- tests/PHPStan/Analyser/nsrt/bug-14319.php | 44 +++++++++++++++++++++++ tests/bench/data/bug-14319.php | 39 ++++++++++++++++++++ 3 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-14319.php create mode 100644 tests/bench/data/bug-14319.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index e60242af43..f0cadbcab1 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -139,6 +139,8 @@ class MutatingScope implements Scope, NodeCallbackInvoker public const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid'; private const CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME = 'containsSuperGlobal'; + private const ARRAY_DIM_FETCH_UNION_TYPE_LIMIT = 8; + /** @var Type[] */ private array $resolvedTypes = []; @@ -2690,7 +2692,8 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, $isArray = $exprVarType->isArray(); if (!$exprVarType instanceof MixedType && !$isArray->no()) { $varType = $exprVarType; - if (!$isArray->yes()) { + $tooComplex = $exprVarType instanceof UnionType && count($exprVarType->getTypes()) > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT; + if (!$tooComplex && !$isArray->yes()) { if ($dimType->isInteger()->yes()) { $varType = TypeCombinator::intersect($exprVarType, StaticTypeFactory::intOffsetAccessibleType()); } else { @@ -2698,19 +2701,21 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, } } - if ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) { + if (!$tooComplex && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType)) { $varType = TypeCombinator::intersect( $varType, new HasOffsetValueType($dimType, $type), ); } - $scope = $scope->specifyExpressionType( - $expr->var, - $varType, - $scope->getNativeType($expr->var), - $certainty, - ); + if (!$tooComplex) { + $scope = $scope->specifyExpressionType( + $expr->var, + $varType, + $scope->getNativeType($expr->var), + $certainty, + ); + } } } } diff --git a/tests/PHPStan/Analyser/nsrt/bug-14319.php b/tests/PHPStan/Analyser/nsrt/bug-14319.php new file mode 100644 index 0000000000..539c347e77 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14319.php @@ -0,0 +1,44 @@ + Date: Wed, 18 Mar 2026 07:51:18 +0100 Subject: [PATCH 02/10] simplify fix --- src/Analyser/MutatingScope.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index f0cadbcab1..b28de95e37 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2692,8 +2692,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, $isArray = $exprVarType->isArray(); if (!$exprVarType instanceof MixedType && !$isArray->no()) { $varType = $exprVarType; - $tooComplex = $exprVarType instanceof UnionType && count($exprVarType->getTypes()) > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT; - if (!$tooComplex && !$isArray->yes()) { + if (!$isArray->yes()) { if ($dimType->isInteger()->yes()) { $varType = TypeCombinator::intersect($exprVarType, StaticTypeFactory::intOffsetAccessibleType()); } else { @@ -2701,6 +2700,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, } } + $tooComplex = $exprVarType instanceof UnionType && count($exprVarType->getTypes()) > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT; if (!$tooComplex && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType)) { $varType = TypeCombinator::intersect( $varType, @@ -2708,14 +2708,12 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, ); } - if (!$tooComplex) { - $scope = $scope->specifyExpressionType( - $expr->var, - $varType, - $scope->getNativeType($expr->var), - $certainty, - ); - } + $scope = $scope->specifyExpressionType( + $expr->var, + $varType, + $scope->getNativeType($expr->var), + $certainty, + ); } } } From a94df705fefec6934b58aae915f131c10fec29ef Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Wed, 18 Mar 2026 07:51:50 +0000 Subject: [PATCH 03/10] Only count HasOffsetValueType accessories for complexity limit Instead of counting all union type members, only count union members that contain HasOffsetValueType accessories. This more precisely targets the source of exponential growth while avoiding false positives on unions that are large for other reasons. Co-Authored-By: Claude Opus 4.6 --- src/Analyser/MutatingScope.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index b28de95e37..50086b9475 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2700,7 +2700,18 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, } } - $tooComplex = $exprVarType instanceof UnionType && count($exprVarType->getTypes()) > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT; + $hasOffsetAccessoryCount = 0; + if ($exprVarType instanceof UnionType) { + foreach ($exprVarType->getTypes() as $innerType) { + foreach (TypeUtils::getAccessoryTypes($innerType) as $accessoryType) { + if ($accessoryType instanceof HasOffsetValueType) { + $hasOffsetAccessoryCount++; + break; + } + } + } + } + $tooComplex = $hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT; if (!$tooComplex && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType)) { $varType = TypeCombinator::intersect( $varType, From f4ed06ff5d91dba9e70abf68c56756db0cf61d9c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 18 Mar 2026 14:35:55 +0100 Subject: [PATCH 04/10] simplify --- src/Analyser/MutatingScope.php | 12 ++++-- tests/PHPStan/Analyser/nsrt/bug-14319.php | 45 ++++++++++++++++++++++- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 50086b9475..acc837297b 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -139,7 +139,7 @@ class MutatingScope implements Scope, NodeCallbackInvoker public const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid'; private const CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME = 'containsSuperGlobal'; - private const ARRAY_DIM_FETCH_UNION_TYPE_LIMIT = 8; + private const ARRAY_DIM_FETCH_UNION_TYPE_LIMIT = 16; /** @var Type[] */ private array $resolvedTypes = []; @@ -2700,18 +2700,22 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, } } - $hasOffsetAccessoryCount = 0; + $tooComplex = false; if ($exprVarType instanceof UnionType) { + $hasOffsetAccessoryCount = 0; foreach ($exprVarType->getTypes() as $innerType) { foreach (TypeUtils::getAccessoryTypes($innerType) as $accessoryType) { if ($accessoryType instanceof HasOffsetValueType) { $hasOffsetAccessoryCount++; - break; + + if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT) { + $tooComplex = true; + break 2; + } } } } } - $tooComplex = $hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT; if (!$tooComplex && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType)) { $varType = TypeCombinator::intersect( $varType, diff --git a/tests/PHPStan/Analyser/nsrt/bug-14319.php b/tests/PHPStan/Analyser/nsrt/bug-14319.php index 539c347e77..6a8266d8bd 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-14319.php +++ b/tests/PHPStan/Analyser/nsrt/bug-14319.php @@ -22,23 +22,64 @@ protected function edit(int|string|null $IdNum = null): void if ($rows['rap_tr']) { $raport .= 'T: '.$rows['rap_tr'].", \n"; } - assertType('string', $raport); if ($rows['rap_ks']) { $raport .= 'K: '.$rows['rap_ks'].", \n"; } if ($rows['rap_br']) { $raport .= 'B: '.$rows['rap_br'].", \n"; } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_cz']) { $raport .= 'C: '.$rows['rap_cz'].", \n"; } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_fil']) { $raport .= 'Fil: '.$rows['rap_fil'].", \n"; } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_roz']) { $raport .= 'Roz: '.$rows['rap_roz'].", \n"; } - assertType('string', $raport); + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); + if ($rows['rap_roz2']) { + $raport .= 'Roz: '.$rows['rap_roz2'].", \n"; + } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); + if ($rows['rap_roz3']) { + $raport .= 'Roz: '.$rows['rap_roz3'].", \n"; + } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); + } + } +} + +final class test2 +{ + protected function edit(int|string|null $IdNum = null): void + { + $rows = foo("SELECT *", $IdNum); + assertType('array|object', $rows); + + if ($_POST['edycja'] === 'edycja' ) { + $raport = ''; + if ($rows['rap_tr']) { + $raport .= 'T: '.$rows['rap_tr'].", \n"; + } + if ($rows['rap_ks']) { + $raport .= 'K: '.$rows['rap_ks'].", \n"; + } + if ($rows['rap_br']) { + $raport .= 'B: '.$rows['rap_br'].", \n"; + } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); + if ($rows['rap_cz']) { + $raport .= 'C: '.$rows['rap_cz'].", \n"; + } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); + if ($rows['rap_fil']) { + $raport .= 'Fil: '.$rows['rap_fil'].", \n"; + } + assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); } } } From fa1783668849ef834bf3ca1fbfce9e033962345c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 18 Mar 2026 14:39:20 +0100 Subject: [PATCH 05/10] Update bug-14319.php --- tests/PHPStan/Analyser/nsrt/bug-14319.php | 36 ----------------------- 1 file changed, 36 deletions(-) diff --git a/tests/PHPStan/Analyser/nsrt/bug-14319.php b/tests/PHPStan/Analyser/nsrt/bug-14319.php index 6a8266d8bd..395d47d78f 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-14319.php +++ b/tests/PHPStan/Analyser/nsrt/bug-14319.php @@ -28,23 +28,18 @@ protected function edit(int|string|null $IdNum = null): void if ($rows['rap_br']) { $raport .= 'B: '.$rows['rap_br'].", \n"; } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_cz']) { $raport .= 'C: '.$rows['rap_cz'].", \n"; } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_fil']) { $raport .= 'Fil: '.$rows['rap_fil'].", \n"; } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_roz']) { $raport .= 'Roz: '.$rows['rap_roz'].", \n"; } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_roz2']) { $raport .= 'Roz: '.$rows['rap_roz2'].", \n"; } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); if ($rows['rap_roz3']) { $raport .= 'Roz: '.$rows['rap_roz3'].", \n"; } @@ -52,34 +47,3 @@ protected function edit(int|string|null $IdNum = null): void } } } - -final class test2 -{ - protected function edit(int|string|null $IdNum = null): void - { - $rows = foo("SELECT *", $IdNum); - assertType('array|object', $rows); - - if ($_POST['edycja'] === 'edycja' ) { - $raport = ''; - if ($rows['rap_tr']) { - $raport .= 'T: '.$rows['rap_tr'].", \n"; - } - if ($rows['rap_ks']) { - $raport .= 'K: '.$rows['rap_ks'].", \n"; - } - if ($rows['rap_br']) { - $raport .= 'B: '.$rows['rap_br'].", \n"; - } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); - if ($rows['rap_cz']) { - $raport .= 'C: '.$rows['rap_cz'].", \n"; - } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); - if ($rows['rap_fil']) { - $raport .= 'Fil: '.$rows['rap_fil'].", \n"; - } - assertType("(non-empty-array&hasOffsetValue('rap_br', mixed)&hasOffsetValue('rap_ks', mixed)&hasOffsetValue('rap_tr', mixed))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', 0|0.0|''|'0'|array{}|false|null)&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', 0|0.0|''|'0'|array{}|false|null))|(ArrayAccess&hasOffsetValue('rap_br', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_ks', mixed~(0|0.0|''|'0'|array{}|false|null))&hasOffsetValue('rap_tr', mixed~(0|0.0|''|'0'|array{}|false|null)))", $rows); - } - } -} From d5aaa3f10ddea62cd8088348e61556663e05103f Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 18 Mar 2026 14:40:30 +0100 Subject: [PATCH 06/10] cs --- src/Analyser/MutatingScope.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index acc837297b..26681bf6ee 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2705,13 +2705,15 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, $hasOffsetAccessoryCount = 0; foreach ($exprVarType->getTypes() as $innerType) { foreach (TypeUtils::getAccessoryTypes($innerType) as $accessoryType) { - if ($accessoryType instanceof HasOffsetValueType) { - $hasOffsetAccessoryCount++; + if (!($accessoryType instanceof HasOffsetValueType)) { + continue; + } + + $hasOffsetAccessoryCount++; - if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT) { - $tooComplex = true; - break 2; - } + if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT) { + $tooComplex = true; + break 2; } } } From fcec2e38241ca9c86648f86c473d5fed6308bbde Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 18 Mar 2026 14:41:08 +0100 Subject: [PATCH 07/10] Update MutatingScope.php --- src/Analyser/MutatingScope.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 26681bf6ee..3ede5c892b 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2718,7 +2718,10 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, } } } - if (!$tooComplex && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType)) { + if ( + !$tooComplex + && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) + ) { $varType = TypeCombinator::intersect( $varType, new HasOffsetValueType($dimType, $type), From 87f29aff4bb542140ef212dfd1f90985e8879d56 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 18 Mar 2026 14:41:47 +0100 Subject: [PATCH 08/10] Update MutatingScope.php --- src/Analyser/MutatingScope.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 3ede5c892b..4ad83ee066 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -139,7 +139,7 @@ class MutatingScope implements Scope, NodeCallbackInvoker public const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid'; private const CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME = 'containsSuperGlobal'; - private const ARRAY_DIM_FETCH_UNION_TYPE_LIMIT = 16; + private const ARRAY_DIM_FETCH_UNION_HAS_OFFSET_VALUE_TYPE_LIMIT = 16; /** @var Type[] */ private array $resolvedTypes = []; @@ -2711,7 +2711,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, $hasOffsetAccessoryCount++; - if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_TYPE_LIMIT) { + if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_HAS_OFFSET_VALUE_TYPE_LIMIT) { $tooComplex = true; break 2; } From 879291bb32116d4043cdf5aae280c5c46f2ca1f6 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 18 Mar 2026 14:52:10 +0100 Subject: [PATCH 09/10] degrade only maybe-arrays --- src/Analyser/MutatingScope.php | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 4ad83ee066..7acf2220bb 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2691,6 +2691,8 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, $exprVarType = $scope->getType($expr->var); $isArray = $exprVarType->isArray(); if (!$exprVarType instanceof MixedType && !$isArray->no()) { + $tooComplex = false; + $varType = $exprVarType; if (!$isArray->yes()) { if ($dimType->isInteger()->yes()) { @@ -2698,26 +2700,26 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, } else { $varType = TypeCombinator::intersect($exprVarType, StaticTypeFactory::generalOffsetAccessibleType()); } - } - $tooComplex = false; - if ($exprVarType instanceof UnionType) { - $hasOffsetAccessoryCount = 0; - foreach ($exprVarType->getTypes() as $innerType) { - foreach (TypeUtils::getAccessoryTypes($innerType) as $accessoryType) { - if (!($accessoryType instanceof HasOffsetValueType)) { - continue; - } + if ($exprVarType instanceof UnionType) { + $hasOffsetAccessoryCount = 0; + foreach ($exprVarType->getTypes() as $innerType) { + foreach (TypeUtils::getAccessoryTypes($innerType) as $accessoryType) { + if (!($accessoryType instanceof HasOffsetValueType)) { + continue; + } - $hasOffsetAccessoryCount++; + $hasOffsetAccessoryCount++; - if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_HAS_OFFSET_VALUE_TYPE_LIMIT) { - $tooComplex = true; - break 2; + if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_HAS_OFFSET_VALUE_TYPE_LIMIT) { + $tooComplex = true; + break 2; + } } } } } + if ( !$tooComplex && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) From e151f738702dbdc527c0952fccd75915abbad5ff Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Wed, 18 Mar 2026 15:22:35 +0000 Subject: [PATCH 10/10] Rename $tooComplex to $tooManyHasOffsetValueTypes for clarity Co-Authored-By: Claude Opus 4.6 --- src/Analyser/MutatingScope.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 7acf2220bb..c181393494 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2691,7 +2691,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, $exprVarType = $scope->getType($expr->var); $isArray = $exprVarType->isArray(); if (!$exprVarType instanceof MixedType && !$isArray->no()) { - $tooComplex = false; + $tooManyHasOffsetValueTypes = false; $varType = $exprVarType; if (!$isArray->yes()) { @@ -2712,7 +2712,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, $hasOffsetAccessoryCount++; if ($hasOffsetAccessoryCount > self::ARRAY_DIM_FETCH_UNION_HAS_OFFSET_VALUE_TYPE_LIMIT) { - $tooComplex = true; + $tooManyHasOffsetValueTypes = true; break 2; } } @@ -2721,7 +2721,7 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType, } if ( - !$tooComplex + !$tooManyHasOffsetValueTypes && ($dimType instanceof ConstantIntegerType || $dimType instanceof ConstantStringType) ) { $varType = TypeCombinator::intersect(