mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-31 12:40:00 +00:00
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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user