diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 6028a9d72..7a2137dc0 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -3489,6 +3489,7 @@ class Calculation $testSheet->getCell($cellAddress['cell']); } } + self::$returnArrayAsType = $returnArrayAsType; throw new Exception($e->getMessage(), $e->getCode(), $e); } diff --git a/src/PhpSpreadsheet/Cell/Cell.php b/src/PhpSpreadsheet/Cell/Cell.php index 987e1a361..543574e84 100644 --- a/src/PhpSpreadsheet/Cell/Cell.php +++ b/src/PhpSpreadsheet/Cell/Cell.php @@ -356,7 +356,7 @@ class Cell implements Stringable $this->getWorksheet()->setSelectedCells($selected); $this->getWorksheet()->getParentOrThrow()->setActiveSheetIndex($index); // We don't yet handle array returns - if (is_array($result)) { + if (is_array($result) && Calculation::getArrayReturnType() !== Calculation::RETURN_ARRAY_AS_ARRAY) { while (is_array($result)) { $result = array_shift($result); } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index a14ff2812..301d988ab 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1402,10 +1402,24 @@ class Worksheet extends WriterPart } $attributes = $cell->getFormulaAttributes(); + $ref = $cell->getCoordinate(); + if (is_array($calculatedValue)) { + $attributes['t'] = 'array'; + $rows = max(1, count($calculatedValue)); + $cols = 1; + foreach ($calculatedValue as $row) { + $cols = max($cols, is_array($row) ? count($row) : 1); + } + $firstCellArray = Coordinate::indexesFromString($ref); + $lastRow = $firstCellArray[1] + $rows - 1; + $lastColumn = $firstCellArray[0] + $cols - 1; + $lastColumnString = Coordinate::stringFromColumnIndex($lastColumn); + $ref .= ":$lastColumnString$lastRow"; + } if (($attributes['t'] ?? null) === 'array') { $objWriter->startElement('f'); $objWriter->writeAttribute('t', 'array'); - $objWriter->writeAttribute('ref', $cell->getCoordinate()); + $objWriter->writeAttribute('ref', $ref); $objWriter->writeAttribute('aca', '1'); $objWriter->writeAttribute('ca', '1'); $objWriter->text(FunctionPrefix::addFunctionPrefixStripEquals($cellValue)); diff --git a/tests/PhpSpreadsheetTests/Worksheet/Table/Issue3659Test.php b/tests/PhpSpreadsheetTests/Worksheet/Table/Issue3659Test.php index 3ec7c6bd5..ff1108fe8 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/Table/Issue3659Test.php +++ b/tests/PhpSpreadsheetTests/Worksheet/Table/Issue3659Test.php @@ -4,10 +4,24 @@ declare(strict_types=1); namespace PhpOffice\PhpSpreadsheetTests\Worksheet\Table; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Worksheet\Table; class Issue3659Test extends SetupTeardown { + private string $arrayReturnType; + + protected function setUp(): void + { + $this->arrayReturnType = Calculation::getArrayReturnType(); + } + + protected function tearDown(): void + { + parent::tearDown(); + Calculation::setArrayReturnType($this->arrayReturnType); + } + public function testTableOnOtherSheet(): void { $spreadsheet = $this->getSpreadsheet(); @@ -44,4 +58,51 @@ class Issue3659Test extends SetupTeardown self::assertSame('F8', $tableSheet->getSelectedCells()); self::assertSame($sheet, $spreadsheet->getActiveSheet()); } + + public function testTableAsArray(): void + { + Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_ARRAY); + $spreadsheet = $this->getSpreadsheet(); + $sheet = $this->getSheet(); + $sheet->setTitle('Feuil1'); + $tableSheet = $spreadsheet->createSheet(); + $tableSheet->setTitle('sheet_with_table'); + $tableSheet->fromArray( + [ + ['MyCol', 'Colonne2', 'Colonne3'], + [10, 20], + [2], + [3], + [4], + ], + null, + 'B1', + true + ); + $table = new Table('B1:D5', 'Tableau1'); + $tableSheet->addTable($table); + $sheet->setSelectedCells('F7'); + $tableSheet->setSelectedCells('F8'); + self::assertSame($sheet, $spreadsheet->getActiveSheet()); + $sheet->getCell('F1')->setValue('=Tableau1[MyCol]'); + $sheet->getCell('H1')->setValue('=Tableau1[]'); + $sheet->getCell('F9')->setValue('=Tableau1'); + $sheet->getCell('J9')->setValue('=CONCAT(Tableau1)'); + $sheet->getCell('J11')->setValue('=SUM(Tableau1[])'); + $expectedResult = [2 => ['B' => 10], ['B' => 2], ['B' => 3], ['B' => 4]]; + self::assertSame($expectedResult, $sheet->getCell('F1')->getCalculatedValue()); + $expectedResult = [ + 2 => ['B' => 10, 'C' => 20, 'D' => null], + ['B' => 2, 'C' => null, 'D' => null], + ['B' => 3, 'C' => null, 'D' => null], + ['B' => 4, 'C' => null, 'D' => null], + ]; + self::assertSame($expectedResult, $sheet->getCell('H1')->getCalculatedValue()); + self::assertSame($expectedResult, $sheet->getCell('F9')->getCalculatedValue()); + self::assertSame('1020234', $sheet->getCell('J9')->getCalculatedValue(), 'Header row not included'); + self::assertSame(39, $sheet->getCell('J11')->getCalculatedValue(), 'Header row not included'); + self::assertSame('F7', $sheet->getSelectedCells()); + self::assertSame('F8', $tableSheet->getSelectedCells()); + self::assertSame($sheet, $spreadsheet->getActiveSheet()); + } } diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/ArrayFunctionsTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/ArrayFunctionsTest.php new file mode 100644 index 000000000..39cc72ff7 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/ArrayFunctionsTest.php @@ -0,0 +1,127 @@ +arrayReturnType = Calculation::getArrayReturnType(); + } + + protected function tearDown(): void + { + Calculation::setArrayReturnType($this->arrayReturnType); + if ($this->outputFile !== '') { + unlink($this->outputFile); + $this->outputFile = ''; + } + } + + public function testArrayOutput(): void + { + Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_ARRAY); + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $columnArray = [ + [41], + [57], + [51], + [54], + [49], + [43], + [35], + [35], + [44], + [47], + [48], + [26], + [57], + [34], + [61], + [34], + [28], + [29], + [41], + ]; + $sheet->fromArray($columnArray, 'A1'); + $sheet->setCellValue('C1', '=UNIQUE(A1:A19)'); + $sheet->setCellValue('D1', '=SORT(A1:A19)'); + $writer = new XlsxWriter($spreadsheet); + $this->outputFile = File::temporaryFilename(); + $writer->save($this->outputFile); + $spreadsheet->disconnectWorksheets(); + + $reader = new XlsxReader(); + $spreadsheet2 = $reader->load($this->outputFile); + $sheet2 = $spreadsheet2->getActiveSheet(); + $expectedUnique = [ + ['41'], + ['57'], + ['51'], + ['54'], + ['49'], + ['43'], + ['35'], + ['44'], + ['47'], + ['48'], + ['26'], + ['34'], + ['61'], + ['28'], + ['29'], + ]; + self::assertCount(15, $expectedUnique); + self::assertSame($expectedUnique, $sheet2->getCell('C1')->getCalculatedValue()); + $expectedSort = [ + [26], + [28], + [29], + [34], + [34], + [35], + [35], + [41], + [41], + [43], + [44], + [47], + [48], + [49], + [51], + [54], + [57], + [57], + [61], + ]; + self::assertCount(19, $expectedSort); + self::assertCount(19, $columnArray); + self::assertSame($expectedSort, $sheet2->getCell('D1')->getCalculatedValue()); + $spreadsheet2->disconnectWorksheets(); + + $file = 'zip://'; + $file .= $this->outputFile; + $file .= '#xl/worksheets/sheet1.xml'; + $data = file_get_contents($file); + if ($data === false) { + self::fail('Unable to read file'); + } else { + self::assertStringContainsString('_xlfn.UNIQUE(A1:A19)', $data, '15 results for UNIQUE'); + self::assertStringContainsString('_xlfn._xlws.SORT(A1:A19)', $data, '19 results for SORT'); + } + } +}