diff --git a/src/Analyser/ExprHandler/AssignHandler.php b/src/Analyser/ExprHandler/AssignHandler.php index 5d2a084ec04..d80219d9577 100644 --- a/src/Analyser/ExprHandler/AssignHandler.php +++ b/src/Analyser/ExprHandler/AssignHandler.php @@ -1061,6 +1061,8 @@ private function processArrayByRefItems(MutatingScope $scope, string $rootVarNam return $scope; } + private const ARRAY_DIM_FETCH_WRITE_DEPTH_LIMIT = 5; + /** * @param non-empty-list $dimFetchStack * @param non-empty-list $offsetTypes @@ -1073,28 +1075,34 @@ private function produceArrayDimFetchAssignValueToWrite(array $dimFetchStack, ar $offsetValueTypeStack = [$offsetValueType]; $generalizeOnWrite = $offsetTypes[array_key_last($offsetTypes)][0] !== null; + $dimDepth = 0; foreach (array_slice($offsetTypes, 0, -1) as [$offsetType, $dimFetch]) { + $dimDepth++; if ($offsetType === null) { $offsetValueType = new ConstantArrayType([], []); $generalizeOnWrite = false; } else { - $has = $offsetValueType->hasOffsetValueType($offsetType); - if ($has->yes()) { - if ($scope->hasExpressionType($dimFetch)->yes()) { - $offsetValueType = $scope->getType($dimFetch); + if ($dimDepth > self::ARRAY_DIM_FETCH_WRITE_DEPTH_LIMIT && $offsetValueType->isOversizedArray()->yes()) { + $offsetValueType = new MixedType(); + } else { + $has = $offsetValueType->hasOffsetValueType($offsetType); + if ($has->yes()) { + if ($scope->hasExpressionType($dimFetch)->yes()) { + $offsetValueType = $scope->getType($dimFetch); + } else { + $offsetValueType = $offsetValueType->getOffsetValueType($offsetType); + } + } elseif ($has->maybe()) { + if ($scope->hasExpressionType($dimFetch)->yes()) { + $generalizeOnWrite = false; + $offsetValueType = $scope->getType($dimFetch); + } else { + $offsetValueType = TypeCombinator::union($offsetValueType->getOffsetValueType($offsetType), new ConstantArrayType([], [])); + } } else { - $offsetValueType = $offsetValueType->getOffsetValueType($offsetType); - } - } elseif ($has->maybe()) { - if ($scope->hasExpressionType($dimFetch)->yes()) { $generalizeOnWrite = false; - $offsetValueType = $scope->getType($dimFetch); - } else { - $offsetValueType = TypeCombinator::union($offsetValueType->getOffsetValueType($offsetType), new ConstantArrayType([], [])); + $offsetValueType = new ConstantArrayType([], []); } - } else { - $generalizeOnWrite = false; - $offsetValueType = new ConstantArrayType([], []); } } diff --git a/tests/bench/data/bug-14624.php b/tests/bench/data/bug-14624.php new file mode 100644 index 00000000000..aafc111c7a4 --- /dev/null +++ b/tests/bench/data/bug-14624.php @@ -0,0 +1,91 @@ +db->query(''); + + while ($row = $rows->fetch_assoc()) { + $row['group_id'] = intval($row['group_id']); + $bucket = intval($row['bucket']); + + if (!isset($out[$row['a']])) { + $out[$row['a']] = ['id' => $row['a'], 'label' => $row['a_label'], 'groups' => []]; + } + + if (!isset($out[$row['a']]['groups'][$bucket])) { + $out[$row['a']]['groups'][$bucket] = []; + } + + if (!isset($out[$row['a']]['groups'][$bucket][$row['group_id']])) { + $out[$row['a']]['groups'][$bucket][$row['group_id']] = ['id' => $row['group_id'], 'label' => $row['group_label'], 'sections' => []]; + } + + if (!isset($out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']])) { + $out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']] = ['id' => $row['section_id'], 'label' => $row['section_label'], 'items' => []]; + } + + if (!isset($out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']]['items'][$row['item_id']])) { + $row['csv_ids'] = $row['csv_ids'] ? array_map('intval', explode(',', $row['csv_ids'])) : []; + $out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']]['items'][$row['item_id']] = [ + 'id' => $row['item_id'], 'title' => $row['item_title'], 'code' => $row['item_code'], + 'type' => $row['item_type'], 'state' => $row['item_state'], 'priority' => $row['item_priority'], + 'csv_ids' => $row['csv_ids'], 'related_rows' => [], 'details' => [], 'tags' => [], + ]; + if ($row['csv_ids']) { + $relatedRows = $this->db->query(''); + while ($relatedRow = $relatedRows->fetch_assoc()) { + $out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']]['items'][$row['item_id']]['related_rows'][] = $relatedRow; + } + } + } + + if (!isset($out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']]['items'][$row['item_id']]['details'][$row['detail_id']])) { + $out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']]['items'][$row['item_id']]['details'][$row['detail_id']] = [ + 'id' => $row['detail_id'], 'title' => $row['detail_title'], 'code' => $row['detail_code'], + 'kind' => $row['detail_kind'], 'amount' => $row['detail_amount'], + 'records' => [], 'notes' => [], 'flags' => [], + ]; + } + + if (!isset($out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']]['items'][$row['item_id']]['details'][$row['detail_id']]['records'][$row['record_id']])) { + $out[$row['a']]['groups'][$bucket][$row['group_id']]['sections'][$row['section_id']]['items'][$row['item_id']]['details'][$row['detail_id']]['records'][$row['record_id']] = [ + 'id' => $row['record_id'], 'name' => $row['record_name'], 'code' => $row['record_code'], + 'version' => $row['record_version'], 'payload' => $row['record_payload'], + ]; + } + } + + return $out; + } +}