OwlCyberSecurity - MANAGER
Edit File: SheetHelper.php
<?php namespace Box\Spout\Reader\XLSX\Helper; use Box\Spout\Reader\Wrapper\XMLReader; use Box\Spout\Reader\XLSX\Sheet; /** * Class SheetHelper * This class provides helper functions related to XLSX sheets * * @package Box\Spout\Reader\XLSX\Helper */ class SheetHelper { /** Paths of XML files relative to the XLSX file root */ const WORKBOOK_XML_RELS_FILE_PATH = 'xl/_rels/workbook.xml.rels'; const WORKBOOK_XML_FILE_PATH = 'xl/workbook.xml'; /** Definition of XML node names used to parse data */ const XML_NODE_WORKBOOK_VIEW = 'workbookView'; const XML_NODE_SHEET = 'sheet'; const XML_NODE_SHEETS = 'sheets'; const XML_NODE_RELATIONSHIP = 'Relationship'; /** Definition of XML attributes used to parse data */ const XML_ATTRIBUTE_ACTIVE_TAB = 'activeTab'; const XML_ATTRIBUTE_R_ID = 'r:id'; const XML_ATTRIBUTE_NAME = 'name'; const XML_ATTRIBUTE_ID = 'Id'; const XML_ATTRIBUTE_TARGET = 'Target'; /** @var string Path of the XLSX file being read */ protected $filePath; /** @var \Box\Spout\Reader\XLSX\ReaderOptions Reader's current options */ protected $options; /** @var \Box\Spout\Reader\XLSX\Helper\SharedStringsHelper Helper to work with shared strings */ protected $sharedStringsHelper; /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */ protected $globalFunctionsHelper; /** * @param string $filePath Path of the XLSX file being read * @param \Box\Spout\Reader\XLSX\ReaderOptions $options Reader's current options * @param \Box\Spout\Reader\XLSX\Helper\SharedStringsHelper Helper to work with shared strings * @param \Box\Spout\Common\Helper\GlobalFunctionsHelper $globalFunctionsHelper */ public function __construct($filePath, $options, $sharedStringsHelper, $globalFunctionsHelper) { $this->filePath = $filePath; $this->options = $options; $this->sharedStringsHelper = $sharedStringsHelper; $this->globalFunctionsHelper = $globalFunctionsHelper; } /** * Returns the sheets metadata of the file located at the previously given file path. * The paths to the sheets' data are read from the [Content_Types].xml file. * * @return Sheet[] Sheets within the XLSX file */ public function getSheets() { $sheets = []; $sheetIndex = 0; $activeSheetIndex = 0; // By default, the first sheet is active $xmlReader = new XMLReader(); if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_XML_FILE_PATH)) { while ($xmlReader->read()) { if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_WORKBOOK_VIEW)) { // The "workbookView" node is located before "sheet" nodes, ensuring that // the active sheet is known before parsing sheets data. $activeSheetIndex = (int) $xmlReader->getAttribute(self::XML_ATTRIBUTE_ACTIVE_TAB); } else if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_SHEET)) { $isSheetActive = ($sheetIndex === $activeSheetIndex); $sheets[] = $this->getSheetFromSheetXMLNode($xmlReader, $sheetIndex, $isSheetActive); $sheetIndex++; } else if ($xmlReader->isPositionedOnEndingNode(self::XML_NODE_SHEETS)) { // stop reading once all sheets have been read break; } } $xmlReader->close(); } return $sheets; } /** * Returns an instance of a sheet, given the XML node describing the sheet - from "workbook.xml". * We can find the XML file path describing the sheet inside "workbook.xml.res", by mapping with the sheet ID * ("r:id" in "workbook.xml", "Id" in "workbook.xml.res"). * * @param \Box\Spout\Reader\Wrapper\XMLReader $xmlReaderOnSheetNode XML Reader instance, pointing on the node describing the sheet, as defined in "workbook.xml" * @param int $sheetIndexZeroBased Index of the sheet, based on order of appearance in the workbook (zero-based) * @param bool $isSheetActive Whether this sheet was defined as active * @return \Box\Spout\Reader\XLSX\Sheet Sheet instance */ protected function getSheetFromSheetXMLNode($xmlReaderOnSheetNode, $sheetIndexZeroBased, $isSheetActive) { $sheetId = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_R_ID); $escapedSheetName = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_NAME); /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */ $escaper = \Box\Spout\Common\Escaper\XLSX::getInstance(); $sheetName = $escaper->unescape($escapedSheetName); $sheetDataXMLFilePath = $this->getSheetDataXMLFilePathForSheetId($sheetId); return new Sheet( $this->filePath, $sheetDataXMLFilePath, $sheetIndexZeroBased, $sheetName, $isSheetActive, $this->options, $this->sharedStringsHelper ); } /** * @param string $sheetId The sheet ID, as defined in "workbook.xml" * @return string The XML file path describing the sheet inside "workbook.xml.res", for the given sheet ID */ protected function getSheetDataXMLFilePathForSheetId($sheetId) { $sheetDataXMLFilePath = ''; // find the file path of the sheet, by looking at the "workbook.xml.res" file $xmlReader = new XMLReader(); if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_XML_RELS_FILE_PATH)) { while ($xmlReader->read()) { if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_RELATIONSHIP)) { $relationshipSheetId = $xmlReader->getAttribute(self::XML_ATTRIBUTE_ID); if ($relationshipSheetId === $sheetId) { // In workbook.xml.rels, it is only "worksheets/sheet1.xml" // In [Content_Types].xml, the path is "/xl/worksheets/sheet1.xml" $sheetDataXMLFilePath = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TARGET); // sometimes, the sheet data file path already contains "/xl/"... if (strpos($sheetDataXMLFilePath, '/xl/') !== 0) { $sheetDataXMLFilePath = '/xl/' . $sheetDataXMLFilePath; break; } } } } $xmlReader->close(); } return $sheetDataXMLFilePath; } }