Avoid Allocating RowDimension Unneccesarily (#3686)

* Avoid Allocating RowDimension Unneccesarily

This PR builds on PR #3527, introduced but subsequently closed by @goetas. The observation was that AutoFilter did not need to allocate a new RowDimension when the row was not to be filtered. While vetting the PR, it became apparent that Xlsx Writer also allocates RowDimension unnecessarily, with some minor adjustments possible for Ods and Mpdf as well. My tests confirm the initial observation that there can be a considerable memory savings when RowDimension is allocated only when needed. So, even though the original PR is withdrawn, there seems to be value in proceeding with it anyhow.

* Add Some Tests
This commit is contained in:
oleibman
2023-09-02 06:37:29 -07:00
committed by GitHub
parent 67b6082049
commit 06c4aff394
8 changed files with 83 additions and 5 deletions
+5 -1
View File
@@ -1049,7 +1049,11 @@ class AutoFilter
}
}
// Set show/hide for the row based on the result of the autoFilter result
$this->workSheet->getRowDimension((int) $row)->setVisible($result);
// If the RowDimension object has not been allocated yet and the row should be visible,
// then we can avoid any operation since the rows are visible by default (saves a lot of memory)
if ($result === false || $this->workSheet->rowDimensionExists((int) $row)) {
$this->workSheet->getRowDimension((int) $row)->setVisible($result);
}
}
$this->evaluated = true;
@@ -3875,4 +3875,9 @@ class Worksheet implements IComparable
{
return preg_match(self::SHEET_NAME_REQUIRES_NO_QUOTES, $sheetName) !== 1;
}
public function isRowVisible(int $row): bool
{
return !$this->rowDimensionExists($row) || $this->getRowDimension($row)->getVisible();
}
}
+1 -1
View File
@@ -504,7 +504,7 @@ class Html extends BaseWriter
$html .= $startTag;
// Write row if there are HTML table cells in it
$mpdfInvisible = $this->isMPdf && $sheet->getRowDimension($row)->getVisible() === false;
$mpdfInvisible = $this->isMPdf && !$sheet->isRowVisible($row);
if (!$mpdfInvisible && !isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) {
// Start a new rowData
$rowData = [];
+1 -1
View File
@@ -171,7 +171,7 @@ class Content extends WriterPart
$objWriter->endElement();
$span_row = 0;
} else {
if ($sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
if ($sheet->rowDimensionExists($row->getRowIndex()) && $sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
$objWriter->writeAttribute(
'table:style-name',
sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
+3 -1
View File
@@ -13,6 +13,7 @@ use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormattingRuleExtension;
use PhpOffice\PhpSpreadsheet\Worksheet\RowDimension;
use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as PhpspreadsheetWorksheet;
@@ -1143,11 +1144,12 @@ class Worksheet extends WriterPart
}
$currentRow = 0;
$emptyDimension = new RowDimension();
while ($currentRow++ < $highestRow) {
$isRowSet = isset($cellsByRow[$currentRow]);
if ($isRowSet || $worksheet->rowDimensionExists($currentRow)) {
// Get row dimension
$rowDimension = $worksheet->getRowDimension($currentRow);
$rowDimension = $worksheet->rowDimensionExists($currentRow) ? $worksheet->getRowDimension($currentRow) : $emptyDimension;
// Write current row?
$writeCurrentRow = $isRowSet || $rowDimension->getRowHeight() >= 0 || $rowDimension->getVisible() === false || $rowDimension->getCollapsed() === true || $rowDimension->getOutlineLevel() > 0 || $rowDimension->getXfIndex() !== null;
@@ -95,6 +95,7 @@ class AutoFilterCustomNumericTest extends SetupTeardown
public function testEqualsList(): void
{
$sheet = $this->initSheet();
$sheet->getRowDimension(4)->setRowHeight(25);
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
@@ -115,6 +116,17 @@ class AutoFilterCustomNumericTest extends SetupTeardown
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals([3, 4, 9, 10], $this->getVisible());
self::assertTrue($sheet->rowDimensionExists(2));
self::assertFalse($sheet->rowDimensionExists(3), 'row visible by default');
self::assertTrue($sheet->rowDimensionExists(4), 'row is visible but height has been set');
self::assertTrue($sheet->rowDimensionExists(5));
self::assertTrue($sheet->rowDimensionExists(6));
self::assertTrue($sheet->rowDimensionExists(7));
self::assertTrue($sheet->rowDimensionExists(8));
self::assertFalse($sheet->rowDimensionExists(9), 'row visible by default');
self::assertFalse($sheet->rowDimensionExists(10), 'row visible by default');
self::assertTrue($sheet->rowDimensionExists(11));
self::assertTrue($sheet->rowDimensionExists(12));
}
public function testNotEqualsList(): void
@@ -62,7 +62,7 @@ class SetupTeardown extends TestCase
$sheet->getAutoFilter()->showHideRows();
$actualVisible = [];
for ($row = 2; $row <= $this->maxRow; ++$row) {
if ($sheet->getRowDimension($row)->getVisible()) {
if ($sheet->isRowVisible($row)) {
$actualVisible[] = $row;
}
}
@@ -0,0 +1,55 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class RowDimensionSaveTest extends AbstractFunctional
{
/**
* @dataProvider typeProvider
*/
public function testSaveNoAllocateRowDimension(string $type): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('A1')->setValue(1);
$sheet->getCell('A2')->setValue(2);
$sheet->getCell('A3')->setValue(3);
$sheet->getRowDimension(2)->setVisible(false);
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $type);
self::assertFalse($sheet->rowDimensionExists(1));
self::assertTrue($sheet->rowDimensionExists(2));
self::assertFalse($sheet->rowDimensionExists(3));
$spreadsheet->disconnectWorksheets();
$reloadedSpreadsheet->disconnectWorksheets();
}
public static function typeProvider(): array
{
return [
['Xlsx'],
['Xls'],
['Ods'],
['Html'],
];
}
public function testSaveNoAllocateRowDimensionMpdf(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('A1')->setValue(1);
$sheet->getCell('A2')->setValue(2);
$sheet->getCell('A3')->setValue(3);
$sheet->getRowDimension(2)->setVisible(false);
$writer = new Mpdf($spreadsheet);
$writer->generateHtmlAll();
self::assertFalse($sheet->rowDimensionExists(1));
self::assertTrue($sheet->rowDimensionExists(2));
self::assertFalse($sheet->rowDimensionExists(3));
$spreadsheet->disconnectWorksheets();
}
}