diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 083f50296..d8c2738e5 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -53,24 +53,16 @@ class Xlsx extends BaseReader /** * ReferenceHelper instance. */ - private ReferenceHelper $referenceHelper; + protected ReferenceHelper $referenceHelper; - private ZipArchive $zip; + protected ZipArchive $zip; private Styles $styleReader; /** @var SharedFormula[] */ - private array $sharedFormulae = []; + protected array $sharedFormulae = []; - private bool $parseHuge = false; - - /** - * Use XMLReader-based streaming for cell data parsing. - * When enabled, worksheet cell data is parsed using XMLReader - * instead of SimpleXML, which significantly reduces memory usage - * for large worksheets. - */ - private bool $useStreamingReader = false; + protected bool $parseHuge = false; /** * Allow use of LIBXML_PARSEHUGE. @@ -83,18 +75,6 @@ class Xlsx extends BaseReader $this->parseHuge = $parseHuge; } - public function getUseStreamingReader(): bool - { - return $this->useStreamingReader; - } - - public function setUseStreamingReader(bool $useStreamingReader): self - { - $this->useStreamingReader = $useStreamingReader; - - return $this; - } - /** * Create a new Xlsx Reader instance. */ @@ -339,7 +319,7 @@ class Xlsx extends BaseReader return $worksheetInfo; } - private static function castToBoolean(SimpleXMLElement $c): bool + protected static function castToBoolean(SimpleXMLElement $c): bool { $value = isset($c->v) ? (string) $c->v : null; if ($value == '0') { @@ -351,12 +331,12 @@ class Xlsx extends BaseReader return (bool) $c->v; } - private static function castToError(?SimpleXMLElement $c): ?string + protected static function castToError(?SimpleXMLElement $c): ?string { return isset($c, $c->v) ? (string) $c->v : null; } - private static function castToString(?SimpleXMLElement $c): ?string + protected static function castToString(?SimpleXMLElement $c): ?string { return isset($c, $c->v) ? (string) $c->v : null; } @@ -366,7 +346,7 @@ class Xlsx extends BaseReader return str_replace(['_xlfn.', '_xlws.'], '', $formula); } - private function castToFormula(?SimpleXMLElement $c, string $r, string &$cellDataType, mixed &$value, mixed &$calculatedValue, string $castBaseType, bool $updateSharedCells = true): void + protected function castToFormula(?SimpleXMLElement $c, string $r, string &$cellDataType, mixed &$value, mixed &$calculatedValue, string $castBaseType, bool $updateSharedCells = true): void { if ($c === null) { return; @@ -418,7 +398,7 @@ class Xlsx extends BaseReader return $contents !== false; } - private function getFromZipArchive(ZipArchive $archive, string $fileName = ''): string + protected function getFromZipArchive(ZipArchive $archive, string $fileName = ''): string { // Root-relative paths if (str_contains($fileName, '//')) { @@ -914,193 +894,19 @@ class Xlsx extends BaseReader } $holdSelectedCells = $docSheet->getSelectedCells(); - if ($this->useStreamingReader) { - /** @var array $styles */ - $this->loadSheetDataWithXmlReader( - $docSheet, - "$dir/$fileWorksheet", - $mainNS, - $sharedStrings, - $styles - ); - } elseif ($xmlSheetNS && $xmlSheetNS->sheetData && $xmlSheetNS->sheetData->row) { - $cIndex = 1; // Cell Start from 1 - foreach ($xmlSheetNS->sheetData->row as $row) { - $rowIndex = 1; - foreach ($row->c as $c) { - $cAttr = self::getAttributes($c); - $r = (string) $cAttr['r']; - if ($r == '') { - $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex; - } - $cellDataType = (string) $cAttr['t']; - $originalCellDataTypeNumeric = $cellDataType === ''; - $value = null; - $calculatedValue = null; + /** @var array $styles */ + $this->loadSheetData( + $xmlSheetNS, + $filename, + $dir, + $richData, + $docSheet, + "$dir/$fileWorksheet", + $mainNS, + $sharedStrings, + $styles + ); - // Read cell? - $coordinates = Coordinate::coordinateFromString($r); - - if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) { - // Normally, just testing for the f attribute should identify this cell as containing a formula - // that we need to read, even though it is outside of the filter range, in case it is a shared formula. - // But in some cases, this attribute isn't set; so we need to delve a level deeper and look at - // whether or not the cell has a child formula element that is shared. - if (isset($cAttr->f) || (isset($c->f, $c->f->attributes()['t']) && strtolower((string) $c->f->attributes()['t']) === 'shared')) { - $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError', false); - } - ++$rowIndex; - - continue; - } - - // Read cell! - $useFormula = isset($c->f) - && ((string) $c->f !== '' || (isset($c->f->attributes()['t']) && strtolower((string) $c->f->attributes()['t']) === 'shared')); - switch ($cellDataType) { - case DataType::TYPE_STRING: - if ((string) $c->v != '') { - $value = $sharedStrings[(int) ($c->v)]; - - if ($value instanceof RichText) { - $value = clone $value; - } - } else { - $value = ''; - } - - break; - case DataType::TYPE_BOOL: - if (!$useFormula) { - if (isset($c->v)) { - $value = self::castToBoolean($c); - } else { - $value = null; - $cellDataType = DataType::TYPE_NULL; - } - } else { - // Formula - $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToBoolean'); - self::storeFormulaAttributes($c->f, $docSheet, $r); - } - - break; - case DataType::TYPE_STRING2: - if ($useFormula) { - $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToString'); - self::storeFormulaAttributes($c->f, $docSheet, $r); - } else { - $value = self::castToString($c); - } - - break; - case DataType::TYPE_INLINE: - if ($useFormula) { - $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError'); - self::storeFormulaAttributes($c->f, $docSheet, $r); - } else { - $value = $this->parseRichText($c->is); - } - - break; - case DataType::TYPE_ERROR: - if (isset($cAttr->vm, $richData['image']['rId' . $cAttr->vm]) && !$useFormula) { - $imagePath = $dir . '/' . str_replace('../', '', $richData['image']['rId' . $cAttr->vm]); - $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); - $objDrawing->setPath( - 'zip://' . File::realpath($filename) . '#' . $imagePath, - false, - $zip - ); - - $objDrawing->setCoordinates($r); - $objDrawing->setResizeProportional(false); - $objDrawing->setInCell(true); - $objDrawing->setWorksheet($docSheet); - - $value = $objDrawing; - $cellDataType = DataType::TYPE_DRAWING_IN_CELL; - $c->t = DataType::TYPE_ERROR; - - break; - } - - if (!$useFormula) { - $value = self::castToError($c); - } else { - // Formula - $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError'); - $eattr = $c->attributes(); - if (isset($eattr['vm'])) { - if ($calculatedValue === ExcelError::VALUE()) { - $calculatedValue = ExcelError::SPILL(); - } - } - } - - break; - default: - if (!$useFormula) { - $value = self::castToString($c); - if (is_numeric($value)) { - $value += 0; - $cellDataType = DataType::TYPE_NUMERIC; - } - } else { - // Formula - $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToString'); - if (is_numeric($calculatedValue)) { - $calculatedValue += 0; - } - self::storeFormulaAttributes($c->f, $docSheet, $r); - } - - break; - } - - // read empty cells or the cells are not empty - if ($this->readEmptyCells || ($value !== null && $value !== '')) { - // Rich text? - if ($value instanceof RichText && $this->readDataOnly) { - $value = $value->getPlainText(); - } - - $cell = $docSheet->getCell($r); - // Assign value - if ($cellDataType != '') { - // it is possible, that datatype is numeric but with an empty string, which result in an error - if ($cellDataType === DataType::TYPE_NUMERIC && ($value === '' || $value === null)) { - $cellDataType = DataType::TYPE_NULL; - } - if ($cellDataType !== DataType::TYPE_NULL) { - $cell->setValueExplicit($value, $cellDataType); - } - } else { - $cell->setValue($value); - } - if ($calculatedValue !== null) { - $cell->setCalculatedValue($calculatedValue, $originalCellDataTypeNumeric); - } - - // Style information? - if (!$this->readDataOnly) { - $cAttrS = (int) ($cAttr['s'] ?? 0); - // no style index means 0, it seems - $cAttrS = isset($styles[$cAttrS]) ? $cAttrS : 0; - $cell->setXfIndex($cAttrS); - // issue 3495 - if ($cellDataType === DataType::TYPE_FORMULA && $styles[$cAttrS]->quotePrefix === true) { //* @phpstan-ignore-line - $holdSelected = $docSheet->getSelectedCells(); - $cell->getStyle()->setQuotePrefix(false); - $docSheet->setSelectedCells($holdSelected); - } - } - } - ++$rowIndex; - } - ++$cIndex; - } - } $docSheet->setSelectedCells($holdSelectedCells); if (!$this->readDataOnly && $xmlSheetNS && $xmlSheetNS->ignoredErrors) { foreach ($xmlSheetNS->ignoredErrors->ignoredError as $ignoredError) { @@ -2084,7 +1890,208 @@ class Xlsx extends BaseReader return $excel; } - private function parseRichText(?SimpleXMLElement $is): RichText + /** + * @param string[][] $richData + * @param Worksheet $docSheet the worksheet to populate + * @param string $fileWorksheetPath path to worksheet XML within the zip + * @param string $mainNS the main spreadsheetml namespace + * @param array $sharedStrings shared string table + * @param object[] $styles style objects array + */ + protected function loadSheetData( + ?SimpleXMLElement $xmlSheetNS, + string $filename, + string $dir, + array $richData, + Worksheet $docSheet, + string $fileWorksheetPath, + string $mainNS, + array $sharedStrings, + array $styles, + ): void { + if (!($xmlSheetNS && $xmlSheetNS->sheetData && $xmlSheetNS->sheetData->row)) { + return; // @codeCoverageIgnore + } + + $cIndex = 1; // Cell Start from 1 + foreach ($xmlSheetNS->sheetData->row as $row) { + $rowIndex = 1; + foreach ($row->c as $c) { + $cAttr = self::getAttributes($c); + $r = (string) $cAttr['r']; + if ($r == '') { + $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex; + } + $cellDataType = (string) $cAttr['t']; + $originalCellDataTypeNumeric = $cellDataType === ''; + $value = null; + $calculatedValue = null; + + // Read cell? + $coordinates = Coordinate::coordinateFromString($r); + + if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) { + // Normally, just testing for the f attribute should identify this cell as containing a formula + // that we need to read, even though it is outside of the filter range, in case it is a shared formula. + // But in some cases, this attribute isn't set; so we need to delve a level deeper and look at + // whether or not the cell has a child formula element that is shared. + if (isset($cAttr->f) || (isset($c->f, $c->f->attributes()['t']) && strtolower((string) $c->f->attributes()['t']) === 'shared')) { + $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError', false); + } + ++$rowIndex; + + continue; + } + + // Read cell! + $useFormula = isset($c->f) + && ((string) $c->f !== '' || (isset($c->f->attributes()['t']) && strtolower((string) $c->f->attributes()['t']) === 'shared')); + switch ($cellDataType) { + case DataType::TYPE_STRING: + if ((string) $c->v != '') { + $value = $sharedStrings[(int) ($c->v)]; + + if ($value instanceof RichText) { + $value = clone $value; + } + } else { + $value = ''; + } + + break; + case DataType::TYPE_BOOL: + if (!$useFormula) { + if (isset($c->v)) { + $value = self::castToBoolean($c); + } else { + $value = null; + $cellDataType = DataType::TYPE_NULL; + } + } else { + // Formula + $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToBoolean'); + self::storeFormulaAttributes($c->f, $docSheet, $r); + } + + break; + case DataType::TYPE_STRING2: + if ($useFormula) { + $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToString'); + self::storeFormulaAttributes($c->f, $docSheet, $r); + } else { + $value = self::castToString($c); + } + + break; + case DataType::TYPE_INLINE: + if ($useFormula) { + $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError'); + self::storeFormulaAttributes($c->f, $docSheet, $r); + } else { + $value = $this->parseRichText($c->is); + } + + break; + case DataType::TYPE_ERROR: + if (isset($cAttr->vm, $richData['image']['rId' . $cAttr->vm]) && !$useFormula) { + $imagePath = $dir . '/' . str_replace('../', '', $richData['image']['rId' . $cAttr->vm]); + $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); + $objDrawing->setPath( + 'zip://' . File::realpath($filename) . '#' . $imagePath, + false, + $this->zip + ); + + $objDrawing->setCoordinates($r); + $objDrawing->setResizeProportional(false); + $objDrawing->setInCell(true); + $objDrawing->setWorksheet($docSheet); + + $value = $objDrawing; + $cellDataType = DataType::TYPE_DRAWING_IN_CELL; + $c->t = DataType::TYPE_ERROR; + + break; + } + + if (!$useFormula) { + $value = self::castToError($c); + } else { + // Formula + $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError'); + $eattr = $c->attributes(); + if (isset($eattr['vm'])) { + if ($calculatedValue === ExcelError::VALUE()) { + $calculatedValue = ExcelError::SPILL(); + } + } + } + + break; + default: + if (!$useFormula) { + $value = self::castToString($c); + if (is_numeric($value)) { + $value += 0; + $cellDataType = DataType::TYPE_NUMERIC; + } + } else { + // Formula + $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToString'); + if (is_numeric($calculatedValue)) { + $calculatedValue += 0; + } + self::storeFormulaAttributes($c->f, $docSheet, $r); + } + + break; + } + + // read empty cells or the cells are not empty + if ($this->readEmptyCells || ($value !== null && $value !== '')) { + // Rich text? + if ($value instanceof RichText && $this->readDataOnly) { + $value = $value->getPlainText(); + } + + $cell = $docSheet->getCell($r); + // Assign value + if ($cellDataType != '') { + // it is possible, that datatype is numeric but with an empty string, which result in an error + if ($cellDataType === DataType::TYPE_NUMERIC && ($value === '' || $value === null)) { + $cellDataType = DataType::TYPE_NULL; + } + if ($cellDataType !== DataType::TYPE_NULL) { + $cell->setValueExplicit($value, $cellDataType); + } + } else { + $cell->setValue($value); + } + if ($calculatedValue !== null) { + $cell->setCalculatedValue($calculatedValue, $originalCellDataTypeNumeric); + } + + // Style information? + if (!$this->readDataOnly) { + $cAttrS = (int) ($cAttr['s'] ?? 0); + // no style index means 0, it seems + $cAttrS = isset($styles[$cAttrS]) ? $cAttrS : 0; + $cell->setXfIndex($cAttrS); + // issue 3495 + if ($cellDataType === DataType::TYPE_FORMULA && $styles[$cAttrS]->quotePrefix === true) { //* @phpstan-ignore-line + $holdSelected = $docSheet->getSelectedCells(); + $cell->getStyle()->setQuotePrefix(false); + $docSheet->setSelectedCells($holdSelected); + } + } + } + ++$rowIndex; + } + ++$cIndex; + } + } + + protected function parseRichText(?SimpleXMLElement $is): RichText { $value = new RichText(); @@ -2695,346 +2702,7 @@ class Xlsx extends BaseReader } } - /** - * Load sheet data using XMLReader for memory-efficient streaming. - * - * @param Worksheet $docSheet the worksheet to populate - * @param string $fileWorksheetPath path to worksheet XML within the zip - * @param string $mainNS the main spreadsheetml namespace - * @param array $sharedStrings shared string table - * @param object[] $styles style objects array - */ - private function loadSheetDataWithXmlReader( - Worksheet $docSheet, - string $fileWorksheetPath, - string $mainNS, - array $sharedStrings, - array $styles, - ): void { - $xmlContent = $this->getFromZipArchive($this->zip, $fileWorksheetPath); - if ($xmlContent === '') { - return; // @codeCoverageIgnore - } - - $xml = new XMLReader(); - $xml->xml( - $this->getSecurityScannerOrThrow()->scan($xmlContent), - null, - $this->parseHuge ? LIBXML_PARSEHUGE : 0 - ); - $xml->setParserProperty(XMLReader::SUBST_ENTITIES, true); - - // Free the raw XML string now that XMLReader has consumed it - unset($xmlContent); - - $inSheetData = false; - $cIndex = 1; - - while ($xml->read()) { - // Look for the sheetData element to start processing - if ($xml->localName === 'sheetData' && $xml->namespaceURI === $mainNS) { - if ($xml->nodeType === XMLReader::ELEMENT) { - $inSheetData = true; - if ($xml->isEmptyElement) { - break; // empty sheetData @codeCoverageIgnore - } - } elseif ($xml->nodeType === XMLReader::END_ELEMENT) { - break; // end of sheetData - } - - continue; - } - - if (!$inSheetData) { - continue; - } - - // Process row elements - if ($xml->localName === 'row' && $xml->nodeType === XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) { - $rowIndex = 1; - - if ($xml->isEmptyElement) { // @codeCoverageIgnoreStart - ++$cIndex; - - continue; // @codeCoverageIgnoreEnd - } - - // Read cell elements within this row - while ($xml->read()) { - if ($xml->localName === 'row' && $xml->nodeType === XMLReader::END_ELEMENT) { - break; // end of row - } - - if ($xml->localName === 'c' && $xml->nodeType === XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) { - $r = $xml->getAttribute('r') ?? ''; - if ($r === '') { - $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex; // @codeCoverageIgnore - } - $cellDataType = $xml->getAttribute('t') ?? ''; - $originalCellDataTypeNumeric = $cellDataType === ''; - $styleIndex = (int) ($xml->getAttribute('s') ?? 0); - $value = null; - $calculatedValue = null; - - // Read cell? - $coordinates = Coordinate::coordinateFromString($r); - - // Parse the cell's inner XML using SimpleXML for the element subtree only. - // This keeps memory-efficient row-by-row iteration while reusing - // existing parsing logic for formulas, inline strings, etc. - $cellXml = null; - if (!$xml->isEmptyElement) { - $outerXml = $xml->readOuterXml(); - if ($outerXml !== '') { - // readOuterXml() typically includes inherited namespace declarations. - // If the namespace is missing, wrap the element with it. - if (!str_contains($outerXml, 'xmlns')) { // @codeCoverageIgnoreStart - $outerXml = 'children($mainNS); - } - } - } - - if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) { - // @codeCoverageIgnoreStart - // Handle shared formulas for filtered cells - if ($cellXml !== null && isset($cellXml->f)) { - $fAttrs = $cellXml->f->attributes(); - if (isset($fAttrs['t']) && strtolower((string) $fAttrs['t']) === 'shared') { - $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue, false); - } - } - ++$rowIndex; - - continue; - // @codeCoverageIgnoreEnd - } - - // Determine if cell contains a formula - $useFormula = false; - if ($cellXml !== null && isset($cellXml->f)) { - $fStr = (string) $cellXml->f; - $fAttrs = $cellXml->f->attributes(); - $useFormula = $fStr !== '' || (isset($fAttrs['t']) && strtolower((string) $fAttrs['t']) === 'shared'); - } - - // Get value element text - $vValue = ($cellXml !== null && isset($cellXml->v)) ? (string) $cellXml->v : null; - - switch ($cellDataType) { - case DataType::TYPE_STRING: - // Shared string - if ($vValue !== null && $vValue !== '') { - $ssIndex = (int) $vValue; - $value = $sharedStrings[$ssIndex] ?? ''; - if ($value instanceof RichText) { - $value = clone $value; // @codeCoverageIgnore - } - } else { - $value = ''; // @codeCoverageIgnore - } - - break; - - case DataType::TYPE_BOOL: - if (!$useFormula) { - if ($vValue !== null) { - $value = ($vValue === '1'); - } else { - $value = null; // @codeCoverageIgnore - $cellDataType = DataType::TYPE_NULL; // @codeCoverageIgnore - } - } else { - // @codeCoverageIgnoreStart - $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); - if ($cellXml !== null && isset($cellXml->f)) { - $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); - } - // @codeCoverageIgnoreEnd - } - - break; - - case DataType::TYPE_STRING2: - if ($useFormula) { - $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); - if ($cellXml !== null && isset($cellXml->f)) { - $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); - } - } else { - $value = $vValue; // @codeCoverageIgnore - } - - break; - - case DataType::TYPE_INLINE: - if ($useFormula) { - // @codeCoverageIgnoreStart - $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); - if ($cellXml !== null && isset($cellXml->f)) { - $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); - } - // @codeCoverageIgnoreEnd - } elseif ($cellXml !== null && isset($cellXml->is)) { - $value = $this->parseRichText($cellXml->is); - } - - break; - - case DataType::TYPE_ERROR: - if (!$useFormula) { - $value = $vValue; - } else { - $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); - } - - break; - - default: - // Numeric or untyped - if (!$useFormula) { - $value = $vValue; - if ($value !== null && is_numeric($value)) { - $value += 0; - $cellDataType = DataType::TYPE_NUMERIC; - } - } else { - $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); - if (is_numeric($calculatedValue)) { - $calculatedValue += 0; - } - if ($cellXml !== null && isset($cellXml->f)) { - $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); - } - } - - break; - } - - // Read empty cells or cells that are not empty - if ($this->readEmptyCells || ($value !== null && $value !== '')) { - // Rich text? - if ($value instanceof RichText && $this->readDataOnly) { - $value = $value->getPlainText(); // @codeCoverageIgnore - } - - $cell = $docSheet->getCell($r); - // Assign value - if ($cellDataType !== '') { - if ($cellDataType === DataType::TYPE_NUMERIC && ($value === '' || $value === null)) { - $cellDataType = DataType::TYPE_NULL; // @codeCoverageIgnore - } - if ($cellDataType !== DataType::TYPE_NULL) { - $cell->setValueExplicit($value, $cellDataType); - } - } else { - $cell->setValue($value); - } - if ($calculatedValue !== null) { - $cell->setCalculatedValue($calculatedValue, $originalCellDataTypeNumeric); - } - - // Style information? - if (!$this->readDataOnly) { - $cAttrS = isset($styles[$styleIndex]) ? $styleIndex : 0; - $cell->setXfIndex($cAttrS); - if ($cellDataType === DataType::TYPE_FORMULA && isset($styles[$cAttrS]) && $styles[$cAttrS]->quotePrefix === true) { //* @phpstan-ignore-line - $holdSelected = $docSheet->getSelectedCells(); // @codeCoverageIgnore - $cell->getStyle()->setQuotePrefix(false); // @codeCoverageIgnore - $docSheet->setSelectedCells($holdSelected); // @codeCoverageIgnore - } - } - } - - ++$rowIndex; - } - } - - ++$cIndex; - } - } - - $xml->close(); - } - - /** - * Process a formula from a streaming-parsed cell element. - */ - private function processStreamingFormula( - ?SimpleXMLElement $cellXml, - string $r, - string &$cellDataType, - mixed &$value, - mixed &$calculatedValue, - bool $updateSharedCells = true, - ): void { - if ($cellXml === null || !isset($cellXml->f)) { - return; // @codeCoverageIgnore - } - - $originalDataType = $cellDataType; - $cellDataType = DataType::TYPE_FORMULA; - $formula = self::replacePrefixes((string) $cellXml->f); - $value = "=$formula"; - - // Calculated value from - $calculatedValue = isset($cellXml->v) ? (string) $cellXml->v : null; - if ($calculatedValue !== null && is_numeric($calculatedValue)) { - $calculatedValue += 0; - } - - // Handle TYPE_BOOL calculated values - match castToBoolean behavior - if ($originalDataType === DataType::TYPE_BOOL && $calculatedValue !== null) { - $calculatedValue = (bool) $calculatedValue; - } - - // Shared formula? - $attr = $cellXml->f->attributes(); - if (isset($attr['t']) && strtolower((string) $attr['t']) === 'shared') { - $instance = (string) $attr['si']; - - if (!isset($this->sharedFormulae[$instance])) { - $this->sharedFormulae[$instance] = new SharedFormula($r, $value); - } elseif ($updateSharedCells) { - $master = Coordinate::indexesFromString($this->sharedFormulae[$instance]->master()); - $current = Coordinate::indexesFromString($r); - - $difference = [0, 0]; - $difference[0] = $current[0] - $master[0]; - $difference[1] = $current[1] - $master[1]; - - $value = $this->referenceHelper->updateFormulaReferences( - $this->sharedFormulae[$instance]->formula(), - 'A1', - $difference[0], - $difference[1] - ); - } - } - } - - /** - * Store formula attributes from a streaming-parsed formula element. - */ - private function storeStreamingFormulaAttributes(SimpleXMLElement $f, Worksheet $docSheet, string $r): void - { - $formulaAttributes = []; - $attributes = $f->attributes(); - if (isset($attributes['t'])) { - $formulaAttributes['t'] = (string) $attributes['t']; - } - if (isset($attributes['ref'])) { - $formulaAttributes['ref'] = (string) $attributes['ref']; - } - if (!empty($formulaAttributes)) { - $docSheet->getCell($r)->setFormulaAttributes($formulaAttributes); - } - } - - private static function storeFormulaAttributes(SimpleXMLElement $f, Worksheet $docSheet, string $r): void + protected static function storeFormulaAttributes(SimpleXMLElement $f, Worksheet $docSheet, string $r): void { $formulaAttributes = []; $attributes = $f->attributes(); diff --git a/tests/Benchmark/XlsxStreamingReadClass.php b/tests/Benchmark/XlsxStreamingReadClass.php new file mode 100644 index 000000000..0304a1c03 --- /dev/null +++ b/tests/Benchmark/XlsxStreamingReadClass.php @@ -0,0 +1,360 @@ + $sharedStrings shared string table + * @param object[] $styles style objects array + */ + protected function loadSheetData( + ?SimpleXMLElement $xmlSheetNS, + string $filename, + string $dir, + array $richData, + Worksheet $docSheet, + string $fileWorksheetPath, + string $mainNS, + array $sharedStrings, + array $styles, + ): void { + $xmlContent = $this->getFromZipArchive($this->zip, $fileWorksheetPath); + if ($xmlContent === '') { + return; // @codeCoverageIgnore + } + + $xml = new XMLReader(); + $xml->xml( + $this->getSecurityScannerOrThrow()->scan($xmlContent), + null, + $this->parseHuge ? LIBXML_PARSEHUGE : 0 + ); + $xml->setParserProperty(XMLReader::SUBST_ENTITIES, true); + + // Free the raw XML string now that XMLReader has consumed it + unset($xmlContent); + + $inSheetData = false; + $cIndex = 1; + + while ($xml->read()) { + // Look for the sheetData element to start processing + if ($xml->localName === 'sheetData' && $xml->namespaceURI === $mainNS) { + if ($xml->nodeType === XMLReader::ELEMENT) { + $inSheetData = true; + if ($xml->isEmptyElement) { + break; // empty sheetData @codeCoverageIgnore + } + } elseif ($xml->nodeType === XMLReader::END_ELEMENT) { + break; // end of sheetData + } + + continue; + } + + if (!$inSheetData) { + continue; + } + + // Process row elements + if ($xml->localName === 'row' && $xml->nodeType === XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) { + $rowIndex = 1; + + if ($xml->isEmptyElement) { // @codeCoverageIgnoreStart + ++$cIndex; + + continue; // @codeCoverageIgnoreEnd + } + + // Read cell elements within this row + while ($xml->read()) { + if ($xml->localName === 'row' && $xml->nodeType === XMLReader::END_ELEMENT) { + break; // end of row + } + + if ($xml->localName === 'c' && $xml->nodeType === XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) { + $r = $xml->getAttribute('r') ?? ''; + if ($r === '') { + $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex; // @codeCoverageIgnore + } + $cellDataType = $xml->getAttribute('t') ?? ''; + $originalCellDataTypeNumeric = $cellDataType === ''; + $styleIndex = (int) ($xml->getAttribute('s') ?? 0); + $value = null; + $calculatedValue = null; + + // Read cell? + $coordinates = Coordinate::coordinateFromString($r); + + // Parse the cell's inner XML using SimpleXML for the element subtree only. + // This keeps memory-efficient row-by-row iteration while reusing + // existing parsing logic for formulas, inline strings, etc. + $cellXml = null; + if (!$xml->isEmptyElement) { + $outerXml = $xml->readOuterXml(); + if ($outerXml !== '') { + // readOuterXml() typically includes inherited namespace declarations. + // If the namespace is missing, wrap the element with it. + if (!str_contains($outerXml, 'xmlns')) { // @codeCoverageIgnoreStart + $outerXml = 'children($mainNS); + } + } + } + + if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) { + // @codeCoverageIgnoreStart + // Handle shared formulas for filtered cells + if ($cellXml !== null && isset($cellXml->f)) { + $fAttrs = $cellXml->f->attributes(); + if (isset($fAttrs['t']) && strtolower((string) $fAttrs['t']) === 'shared') { + $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue, false); + } + } + ++$rowIndex; + + continue; + // @codeCoverageIgnoreEnd + } + + // Determine if cell contains a formula + $useFormula = false; + if ($cellXml !== null && isset($cellXml->f)) { + $fStr = (string) $cellXml->f; + $fAttrs = $cellXml->f->attributes(); + $useFormula = $fStr !== '' || (isset($fAttrs['t']) && strtolower((string) $fAttrs['t']) === 'shared'); + } + + // Get value element text + $vValue = ($cellXml !== null && isset($cellXml->v)) ? (string) $cellXml->v : null; + + switch ($cellDataType) { + case DataType::TYPE_STRING: + // Shared string + if ($vValue !== null && $vValue !== '') { + $ssIndex = (int) $vValue; + $value = $sharedStrings[$ssIndex] ?? ''; + if ($value instanceof RichText) { + $value = clone $value; // @codeCoverageIgnore + } + } else { + $value = ''; // @codeCoverageIgnore + } + + break; + + case DataType::TYPE_BOOL: + if (!$useFormula) { + if ($vValue !== null) { + $value = ($vValue === '1'); + } else { + $value = null; // @codeCoverageIgnore + $cellDataType = DataType::TYPE_NULL; // @codeCoverageIgnore + } + } else { + // @codeCoverageIgnoreStart + $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); + if ($cellXml !== null && isset($cellXml->f)) { + $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); + } + // @codeCoverageIgnoreEnd + } + + break; + + case DataType::TYPE_STRING2: + if ($useFormula) { + $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); + if ($cellXml !== null && isset($cellXml->f)) { + $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); + } + } else { + $value = $vValue; // @codeCoverageIgnore + } + + break; + + case DataType::TYPE_INLINE: + if ($useFormula) { + // @codeCoverageIgnoreStart + $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); + if ($cellXml !== null && isset($cellXml->f)) { + $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); + } + // @codeCoverageIgnoreEnd + } elseif ($cellXml !== null && isset($cellXml->is)) { + $value = $this->parseRichText($cellXml->is); + } + + break; + + case DataType::TYPE_ERROR: + if (!$useFormula) { + $value = $vValue; + } else { + $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); + } + + break; + + default: + // Numeric or untyped + if (!$useFormula) { + $value = $vValue; + if ($value !== null && is_numeric($value)) { + $value += 0; + $cellDataType = DataType::TYPE_NUMERIC; + } + } else { + $this->processStreamingFormula($cellXml, $r, $cellDataType, $value, $calculatedValue); + if (is_numeric($calculatedValue)) { + $calculatedValue += 0; + } + if ($cellXml !== null && isset($cellXml->f)) { + $this->storeStreamingFormulaAttributes($cellXml->f, $docSheet, $r); + } + } + + break; + } + + // Read empty cells or cells that are not empty + if ($this->readEmptyCells || ($value !== null && $value !== '')) { + // Rich text? + if ($value instanceof RichText && $this->readDataOnly) { + $value = $value->getPlainText(); // @codeCoverageIgnore + } + + $cell = $docSheet->getCell($r); + // Assign value + if ($cellDataType !== '') { + if ($cellDataType === DataType::TYPE_NUMERIC && ($value === '' || $value === null)) { + $cellDataType = DataType::TYPE_NULL; // @codeCoverageIgnore + } + if ($cellDataType !== DataType::TYPE_NULL) { + $cell->setValueExplicit($value, $cellDataType); + } + } else { + $cell->setValue($value); + } + if ($calculatedValue !== null) { + $cell->setCalculatedValue($calculatedValue, $originalCellDataTypeNumeric); + } + + // Style information? + if (!$this->readDataOnly) { + $cAttrS = isset($styles[$styleIndex]) ? $styleIndex : 0; + $cell->setXfIndex($cAttrS); + if ($cellDataType === DataType::TYPE_FORMULA && isset($styles[$cAttrS]) && $styles[$cAttrS]->quotePrefix === true) { //* @phpstan-ignore-line + $holdSelected = $docSheet->getSelectedCells(); // @codeCoverageIgnore + $cell->getStyle()->setQuotePrefix(false); // @codeCoverageIgnore + $docSheet->setSelectedCells($holdSelected); // @codeCoverageIgnore + } + } + } + + ++$rowIndex; + } + } + + ++$cIndex; + } + } + + $xml->close(); + } + + /** + * Process a formula from a streaming-parsed cell element. + */ + private function processStreamingFormula( + ?SimpleXMLElement $cellXml, + string $r, + string &$cellDataType, + mixed &$value, + mixed &$calculatedValue, + bool $updateSharedCells = true, + ): void { + if ($cellXml === null || !isset($cellXml->f)) { + return; // @codeCoverageIgnore + } + + $originalDataType = $cellDataType; + $cellDataType = DataType::TYPE_FORMULA; + $formula = self::replacePrefixes((string) $cellXml->f); + $value = "=$formula"; + + // Calculated value from + $calculatedValue = isset($cellXml->v) ? (string) $cellXml->v : null; + if ($calculatedValue !== null && is_numeric($calculatedValue)) { + $calculatedValue += 0; + } + + // Handle TYPE_BOOL calculated values - match castToBoolean behavior + if ($originalDataType === DataType::TYPE_BOOL && $calculatedValue !== null) { + $calculatedValue = (bool) $calculatedValue; + } + + // Shared formula? + $attr = $cellXml->f->attributes(); + if (isset($attr['t']) && strtolower((string) $attr['t']) === 'shared') { + $instance = (string) $attr['si']; + + if (!isset($this->sharedFormulae[$instance])) { + $this->sharedFormulae[$instance] = new SharedFormula($r, $value); + } elseif ($updateSharedCells) { + $master = Coordinate::indexesFromString($this->sharedFormulae[$instance]->master()); + $current = Coordinate::indexesFromString($r); + + $difference = [0, 0]; + $difference[0] = $current[0] - $master[0]; + $difference[1] = $current[1] - $master[1]; + + $value = $this->referenceHelper->updateFormulaReferences( + $this->sharedFormulae[$instance]->formula(), + 'A1', + $difference[0], + $difference[1] + ); + } + } + } + + /** + * Store formula attributes from a streaming-parsed formula element. + */ + private function storeStreamingFormulaAttributes(SimpleXMLElement $f, Worksheet $docSheet, string $r): void + { + $formulaAttributes = []; + $attributes = $f->attributes(); + if (isset($attributes['t'])) { + $formulaAttributes['t'] = (string) $attributes['t']; + } + if (isset($attributes['ref'])) { + $formulaAttributes['ref'] = (string) $attributes['ref']; + } + if (!empty($formulaAttributes)) { + $docSheet->getCell($r)->setFormulaAttributes($formulaAttributes); + } + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/StreamingReadTest.php b/tests/Benchmark/XlsxStreamingReadTest.php similarity index 91% rename from tests/PhpSpreadsheetTests/Reader/Xlsx/StreamingReadTest.php rename to tests/Benchmark/XlsxStreamingReadTest.php index eac528150..390a90f9b 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/StreamingReadTest.php +++ b/tests/Benchmark/XlsxStreamingReadTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx; +namespace PhpOffice\PhpSpreadsheetBenchmarks; use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Reader\Xlsx; @@ -10,9 +10,10 @@ use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter; +use PhpOffice\PhpSpreadsheetBenchmarks\XlsxStreamingReadClass as Xlsx2; use PHPUnit\Framework\TestCase; -class StreamingReadTest extends TestCase +class XlsxStreamingReadTest extends TestCase { private string $tempFile = ''; @@ -76,20 +77,6 @@ class StreamingReadTest extends TestCase return $path; } - public function testStreamingFlagDefaultsFalse(): void - { - $reader = new Xlsx(); - self::assertFalse($reader->getUseStreamingReader()); - } - - public function testStreamingFlagSetterGetter(): void - { - $reader = new Xlsx(); - $result = $reader->setUseStreamingReader(true); - self::assertTrue($reader->getUseStreamingReader()); - self::assertSame($reader, $result); // fluent interface - } - public function testStreamingLoadsIdenticalToSimpleXml(): void { // Load with SimpleXML (default) @@ -99,9 +86,8 @@ class StreamingReadTest extends TestCase $sheetSimple = $spreadsheetSimple->getActiveSheet(); // Load with streaming XMLReader - $readerStreaming = new Xlsx(); + $readerStreaming = new Xlsx2(); $readerStreaming->setReadDataOnly(true); - $readerStreaming->setUseStreamingReader(true); $spreadsheetStreaming = $readerStreaming->load($this->tempFile); $sheetStreaming = $spreadsheetStreaming->getActiveSheet(); @@ -126,8 +112,7 @@ class StreamingReadTest extends TestCase public function testStreamingNumericCells(): void { - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -141,8 +126,7 @@ class StreamingReadTest extends TestCase public function testStreamingSharedStrings(): void { - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -156,8 +140,7 @@ class StreamingReadTest extends TestCase public function testStreamingBooleanCells(): void { - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -169,8 +152,7 @@ class StreamingReadTest extends TestCase public function testStreamingFormulaCells(): void { - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -188,8 +170,7 @@ class StreamingReadTest extends TestCase public function testStreamingBooleanFormulaCalculatedValue(): void { - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -209,9 +190,8 @@ class StreamingReadTest extends TestCase $readerSimple->setReadDataOnly(true); $spreadsheetSimple = $readerSimple->load($filename); - $readerStreaming = new Xlsx(); + $readerStreaming = new Xlsx2(); $readerStreaming->setReadDataOnly(true); - $readerStreaming->setUseStreamingReader(true); $spreadsheetStreaming = $readerStreaming->load($filename); foreach ($spreadsheetSimple->getActiveSheet()->getRowIterator() as $row) { @@ -234,8 +214,7 @@ class StreamingReadTest extends TestCase public function testStreamingWithStyles(): void { // Verify streaming mode applies styles correctly - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -248,8 +227,7 @@ class StreamingReadTest extends TestCase public function testStreamingInlineString(): void { - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -286,12 +264,10 @@ class StreamingReadTest extends TestCase // Load with streaming and compare with SimpleXML $readerSimple = new Xlsx(); - $readerSimple->setReadDataOnly(true); $simple = $readerSimple->load($file); - $readerStream = new Xlsx(); + $readerStream = new Xlsx2(); $readerStream->setReadDataOnly(true); - $readerStream->setUseStreamingReader(true); $stream = $readerStream->load($file); foreach (['A1', 'A2', 'A3', 'A4'] as $coord) { @@ -337,8 +313,7 @@ class StreamingReadTest extends TestCase $writer->save($file); $spreadsheet->disconnectWorksheets(); - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $result = $reader->load($file); self::assertSame(3, $result->getSheetCount()); @@ -369,8 +344,7 @@ class StreamingReadTest extends TestCase $writer->save($file); $spreadsheet->disconnectWorksheets(); - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $reader->setReadDataOnly(true); $result = $reader->load($file); $sheet = $result->getActiveSheet(); @@ -407,8 +381,7 @@ class StreamingReadTest extends TestCase $readerSimple = new Xlsx(); $simple = $readerSimple->load($file); - $readerStream = new Xlsx(); - $readerStream->setUseStreamingReader(true); + $readerStream = new Xlsx2(); $stream = $readerStream->load($file); foreach (['A1', 'A2', 'A3'] as $coord) { @@ -443,8 +416,7 @@ class StreamingReadTest extends TestCase $spreadsheet->disconnectWorksheets(); // Load with styles (readDataOnly=false) using streaming - $readerStream = new Xlsx(); - $readerStream->setUseStreamingReader(true); + $readerStream = new Xlsx2(); // readDataOnly defaults to false $stream = $readerStream->load($file); $sheetStream = $stream->getActiveSheet(); @@ -508,9 +480,8 @@ class StreamingReadTest extends TestCase $readerSimple->setReadDataOnly(true); $simple = $readerSimple->load($file); - $readerStream = new Xlsx(); + $readerStream = new Xlsx2(); $readerStream->setReadDataOnly(true); - $readerStream->setUseStreamingReader(true); $stream = $readerStream->load($file); for ($row = 1; $row <= 5; ++$row) { @@ -535,8 +506,7 @@ class StreamingReadTest extends TestCase { // Test the readDataOnly=false path which applies style indices // and handles quotePrefix for formula cells - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); // readDataOnly defaults to false $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -585,9 +555,8 @@ class StreamingReadTest extends TestCase $readerSimple->setReadDataOnly(true); $simple = $readerSimple->load($file); - $readerStream = new Xlsx(); + $readerStream = new Xlsx2(); $readerStream->setReadDataOnly(true); - $readerStream->setUseStreamingReader(true); $stream = $readerStream->load($file); self::assertSame( @@ -602,8 +571,7 @@ class StreamingReadTest extends TestCase public function testStreamingCalculatedValuePreserved(): void { // Test that calculated values are preserved for formula cells - $reader = new Xlsx(); - $reader->setUseStreamingReader(true); + $reader = new Xlsx2(); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); @@ -633,8 +601,7 @@ class StreamingReadTest extends TestCase $readerSimple = new Xlsx(); $simple = $readerSimple->load($this->tempFile); - $readerStream = new Xlsx(); - $readerStream->setUseStreamingReader(true); + $readerStream = new Xlsx2(); $stream = $readerStream->load($this->tempFile); // String formula should match @@ -663,8 +630,7 @@ class StreamingReadTest extends TestCase $simple = $readerSimple->load($filename); $sheetSimple = $simple->getActiveSheet(); - $readerStream = new Xlsx(); - $readerStream->setUseStreamingReader(true); + $readerStream = new Xlsx2(); $stream = $readerStream->load($filename); $sheetStream = $stream->getActiveSheet(); @@ -744,8 +710,7 @@ class StreamingReadTest extends TestCase $simple = $readerSimple->load($file); $sheetSimple = $simple->getActiveSheet(); - $readerStream = new Xlsx(); - $readerStream->setUseStreamingReader(true); + $readerStream = new Xlsx2(); $stream = $readerStream->load($file); $sheetStream = $stream->getActiveSheet(); @@ -774,9 +739,8 @@ class StreamingReadTest extends TestCase public function testParseHugeOption(): void { - $reader = new Xlsx(); + $reader = new Xlsx2(); $reader->setParseHuge(true); - $reader->setUseStreamingReader(true); $spreadsheet = $reader->load($this->tempFile); $sheet = $spreadsheet->getActiveSheet(); diff --git a/tests/Benchmark/XlsxStreamingReaderBenchmark.php b/tests/Benchmark/XlsxStreamingReaderBenchmarkTest.php similarity index 97% rename from tests/Benchmark/XlsxStreamingReaderBenchmark.php rename to tests/Benchmark/XlsxStreamingReaderBenchmarkTest.php index c3a1e8baa..f611ad2f6 100644 --- a/tests/Benchmark/XlsxStreamingReaderBenchmark.php +++ b/tests/Benchmark/XlsxStreamingReaderBenchmarkTest.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter; +use PhpOffice\PhpSpreadsheetBenchmarks\XlsxStreamingReadClass as Xlsx2; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -15,7 +16,7 @@ use PHPUnit\Framework\TestCase; /** * @codeCoverageIgnore */ -class XlsxStreamingReaderBenchmark extends TestCase +class XlsxStreamingReaderBenchmarkTest extends TestCase { private const ROW_COUNT = 2000; @@ -94,7 +95,6 @@ class XlsxStreamingReaderBenchmark extends TestCase $startSimple = hrtime(true); $readerSimple = new Xlsx(); $readerSimple->setReadDataOnly(true); - $readerSimple->setUseStreamingReader(false); $spreadsheetSimple = $readerSimple->load($this->tempFile); $elapsedSimple = (hrtime(true) - $startSimple) / 1e6; // ms $peakSimple = memory_get_peak_usage(true); @@ -117,9 +117,8 @@ class XlsxStreamingReaderBenchmark extends TestCase $peakBeforeStreaming = memory_get_peak_usage(true); $startStreaming = hrtime(true); - $readerStreaming = new Xlsx(); + $readerStreaming = new Xlsx2(); $readerStreaming->setReadDataOnly(true); - $readerStreaming->setUseStreamingReader(true); $spreadsheetStreaming = $readerStreaming->load($this->tempFile); $elapsedStreaming = (hrtime(true) - $startStreaming) / 1e6; // ms $peakStreaming = memory_get_peak_usage(true);