EOD; /** @var \Box\Spout\Writer\Common\Sheet The "external" sheet */ protected $externalSheet; /** @var string Path to the XML file that will contain the sheet data */ protected $worksheetFilePath; /** @var \Box\Spout\Writer\XLSX\Helper\SharedStringsHelper Helper to write shared strings */ protected $sharedStringsHelper; /** @var \Box\Spout\Writer\XLSX\Helper\StyleHelper Helper to work with styles */ protected $styleHelper; /** @var bool Whether inline or shared strings should be used */ protected $shouldUseInlineStrings; /** @var \Box\Spout\Common\Escaper\XLSX Strings escaper */ protected $stringsEscaper; /** @var Resource Pointer to the sheet data file (e.g. xl/worksheets/sheet1.xml) */ protected $sheetFilePointer; /** @var int Index of the last written row */ protected $lastWrittenRowIndex = 0; /** * @param \Box\Spout\Writer\Common\Sheet $externalSheet The associated "external" sheet * @param string $worksheetFilesFolder Temporary folder where the files to create the XLSX will be stored * @param \Box\Spout\Writer\XLSX\Helper\SharedStringsHelper $sharedStringsHelper Helper for shared strings * @param \Box\Spout\Writer\XLSX\Helper\StyleHelper Helper to work with styles * @param bool $shouldUseInlineStrings Whether inline or shared strings should be used * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing */ public function __construct($externalSheet, $worksheetFilesFolder, $sharedStringsHelper, $styleHelper, $shouldUseInlineStrings) { $this->externalSheet = $externalSheet; $this->sharedStringsHelper = $sharedStringsHelper; $this->styleHelper = $styleHelper; $this->shouldUseInlineStrings = $shouldUseInlineStrings; /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */ $this->stringsEscaper = \Box\Spout\Common\Escaper\XLSX::getInstance(); $this->worksheetFilePath = $worksheetFilesFolder . '/' . strtolower($this->externalSheet->getName()) . '.xml'; $this->startSheet(); } /** * Prepares the worksheet to accept data * * @return void * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing */ protected function startSheet() { $this->sheetFilePointer = fopen($this->worksheetFilePath, 'w'); $this->throwIfSheetFilePointerIsNotAvailable(); fwrite($this->sheetFilePointer, self::SHEET_XML_FILE_HEADER); fwrite($this->sheetFilePointer, ''); } /** * Checks if the book has been created. Throws an exception if not created yet. * * @return void * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing */ protected function throwIfSheetFilePointerIsNotAvailable() { if (!$this->sheetFilePointer) { throw new IOException('Unable to open sheet for writing.'); } } /** * @return \Box\Spout\Writer\Common\Sheet The "external" sheet */ public function getExternalSheet() { return $this->externalSheet; } /** * @return int The index of the last written row */ public function getLastWrittenRowIndex() { return $this->lastWrittenRowIndex; } /** * @return int The ID of the worksheet */ public function getId() { // sheet index is zero-based, while ID is 1-based return $this->externalSheet->getIndex() + 1; } /** * Adds data to the worksheet. * * @param array $dataRow Array containing data to be written. Cannot be empty. * Example $dataRow = ['data1', 1234, null, '', 'data5']; * @param \Box\Spout\Writer\Style\Style $style Style to be applied to the row. NULL means use default style. * @return void * @throws \Box\Spout\Common\Exception\IOException If the data cannot be written * @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported */ public function addRow($dataRow, $style) { if (!$this->isEmptyRow($dataRow)) { $this->addNonEmptyRow($dataRow, $style); } $this->lastWrittenRowIndex++; } /** * Returns whether the given row is empty * * @param array $dataRow Array containing data to be written. Cannot be empty. * Example $dataRow = ['data1', 1234, null, '', 'data5']; * @return bool Whether the given row is empty */ private function isEmptyRow($dataRow) { $numCells = count($dataRow); return ($numCells === 1 && CellHelper::isEmpty($dataRow[0])); } /** * Adds non empty row to the worksheet. * * @param array $dataRow Array containing data to be written. Cannot be empty. * Example $dataRow = ['data1', 1234, null, '', 'data5']; * @param \Box\Spout\Writer\Style\Style $style Style to be applied to the row. NULL means use default style. * @return void * @throws \Box\Spout\Common\Exception\IOException If the data cannot be written * @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported */ private function addNonEmptyRow($dataRow, $style) { $cellNumber = 0; $rowIndex = $this->lastWrittenRowIndex + 1; $numCells = count($dataRow); $rowXML = ''; foreach($dataRow as $cellValue) { $rowXML .= $this->getCellXML($rowIndex, $cellNumber, $cellValue, $style->getId()); $cellNumber++; } $rowXML .= ''; $wasWriteSuccessful = fwrite($this->sheetFilePointer, $rowXML); if ($wasWriteSuccessful === false) { throw new IOException("Unable to write data in {$this->worksheetFilePath}"); } } /** * Build and return xml for a single cell. * * @param int $rowIndex * @param int $cellNumber * @param mixed $cellValue * @param int $styleId * @return string * @throws InvalidArgumentException */ private function getCellXML($rowIndex, $cellNumber, $cellValue, $styleId) { $columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber); $cellXML = 'stringsEscaper->escape($matches[1]); $text = $this->stringsEscaper->escape($matches[2]); $formula = sprintf('HYPERLINK("%s","%s")', $url, $text); $cellXML = sprintf( '%s%s', $columnIndex, $rowIndex, $formula, $text); } else { // CFDB EDIT END if ($this->shouldUseInlineStrings) { $cellXML .= ' t="inlineStr">' . $this->stringsEscaper->escape($cellValue) . ''; } else { $sharedStringId = $this->sharedStringsHelper->writeString($cellValue); $cellXML .= ' t="s">' . $sharedStringId . ''; } } // CFDB EDIT this line } else if (CellHelper::isBoolean($cellValue)) { $cellXML .= ' t="b">' . intval($cellValue) . ''; } else if (CellHelper::isNumeric($cellValue)) { $cellXML .= '>' . $cellValue . ''; } else if (empty($cellValue)) { if ($this->styleHelper->shouldApplyStyleOnEmptyCell($styleId)) { $cellXML .= '/>'; } else { // don't write empty cells that do no need styling // NOTE: not appending to $cellXML is the right behavior!! $cellXML = ''; } } else { throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cellValue)); } return $cellXML; } /** * Closes the worksheet * * @return void */ public function close() { if (!is_resource($this->sheetFilePointer)) { return; } fwrite($this->sheetFilePointer, ''); fwrite($this->sheetFilePointer, ''); fclose($this->sheetFilePointer); } }