Refactor writers for better DI (#427)

This commit is a big refactor that improves the code organization.
It focuses on how dependencies are injected into the different classes. This is now done via some factories.

Also, the code is now built around entities (data model that only exposes getters and setters), managers (used to manage an entity) and helpers (used by the managers to perform some specific tasks).

The refactoring is not fully complete, as some dependencies are still hidden...
This commit is contained in:
Adrien Loison 2017-05-29 22:18:40 +02:00 committed by GitHub
parent a366d0d0af
commit bc17311f5f
37 changed files with 1529 additions and 1031 deletions

View File

@ -11,7 +11,7 @@ use Box\Spout\Common\Exception\IOException;
*
* @package Box\Spout\Common\Helper
*/
class FileSystemHelper
class FileSystemHelper implements FileSystemHelperInterface
{
/** @var string Real path of the base folder where all the I/O can occur */
protected $baseFolderRealPath;

View File

@ -0,0 +1,53 @@
<?php
namespace Box\Spout\Common\Helper;
/**
* Class FileSystemHelperInterface
* This interface describes helper functions to help with the file system operations
* like files/folders creation & deletion
*
* @package Box\Spout\Common\Helper
*/
interface FileSystemHelperInterface
{
/**
* Creates an empty folder with the given name under the given parent folder.
*
* @param string $parentFolderPath The parent folder path under which the folder is going to be created
* @param string $folderName The name of the folder to create
* @return string Path of the created folder
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or if the folder path is not inside of the base folder
*/
public function createFolder($parentFolderPath, $folderName);
/**
* Creates a file with the given name and content in the given folder.
* The parent folder must exist.
*
* @param string $parentFolderPath The parent folder path where the file is going to be created
* @param string $fileName The name of the file to create
* @param string $fileContents The contents of the file to create
* @return string Path of the created file
* @throws \Box\Spout\Common\Exception\IOException If unable to create the file or if the file path is not inside of the base folder
*/
public function createFileWithContents($parentFolderPath, $fileName, $fileContents);
/**
* Delete the file at the given path
*
* @param string $filePath Path of the file to delete
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the file path is not inside of the base folder
*/
public function deleteFile($filePath);
/**
* Delete the folder at the given path as well as all its contents
*
* @param string $folderPath Path of the folder to delete
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the folder path is not inside of the base folder
*/
public function deleteFolderRecursively($folderPath);
}

View File

@ -1,117 +0,0 @@
<?php
namespace Box\Spout\Writer;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Exception\WriterNotOpenedException;
/**
* Class AbstractMultiSheetsWriter
*
* @package Box\Spout\Writer
* @abstract
*/
abstract class AbstractMultiSheetsWriter extends AbstractWriter
{
/**
* @return Common\Internal\WorkbookInterface The workbook representing the file to be written
*/
abstract protected function getWorkbook();
/**
* Sets whether new sheets should be automatically created when the max rows limit per sheet is reached.
* This must be set before opening the writer.
*
* @api
* @param bool $shouldCreateNewSheetsAutomatically Whether new sheets should be automatically created when the max rows limit per sheet is reached
* @return AbstractMultiSheetsWriter
* @throws \Box\Spout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
*/
public function setShouldCreateNewSheetsAutomatically($shouldCreateNewSheetsAutomatically)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, $shouldCreateNewSheetsAutomatically);
return $this;
}
/**
* Returns all the workbook's sheets
*
* @api
* @return Common\Sheet[] All the workbook's sheets
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
*/
public function getSheets()
{
$this->throwIfBookIsNotAvailable();
$externalSheets = [];
$worksheets = $this->getWorkbook()->getWorksheets();
/** @var Common\Internal\WorksheetInterface $worksheet */
foreach ($worksheets as $worksheet) {
$externalSheets[] = $worksheet->getExternalSheet();
}
return $externalSheets;
}
/**
* Creates a new sheet and make it the current sheet. The data will now be written to this sheet.
*
* @api
* @return Common\Sheet The created sheet
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
*/
public function addNewSheetAndMakeItCurrent()
{
$this->throwIfBookIsNotAvailable();
$worksheet = $this->getWorkbook()->addNewSheetAndMakeItCurrent();
return $worksheet->getExternalSheet();
}
/**
* Returns the current sheet
*
* @api
* @return Common\Sheet The current sheet
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
*/
public function getCurrentSheet()
{
$this->throwIfBookIsNotAvailable();
return $this->getWorkbook()->getCurrentWorksheet()->getExternalSheet();
}
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @api
* @param Common\Sheet $sheet The sheet to set as current
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @throws \Box\Spout\Writer\Exception\SheetNotFoundException If the given sheet does not exist in the workbook
*/
public function setCurrentSheet($sheet)
{
$this->throwIfBookIsNotAvailable();
$this->getWorkbook()->setCurrentSheet($sheet);
}
/**
* Checks if the book has been created. Throws an exception if not created yet.
*
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
*/
protected function throwIfBookIsNotAvailable()
{
if (!$this->getWorkbook()) {
throw new WriterNotOpenedException('The writer must be opened before performing this action.');
}
}
}

View File

@ -2,7 +2,7 @@
namespace Box\Spout\Writer\CSV;
use Box\Spout\Writer\AbstractWriter;
use Box\Spout\Writer\WriterAbstract;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Common\Helper\EncodingHelper;
use Box\Spout\Writer\Common\Options;
@ -13,7 +13,7 @@ use Box\Spout\Writer\Common\Options;
*
* @package Box\Spout\Writer\CSV
*/
class Writer extends AbstractWriter
class Writer extends WriterAbstract
{
/** Number of rows to write before flushing */
const FLUSH_THRESHOLD = 500;

View File

@ -0,0 +1,28 @@
<?php
namespace Box\Spout\Writer\Common\Helper;
use \Box\Spout\Common\Helper\FileSystemHelperInterface;
/**
* Class FileSystemHelperInterface
* This interface describes helper functions to help with the file system operations
* like files/folders creation & deletion
*
* @package Box\Spout\Writer\Common\Helper
*/
interface FileSystemWithRootFolderHelperInterface extends FileSystemHelperInterface
{
/**
* Creates all the folders needed to create a spreadsheet, as well as the files that won't change.
*
* @return void
* @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the base folders
*/
public function createBaseFilesAndFolders();
/**
* @return string
*/
public function getRootFolder();
}

View File

@ -3,12 +3,12 @@
namespace Box\Spout\Writer\Common\Helper;
/**
* Class AbstractStyleHelper
* Class StyleHelperAbstract
* This class provides helper functions to manage styles
*
* @package Box\Spout\Writer\Common\Helper
*/
abstract class AbstractStyleHelper
abstract class StyleHelperAbstract implements StyleHelperInterface
{
/** @var array [SERIALIZED_STYLE] => [STYLE_ID] mapping table, keeping track of the registered styles */
protected $serializedStyleToStyleIdMappingTable = [];

View File

@ -0,0 +1,30 @@
<?php
namespace Box\Spout\Writer\Common\Helper;
/**
* Interface StyleHelperInterface
*
* @package Box\Spout\Writer\Common\Helper
*/
interface StyleHelperInterface
{
/**
* Registers the given style as a used style.
* Duplicate styles won't be registered more than once.
*
* @param \Box\Spout\Writer\Style\Style $style The style to be registered
* @return \Box\Spout\Writer\Style\Style The registered style, updated with an internal ID.
*/
public function registerStyle($style);
/**
* Apply additional styles if the given row needs it.
* Typically, set "wrap text" if a cell contains a new line.
*
* @param \Box\Spout\Writer\Style\Style $style The original style
* @param array $dataRow The row the style will be applied to
* @return \Box\Spout\Writer\Style\Style The updated style
*/
public function applyExtraStylesIfNeeded($style, $dataRow);
}

View File

@ -1,193 +0,0 @@
<?php
namespace Box\Spout\Writer\Common\Internal;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Exception\SheetNotFoundException;
/**
* Class Workbook
* Represents a workbook within a spreadsheet file.
* It provides the functions to work with worksheets.
*
* @package Box\Spout\Writer\Common
*/
abstract class AbstractWorkbook implements WorkbookInterface
{
/** @var \Box\Spout\Writer\Common\Manager\OptionsManagerInterface $optionsManager */
protected $optionManager;
/** @var string Timestamp based unique ID identifying the workbook */
protected $internalId;
/** @var WorksheetInterface[] Array containing the workbook's sheets */
protected $worksheets = [];
/** @var WorksheetInterface The worksheet where data will be written to */
protected $currentWorksheet;
/**
* @param \Box\Spout\Writer\Common\Manager\OptionsManagerInterface $optionsManager
* @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the base folders
*/
public function __construct(OptionsManagerInterface $optionsManager)
{
$this->optionManager = $optionsManager;
$this->internalId = uniqid();
}
/**
* @return \Box\Spout\Writer\Common\Helper\AbstractStyleHelper The specific style helper
*/
abstract protected function getStyleHelper();
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
abstract protected function getMaxRowsPerWorksheet();
/**
* Creates a new sheet in the workbook. The current sheet remains unchanged.
*
* @return WorksheetInterface The created sheet
* @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
*/
abstract public function addNewSheet();
/**
* Creates a new sheet in the workbook and make it the current sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @return WorksheetInterface The created sheet
* @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
*/
public function addNewSheetAndMakeItCurrent()
{
$worksheet = $this->addNewSheet();
$this->setCurrentWorksheet($worksheet);
return $worksheet;
}
/**
* @return WorksheetInterface[] All the workbook's sheets
*/
public function getWorksheets()
{
return $this->worksheets;
}
/**
* Returns the current sheet
*
* @return WorksheetInterface The current sheet
*/
public function getCurrentWorksheet()
{
return $this->currentWorksheet;
}
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @param \Box\Spout\Writer\Common\Sheet $sheet The "external" sheet to set as current
* @return void
* @throws \Box\Spout\Writer\Exception\SheetNotFoundException If the given sheet does not exist in the workbook
*/
public function setCurrentSheet($sheet)
{
$worksheet = $this->getWorksheetFromExternalSheet($sheet);
if ($worksheet !== null) {
$this->currentWorksheet = $worksheet;
} else {
throw new SheetNotFoundException('The given sheet does not exist in the workbook.');
}
}
/**
* @param WorksheetInterface $worksheet
* @return void
*/
protected function setCurrentWorksheet($worksheet)
{
$this->currentWorksheet = $worksheet;
}
/**
* Returns the worksheet associated to the given external sheet.
*
* @param \Box\Spout\Writer\Common\Sheet $sheet
* @return WorksheetInterface|null The worksheet associated to the given external sheet or null if not found.
*/
protected function getWorksheetFromExternalSheet($sheet)
{
$worksheetFound = null;
foreach ($this->worksheets as $worksheet) {
if ($worksheet->getExternalSheet() === $sheet) {
$worksheetFound = $worksheet;
break;
}
}
return $worksheetFound;
}
/**
* Adds data to the current sheet.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @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.
* @return void
* @throws \Box\Spout\Common\Exception\IOException If trying to create a new sheet and unable to open the sheet for writing
* @throws \Box\Spout\Writer\Exception\WriterException If unable to write data
*/
public function addRowToCurrentWorksheet($dataRow, $style)
{
$currentWorksheet = $this->getCurrentWorksheet();
$hasReachedMaxRows = $this->hasCurrentWorkseetReachedMaxRows();
$styleHelper = $this->getStyleHelper();
// if we reached the maximum number of rows for the current sheet...
if ($hasReachedMaxRows) {
// ... continue writing in a new sheet if option set
if ($this->optionManager->getOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY)) {
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
$updatedStyle = $styleHelper->applyExtraStylesIfNeeded($style, $dataRow);
$registeredStyle = $styleHelper->registerStyle($updatedStyle);
$currentWorksheet->addRow($dataRow, $registeredStyle);
} else {
// otherwise, do nothing as the data won't be read anyways
}
} else {
$updatedStyle = $styleHelper->applyExtraStylesIfNeeded($style, $dataRow);
$registeredStyle = $styleHelper->registerStyle($updatedStyle);
$currentWorksheet->addRow($dataRow, $registeredStyle);
}
}
/**
* @return bool Whether the current worksheet has reached the maximum number of rows per sheet.
*/
protected function hasCurrentWorkseetReachedMaxRows()
{
$currentWorksheet = $this->getCurrentWorksheet();
return ($currentWorksheet->getLastWrittenRowIndex() >= $this->getMaxRowsPerWorksheet());
}
/**
* Closes the workbook and all its associated sheets.
* All the necessary files are written to disk and zipped together to create the ODS file.
* All the temporary files are then deleted.
*
* @param resource $finalFilePointer Pointer to the ODS that will be created
* @return void
*/
abstract public function close($finalFilePointer);
}

View File

@ -1,74 +0,0 @@
<?php
namespace Box\Spout\Writer\Common\Internal;
/**
* Interface WorkbookInterface
*
* @package Box\Spout\Writer\Common\Internal
*/
interface WorkbookInterface
{
/**
* Creates a new sheet in the workbook. The current sheet remains unchanged.
*
* @return WorksheetInterface The created sheet
* @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
*/
public function addNewSheet();
/**
* Creates a new sheet in the workbook and make it the current sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @return WorksheetInterface The created sheet
* @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
*/
public function addNewSheetAndMakeItCurrent();
/**
* @return WorksheetInterface[] All the workbook's sheets
*/
public function getWorksheets();
/**
* Returns the current sheet
*
* @return WorksheetInterface The current sheet
*/
public function getCurrentWorksheet();
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @param \Box\Spout\Writer\Common\Sheet $sheet The "external" sheet to set as current
* @return void
* @throws \Box\Spout\Writer\Exception\SheetNotFoundException If the given sheet does not exist in the workbook
*/
public function setCurrentSheet($sheet);
/**
* Adds data to the current sheet.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param \Box\Spout\Writer\Style\Style $style Style to be applied to the row.
* @return void
* @throws \Box\Spout\Common\Exception\IOException If trying to create a new sheet and unable to open the sheet for writing
* @throws \Box\Spout\Writer\Exception\WriterException If unable to write data
*/
public function addRowToCurrentWorksheet($dataRow, $style);
/**
* Closes the workbook and all its associated sheets.
* All the necessary files are written to disk and zipped together to create the ODS file.
* All the temporary files are then deleted.
*
* @param resource $finalFilePointer Pointer to the ODS that will be created
* @return void
*/
public function close($finalFilePointer);
}

View File

@ -1,40 +0,0 @@
<?php
namespace Box\Spout\Writer\Common\Internal;
/**
* Interface WorksheetInterface
*
* @package Box\Spout\Writer\Common\Internal
*/
interface WorksheetInterface
{
/**
* @return \Box\Spout\Writer\Common\Sheet The "external" sheet
*/
public function getExternalSheet();
/**
* @return int The index of the last written row
*/
public function getLastWrittenRowIndex();
/**
* Adds data to the worksheet.
*
* @param array $dataRow Array containing data to be written.
* 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);
/**
* Closes the worksheet
*
* @return void
*/
public function close();
}

View File

@ -0,0 +1,50 @@
<?php
namespace Box\Spout\Writer\Entity;
/**
* Class Workbook
* Entity describing a workbook
*
* @package Box\Spout\Writer\Entity
*/
class Workbook
{
/** @var Worksheet[] List of the workbook's sheets */
private $worksheets = [];
/** @var string Timestamp based unique ID identifying the workbook */
private $internalId;
/**
* Workbook constructor.
*/
public function __construct()
{
$this->internalId = uniqid();
}
/**
* @return Worksheet[]
*/
public function getWorksheets()
{
return $this->worksheets;
}
/**
* @param Worksheet[] $worksheets
*/
public function setWorksheets($worksheets)
{
$this->worksheets = $worksheets;
}
/**
* @return string
*/
public function getInternalId()
{
return $this->internalId;
}
}

View File

@ -0,0 +1,117 @@
<?php
namespace Box\Spout\Writer\Entity;
use Box\Spout\Writer\Common\Sheet;
/**
* Class Worksheet
* Entity describing a Worksheet
*
* @package Box\Spout\Writer\Entity
*/
class Worksheet
{
/** @var string Path to the XML file that will contain the sheet data */
private $filePath;
/** @var Resource Pointer to the sheet data file (e.g. xl/worksheets/sheet1.xml) */
private $filePointer;
/** @var Sheet The "external" sheet */
private $externalSheet;
/** @var int Maximum number of columns among all the written rows */
private $maxNumColumns;
/** @var int Index of the last written row */
private $lastWrittenRowIndex;
/**
* Worksheet constructor.
*
* @param string $worksheetFilePath
* @param Sheet $externalSheet
*/
public function __construct($worksheetFilePath, Sheet $externalSheet)
{
$this->filePath = $worksheetFilePath;
$this->filePointer = null;
$this->externalSheet = $externalSheet;
$this->maxNumColumns = 0;
$this->lastWrittenRowIndex = 0;
}
/**
* @return string
*/
public function getFilePath()
{
return $this->filePath;
}
/**
* @return Resource
*/
public function getFilePointer()
{
return $this->filePointer;
}
/**
* @param Resource $filePointer
*/
public function setFilePointer($filePointer)
{
$this->filePointer = $filePointer;
}
/**
* @return Sheet
*/
public function getExternalSheet()
{
return $this->externalSheet;
}
/**
* @return int
*/
public function getMaxNumColumns()
{
return $this->maxNumColumns;
}
/**
* @param int $maxNumColumns
*/
public function setMaxNumColumns($maxNumColumns)
{
$this->maxNumColumns = $maxNumColumns;
}
/**
* @return int
*/
public function getLastWrittenRowIndex()
{
return $this->lastWrittenRowIndex;
}
/**
* @param int $lastWrittenRowIndex
*/
public function setLastWrittenRowIndex($lastWrittenRowIndex)
{
$this->lastWrittenRowIndex = $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;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Box\Spout\Writer\Factory;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Entity\Workbook;
use Box\Spout\Writer\Entity\Worksheet;
/**
* Class EntityFactory
* Entity factory
*
* @package Box\Spout\Writer\Factory
*/
class EntityFactory
{
/**
* @return Workbook
*/
public function createWorkbook()
{
return new Workbook();
}
/**
* @param string $worksheetFilePath
* @param Sheet $externalSheet
* @return Worksheet
*/
public function createWorksheet($worksheetFilePath, Sheet $externalSheet)
{
return new Worksheet($worksheetFilePath, $externalSheet);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Box\Spout\Writer\Factory;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Manager\WorkbookManagerInterface;
/**
* Interface GeneralFactoryInterface
*
* @package Box\Spout\Writer\Factory
*/
interface InternalFactoryInterface
{
/**
* @param OptionsManagerInterface $optionsManager
* @return WorkbookManagerInterface
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager);
}

View File

@ -0,0 +1,316 @@
<?php
namespace Box\Spout\Writer\Manager;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface;
use Box\Spout\Writer\Common\Helper\StyleHelperInterface;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Entity\Workbook;
use Box\Spout\Writer\Entity\Worksheet;
use Box\Spout\Writer\Exception\SheetNotFoundException;
use Box\Spout\Writer\Exception\WriterException;
use Box\Spout\Writer\Factory\EntityFactory;
use Box\Spout\Writer\Style\Style;
/**
* Class WorkbookManagerAbstract
* Abstract workbook manager, providing the generic interfaces to work with workbook.
*
* @package Box\Spout\Writer\Manager
*/
abstract class WorkbookManagerAbstract implements WorkbookManagerInterface
{
/** @var Workbook The workbook to manage */
protected $workbook;
/** @var OptionsManagerInterface */
protected $optionManager;
/** @var WorksheetManagerInterface */
protected $worksheetManager;
/** @var StyleHelperInterface */
protected $styleHelper;
/** @var FileSystemWithRootFolderHelperInterface Helper to perform file system operations */
protected $fileSystemHelper;
/** @var EntityFactory Factory to create entities */
private $entityFactory;
/** @var Worksheet The worksheet where data will be written to */
protected $currentWorksheet;
/**
* @param Workbook $workbook
* @param OptionsManagerInterface $optionsManager
* @param WorksheetManagerInterface $worksheetManager
* @param StyleHelperInterface $styleHelper
* @param FileSystemWithRootFolderHelperInterface $fileSystemHelper
* @param EntityFactory $entityFactory
*/
public function __construct(
Workbook $workbook,
OptionsManagerInterface $optionsManager,
WorksheetManagerInterface $worksheetManager,
StyleHelperInterface $styleHelper,
FileSystemWithRootFolderHelperInterface $fileSystemHelper,
EntityFactory $entityFactory)
{
$this->workbook = $workbook;
$this->optionManager = $optionsManager;
$this->worksheetManager = $worksheetManager;
$this->styleHelper = $styleHelper;
$this->fileSystemHelper = $fileSystemHelper;
$this->entityFactory = $entityFactory;
}
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
abstract protected function getMaxRowsPerWorksheet();
/**
* @param Sheet $sheet
* @return string The file path where the data for the given sheet will be stored
*/
abstract protected function getWorksheetFilePath(Sheet $sheet);
/**
* @return Workbook
*/
public function getWorkbook()
{
return $this->workbook;
}
/**
* Creates a new sheet in the workbook and make it the current sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @return Worksheet The created sheet
* @throws IOException If unable to open the sheet for writing
*/
public function addNewSheetAndMakeItCurrent()
{
$worksheet = $this->addNewSheet();
$this->setCurrentWorksheet($worksheet);
return $worksheet;
}
/**
* Creates a new sheet in the workbook. The current sheet remains unchanged.
*
* @return Worksheet The created sheet
* @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
*/
private function addNewSheet()
{
$worksheets = $this->getWorksheets();
$newSheetIndex = count($worksheets);
$sheet = new Sheet($newSheetIndex, $this->workbook->getInternalId());
$worksheetFilePath = $this->getWorksheetFilePath($sheet);
$worksheet = $this->entityFactory->createWorksheet($worksheetFilePath, $sheet);
$this->worksheetManager->startSheet($worksheet);
$worksheets[] = $worksheet;
$this->workbook->setWorksheets($worksheets);
return $worksheet;
}
/**
* @return Worksheet[] All the workbook's sheets
*/
public function getWorksheets()
{
return $this->workbook->getWorksheets();
}
/**
* Returns the current sheet
*
* @return Worksheet The current sheet
*/
public function getCurrentWorksheet()
{
return $this->currentWorksheet;
}
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @param Sheet $sheet The "external" sheet to set as current
* @return void
* @throws SheetNotFoundException If the given sheet does not exist in the workbook
*/
public function setCurrentSheet(Sheet $sheet)
{
$worksheet = $this->getWorksheetFromExternalSheet($sheet);
if ($worksheet !== null) {
$this->currentWorksheet = $worksheet;
} else {
throw new SheetNotFoundException('The given sheet does not exist in the workbook.');
}
}
/**
* @param Worksheet $worksheet
* @return void
*/
private function setCurrentWorksheet($worksheet)
{
$this->currentWorksheet = $worksheet;
}
/**
* Returns the worksheet associated to the given external sheet.
*
* @param Sheet $sheet
* @return Worksheet|null The worksheet associated to the given external sheet or null if not found.
*/
private function getWorksheetFromExternalSheet($sheet)
{
$worksheetFound = null;
foreach ($this->getWorksheets() as $worksheet) {
if ($worksheet->getExternalSheet() === $sheet) {
$worksheetFound = $worksheet;
break;
}
}
return $worksheetFound;
}
/**
* Adds data to the current sheet.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param array $dataRow Array containing data to be written. Cannot be empty.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param Style $style Style to be applied to the row.
* @return void
* @throws IOException If trying to create a new sheet and unable to open the sheet for writing
* @throws WriterException If unable to write data
*/
public function addRowToCurrentWorksheet($dataRow, Style $style)
{
$currentWorksheet = $this->getCurrentWorksheet();
$hasReachedMaxRows = $this->hasCurrentWorkseetReachedMaxRows();
// if we reached the maximum number of rows for the current sheet...
if ($hasReachedMaxRows) {
// ... continue writing in a new sheet if option set
if ($this->optionManager->getOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY)) {
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
$this->addRowWithStyleToWorksheet($currentWorksheet, $dataRow, $style);
} else {
// otherwise, do nothing as the data won't be written anyways
}
} else {
$this->addRowWithStyleToWorksheet($currentWorksheet, $dataRow, $style);
}
}
/**
* @return bool Whether the current worksheet has reached the maximum number of rows per sheet.
*/
private function hasCurrentWorkseetReachedMaxRows()
{
$currentWorksheet = $this->getCurrentWorksheet();
return ($currentWorksheet->getLastWrittenRowIndex() >= $this->getMaxRowsPerWorksheet());
}
/**
* Adds data with the given style to the given sheet.
*
* @param Worksheet $worksheet Worksheet to write the row to
* @param array $dataRow Array containing data to be written. Cannot be empty.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param Style $style Style to be applied to the row.
* @return void
* @throws WriterException If unable to write data
*/
private function addRowWithStyleToWorksheet(Worksheet $worksheet, $dataRow, Style $style)
{
$updatedStyle = $this->styleHelper->applyExtraStylesIfNeeded($style, $dataRow);
$registeredStyle = $this->styleHelper->registerStyle($updatedStyle);
$this->worksheetManager->addRow($worksheet, $dataRow, $registeredStyle);
// update max num columns for the worksheet
$currentMaxNumColumns = $worksheet->getMaxNumColumns();
$cellsCount = count($dataRow);
$worksheet->setMaxNumColumns(max($currentMaxNumColumns, $cellsCount));
}
/**
* Closes the workbook and all its associated sheets.
* All the necessary files are written to disk and zipped together to create the final file.
* All the temporary files are then deleted.
*
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
* @return void
*/
public function close($finalFilePointer)
{
$this->closeAllWorksheets();
$this->closeRemainingObjects();
$this->writeAllFilesToDiskAndZipThem($finalFilePointer);
$this->cleanupTempFolder();
}
/**
* Closes custom objects that are still opened
*
* @return void
*/
protected function closeRemainingObjects()
{
// do nothing by default
}
/**
* Writes all the necessary files to disk and zip them together to create the final file.
*
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
* @return void
*/
abstract protected function writeAllFilesToDiskAndZipThem($finalFilePointer);
/**
* Closes all workbook's associated sheets.
*
* @return void
*/
private function closeAllWorksheets()
{
$worksheets = $this->getWorksheets();
foreach ($worksheets as $worksheet) {
$this->worksheetManager->close($worksheet);
}
}
/**
* Deletes the root folder created in the temp folder and all its contents.
*
* @return void
*/
protected function cleanupTempFolder()
{
$rootFolder = $this->fileSystemHelper->getRootFolder();
$this->fileSystemHelper->deleteFolderRecursively($rootFolder);
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace Box\Spout\Writer\Manager;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Entity\Workbook;
use Box\Spout\Writer\Entity\Worksheet;
use Box\Spout\Writer\Exception\SheetNotFoundException;
use Box\Spout\Writer\Exception\WriterException;
use Box\Spout\Writer\Style\Style;
/**
* Interface WorkbookManagerInterface
* workbook manager interface, providing the generic interfaces to work with workbook.
*
* @package Box\Spout\Writer\Manager
*/
interface WorkbookManagerInterface
{
/**
* @return Workbook
*/
public function getWorkbook();
/**
* Creates a new sheet in the workbook and make it the current sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @return Worksheet The created sheet
* @throws IOException If unable to open the sheet for writing
*/
public function addNewSheetAndMakeItCurrent();
/**
* @return Worksheet[] All the workbook's sheets
*/
public function getWorksheets();
/**
* Returns the current sheet
*
* @return Worksheet The current sheet
*/
public function getCurrentWorksheet();
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @param Sheet $sheet The "external" sheet to set as current
* @return void
* @throws SheetNotFoundException If the given sheet does not exist in the workbook
*/
public function setCurrentSheet(Sheet $sheet);
/**
* Adds data to the current sheet.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param array $dataRow Array containing data to be written. Cannot be empty.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param Style $style Style to be applied to the row.
* @return void
* @throws IOException If trying to create a new sheet and unable to open the sheet for writing
* @throws WriterException If unable to write data
*/
public function addRowToCurrentWorksheet($dataRow, Style $style);
/**
* Closes the workbook and all its associated sheets.
* All the necessary files are written to disk and zipped together to create the final file.
* All the temporary files are then deleted.
*
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
* @return void
*/
public function close($finalFilePointer);
}

View File

@ -0,0 +1,45 @@
<?php
namespace Box\Spout\Writer\Manager;
use Box\Spout\Writer\Entity\Worksheet;
use Box\Spout\Writer\Style\Style;
/**
* Interface WorksheetManagerInterface
* Inteface for worksheet managers, providing the generic interfaces to work with worksheets.
*
* @package Box\Spout\Writer\Manager
*/
interface WorksheetManagerInterface
{
/**
* Adds data to the worksheet.
*
* @param Worksheet $worksheet The worksheet to add the row to
* @param array $dataRow Array containing data to be written. Cannot be empty.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param Style $rowStyle 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(Worksheet $worksheet, $dataRow, $rowStyle);
/**
* Prepares the worksheet to accept data
*
* @param Worksheet $worksheet The worksheet to start
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
*/
public function startSheet(Worksheet $worksheet);
/**
* Closes the worksheet
*
* @param Worksheet $worksheet
* @return void
*/
public function close(Worksheet $worksheet);
}

View File

@ -0,0 +1,101 @@
<?php
namespace Box\Spout\Writer\ODS\Factory;
use Box\Spout\Common\Helper\StringHelper;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Factory\EntityFactory;
use Box\Spout\Writer\Factory\InternalFactoryInterface;
use Box\Spout\Writer\ODS\Helper\FileSystemHelper;
use Box\Spout\Writer\ODS\Helper\StyleHelper;
use Box\Spout\Writer\ODS\Manager\WorkbookManager;
use Box\Spout\Writer\ODS\Manager\WorksheetManager;
use \Box\Spout\Common\Escaper;
/**
* Class InternalFactory
* Factory for all useful types of objects needed by the ODS Writer
*
* @package Box\Spout\Writer\ODS\Factory
*/
class InternalFactory implements InternalFactoryInterface
{
/** @var EntityFactory */
private $entityFactory;
/**
* InternalFactory constructor.
*
* @param EntityFactory $entityFactory
*/
public function __construct(EntityFactory $entityFactory)
{
$this->entityFactory = $entityFactory;
}
/**
* @param OptionsManagerInterface $optionsManager
* @return WorkbookManager
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
{
$workbook = $this->entityFactory->createWorkbook();
$fileSystemHelper = $this->createFileSystemHelper($optionsManager);
$fileSystemHelper->createBaseFilesAndFolders();
$styleHelper = $this->createStyleHelper($optionsManager);
$worksheetManager = $this->createWorksheetManager($styleHelper);
return new WorkbookManager($workbook, $optionsManager, $worksheetManager, $styleHelper, $fileSystemHelper, $this->entityFactory);
}
/**
* @param StyleHelper $styleHelper
* @return WorksheetManager
*/
private function createWorksheetManager(StyleHelper $styleHelper)
{
$stringsEscaper = $this->createStringsEscaper();
$stringsHelper = $this->createStringHelper();
return new WorksheetManager($styleHelper, $stringsEscaper, $stringsHelper);
}
/**
* @param OptionsManagerInterface $optionsManager
* @return FileSystemHelper
*/
public function createFileSystemHelper(OptionsManagerInterface $optionsManager)
{
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
return new FileSystemHelper($tempFolder);
}
/**
* @param OptionsManagerInterface $optionsManager
* @return StyleHelper
*/
private function createStyleHelper(OptionsManagerInterface $optionsManager)
{
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
return new StyleHelper($defaultRowStyle);
}
/**
* @return Escaper\ODS
*/
private function createStringsEscaper()
{
return Escaper\ODS::getInstance();
}
/**
* @return StringHelper
*/
private function createStringHelper()
{
return new StringHelper();
}
}

View File

@ -2,8 +2,10 @@
namespace Box\Spout\Writer\ODS\Helper;
use Box\Spout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface;
use Box\Spout\Writer\Common\Helper\ZipHelper;
use Box\Spout\Writer\ODS\Internal\Worksheet;
use Box\Spout\Writer\Entity\Worksheet;
use Box\Spout\Writer\ODS\Manager\WorksheetManager;
/**
* Class FileSystemHelper
@ -12,7 +14,7 @@ use Box\Spout\Writer\ODS\Internal\Worksheet;
*
* @package Box\Spout\Writer\ODS\Helper
*/
class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper implements FileSystemWithRootFolderHelperInterface
{
const APP_NAME = 'Spout';
const MIMETYPE = 'application/vnd.oasis.opendocument.spreadsheet';
@ -172,11 +174,12 @@ EOD;
/**
* Creates the "content.xml" file under the root folder
*
* @param WorksheetManager $worksheetManager
* @param Worksheet[] $worksheets
* @param StyleHelper $styleHelper
* @return FileSystemHelper
*/
public function createContentFile($worksheets, $styleHelper)
public function createContentFile($worksheetManager, $worksheets, $styleHelper)
{
$contentXmlFileContents = <<<EOD
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@ -196,9 +199,9 @@ EOD;
foreach ($worksheets as $worksheet) {
// write the "<table:table>" node, with the final sheet's name
fwrite($contentXmlHandle, $worksheet->getTableElementStartAsString());
fwrite($contentXmlHandle, $worksheetManager->getTableElementStartAsString($worksheet));
$worksheetFilePath = $worksheet->getWorksheetFilePath();
$worksheetFilePath = $worksheet->getFilePath();
$this->copyFileContentsToTarget($worksheetFilePath, $contentXmlHandle);
fwrite($contentXmlHandle, '</table:table>');

View File

@ -2,7 +2,7 @@
namespace Box\Spout\Writer\ODS\Helper;
use Box\Spout\Writer\Common\Helper\AbstractStyleHelper;
use Box\Spout\Writer\Common\Helper\StyleHelperAbstract;
use Box\Spout\Writer\Style\BorderPart;
/**
@ -11,7 +11,7 @@ use Box\Spout\Writer\Style\BorderPart;
*
* @package Box\Spout\Writer\ODS\Helper
*/
class StyleHelper extends AbstractStyleHelper
class StyleHelper extends StyleHelperAbstract
{
/** @var string[] [FONT_NAME] => [] Map whose keys contain all the fonts used */
protected $usedFontsSet = [];

View File

@ -1,121 +0,0 @@
<?php
namespace Box\Spout\Writer\ODS\Internal;
use Box\Spout\Writer\Common\Internal\AbstractWorkbook;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\ODS\Helper\FileSystemHelper;
use Box\Spout\Writer\ODS\Helper\StyleHelper;
use Box\Spout\Writer\Common\Sheet;
/**
* Class Workbook
* Represents a workbook within a ODS file.
* It provides the functions to work with worksheets.
*
* @package Box\Spout\Writer\ODS\Internal
*/
class Workbook extends AbstractWorkbook
{
/**
* Maximum number of rows a ODS sheet can contain
* @see https://ask.libreoffice.org/en/question/8631/upper-limit-to-number-of-rows-in-calc/
*/
protected static $maxRowsPerWorksheet = 1048576;
/** @var \Box\Spout\Writer\ODS\Helper\FileSystemHelper Helper to perform file system operations */
protected $fileSystemHelper;
/** @var \Box\Spout\Writer\ODS\Helper\StyleHelper Helper to apply styles */
protected $styleHelper;
/**
* @param \Box\Spout\Writer\Common\Manager\OptionsManagerInterface $optionsManager Options manager
* @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the base folders
*/
public function __construct(OptionsManagerInterface $optionsManager)
{
parent::__construct($optionsManager);
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
$this->fileSystemHelper = new FileSystemHelper($tempFolder);
$this->fileSystemHelper->createBaseFilesAndFolders();
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
$this->styleHelper = new StyleHelper($defaultRowStyle);
}
/**
* @return \Box\Spout\Writer\ODS\Helper\StyleHelper Helper to apply styles to ODS files
*/
protected function getStyleHelper()
{
return $this->styleHelper;
}
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
protected function getMaxRowsPerWorksheet()
{
return self::$maxRowsPerWorksheet;
}
/**
* Creates a new sheet in the workbook. The current sheet remains unchanged.
*
* @return Worksheet The created sheet
* @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
*/
public function addNewSheet()
{
$newSheetIndex = count($this->worksheets);
$sheet = new Sheet($newSheetIndex, $this->internalId);
$sheetsContentTempFolder = $this->fileSystemHelper->getSheetsContentTempFolder();
$worksheet = new Worksheet($sheet, $sheetsContentTempFolder);
$this->worksheets[] = $worksheet;
return $worksheet;
}
/**
* Closes the workbook and all its associated sheets.
* All the necessary files are written to disk and zipped together to create the ODS file.
* All the temporary files are then deleted.
*
* @param resource $finalFilePointer Pointer to the ODS that will be created
* @return void
*/
public function close($finalFilePointer)
{
/** @var Worksheet[] $worksheets */
$worksheets = $this->worksheets;
$numWorksheets = count($worksheets);
foreach ($worksheets as $worksheet) {
$worksheet->close();
}
// Finish creating all the necessary files before zipping everything together
$this->fileSystemHelper
->createContentFile($worksheets, $this->styleHelper)
->deleteWorksheetTempFolder()
->createStylesFile($this->styleHelper, $numWorksheets)
->zipRootFolderAndCopyToStream($finalFilePointer);
$this->cleanupTempFolder();
}
/**
* Deletes the root folder created in the temp folder and all its contents.
*
* @return void
*/
protected function cleanupTempFolder()
{
$xlsxRootFolder = $this->fileSystemHelper->getRootFolder();
$this->fileSystemHelper->deleteFolderRecursively($xlsxRootFolder);
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace Box\Spout\Writer\ODS\Manager;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Manager\WorkbookManagerAbstract;
use Box\Spout\Writer\ODS\Helper\FileSystemHelper;
use Box\Spout\Writer\ODS\Helper\StyleHelper;
/**
* Class WorkbookManager
* ODS workbook manager, providing the interfaces to work with workbook.
*
* @package Box\Spout\Writer\ODS\Manager
*/
class WorkbookManager extends WorkbookManagerAbstract
{
/**
* Maximum number of rows a ODS sheet can contain
* @see https://ask.libreoffice.org/en/question/8631/upper-limit-to-number-of-rows-in-calc/
*/
protected static $maxRowsPerWorksheet = 1048576;
/** @var WorksheetManager Object used to manage worksheets */
protected $worksheetManager;
/** @var FileSystemHelper Helper to perform file system operations */
protected $fileSystemHelper;
/** @var StyleHelper Helper to apply styles */
protected $styleHelper;
/** @var int Maximum number of columns among all the written rows */
protected $maxNumColumns = 1;
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
protected function getMaxRowsPerWorksheet()
{
return self::$maxRowsPerWorksheet;
}
/**
* @param Sheet $sheet
* @return string The file path where the data for the given sheet will be stored
*/
public function getWorksheetFilePath(Sheet $sheet)
{
$sheetsContentTempFolder = $this->fileSystemHelper->getSheetsContentTempFolder();
return $sheetsContentTempFolder . '/sheet' . $sheet->getIndex() . '.xml';
}
/**
* Writes all the necessary files to disk and zip them together to create the final file.
*
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
* @return void
*/
protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
{
$worksheets = $this->getWorksheets();
$numWorksheets = count($worksheets);
$this->fileSystemHelper
->createContentFile($this->worksheetManager, $worksheets, $this->styleHelper)
->deleteWorksheetTempFolder()
->createStylesFile($this->styleHelper, $numWorksheets)
->zipRootFolderAndCopyToStream($finalFilePointer);
}
}

View File

@ -1,147 +1,116 @@
<?php
namespace Box\Spout\Writer\ODS\Internal;
namespace Box\Spout\Writer\ODS\Manager;
use Box\Spout\Common\Exception\InvalidArgumentException;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Common\Helper\StringHelper;
use Box\Spout\Writer\Common\Cell;
use Box\Spout\Writer\Common\Helper\CellHelper;
use Box\Spout\Writer\Common\Internal\WorksheetInterface;
use Box\Spout\Writer\Entity\Worksheet;
use Box\Spout\Writer\Manager\WorksheetManagerInterface;
use Box\Spout\Writer\ODS\Helper\StyleHelper;
use Box\Spout\Writer\Style\Style;
/**
* Class Worksheet
* Represents a worksheet within a ODS file. The difference with the Sheet object is
* that this class provides an interface to write data
* Class WorksheetManager
* ODS worksheet manager, providing the interfaces to work with ODS worksheets.
*
* @package Box\Spout\Writer\ODS\Internal
* @package Box\Spout\Writer\ODS\Manager
*/
class Worksheet implements WorksheetInterface
class WorksheetManager implements WorksheetManagerInterface
{
/** @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 StyleHelper Helper to work with styles */
private $styleHelper;
/** @var \Box\Spout\Common\Escaper\ODS Strings escaper */
protected $stringsEscaper;
private $stringsEscaper;
/** @var \Box\Spout\Common\Helper\StringHelper To help with string manipulation */
protected $stringHelper;
/** @var Resource Pointer to the temporary sheet data file (e.g. worksheets-temp/sheet1.xml) */
protected $sheetFilePointer;
/** @var int Maximum number of columns among all the written rows */
protected $maxNumColumns = 1;
/** @var int Index of the last written row */
protected $lastWrittenRowIndex = 0;
/** @var StringHelper String helper */
private $stringHelper;
/**
* @param \Box\Spout\Writer\Common\Sheet $externalSheet The associated "external" sheet
* @param string $worksheetFilesFolder Temporary folder where the files to create the ODS will be stored
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
* WorksheetManager constructor.
*
* @param StyleHelper $styleHelper
* @param \Box\Spout\Common\Escaper\ODS $stringsEscaper
* @param StringHelper $stringHelper
*/
public function __construct($externalSheet, $worksheetFilesFolder)
public function __construct(
StyleHelper $styleHelper,
\Box\Spout\Common\Escaper\ODS $stringsEscaper,
StringHelper $stringHelper)
{
$this->externalSheet = $externalSheet;
/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
$this->stringsEscaper = \Box\Spout\Common\Escaper\ODS::getInstance();
$this->worksheetFilePath = $worksheetFilesFolder . '/sheet' . $externalSheet->getIndex() . '.xml';
$this->stringHelper = new StringHelper();
$this->startSheet();
$this->styleHelper = $styleHelper;
$this->stringsEscaper = $stringsEscaper;
$this->stringHelper = $stringHelper;
}
/**
* Prepares the worksheet to accept data
* The XML file does not contain the "<table:table>" node as it contains the sheet's name
* which may change during the execution of the program. It will be added at the end.
*
* @param Worksheet $worksheet The worksheet to start
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
*/
protected function startSheet()
public function startSheet(Worksheet $worksheet)
{
$this->sheetFilePointer = fopen($this->worksheetFilePath, 'w');
$this->throwIfSheetFilePointerIsNotAvailable();
$sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
$worksheet->setFilePointer($sheetFilePointer);
}
/**
* Checks if the book has been created. Throws an exception if not created yet.
* Checks if the sheet has been sucessfully created. Throws an exception if not.
*
* @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
* @throws IOException If the sheet data file cannot be opened for writing
*/
protected function throwIfSheetFilePointerIsNotAvailable()
private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
{
if (!$this->sheetFilePointer) {
if (!$sheetFilePointer) {
throw new IOException('Unable to open sheet for writing.');
}
}
/**
* @return string Path to the temporary sheet content XML file
*/
public function getWorksheetFilePath()
{
return $this->worksheetFilePath;
}
/**
* Returns the table XML root node as string.
*
* @param Worksheet $worksheet
* @return string <table> node as string
*/
public function getTableElementStartAsString()
public function getTableElementStartAsString(Worksheet $worksheet)
{
$escapedSheetName = $this->stringsEscaper->escape($this->externalSheet->getName());
$tableStyleName = 'ta' . ($this->externalSheet->getIndex() + 1);
$externalSheet = $worksheet->getExternalSheet();
$escapedSheetName = $this->stringsEscaper->escape($externalSheet->getName());
$tableStyleName = 'ta' . ($externalSheet->getIndex() + 1);
$tableElement = '<table:table table:style-name="' . $tableStyleName . '" table:name="' . $escapedSheetName . '">';
$tableElement .= '<table:table-column table:default-cell-style-name="ce1" table:style-name="co1" table:number-columns-repeated="' . $this->maxNumColumns . '"/>';
$tableElement .= '<table:table-column table:default-cell-style-name="ce1" table:style-name="co1" table:number-columns-repeated="' . $worksheet->getMaxNumColumns() . '"/>';
return $tableElement;
}
/**
* @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;
}
/**
* Adds data to the worksheet.
* Adds data to the given worksheet.
*
* @param Worksheet $worksheet The worksheet to add the row to
* @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.
* @param Style $rowStyle 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
* @throws IOException If the data cannot be written
* @throws InvalidArgumentException If a cell value's type is not supported
*/
public function addRow($dataRow, $style)
public function addRow(Worksheet $worksheet, $dataRow, $rowStyle)
{
// $dataRow can be an associative array. We need to transform
// it into a regular array, as we'll use the numeric indexes.
$dataRowWithNumericIndexes = array_values($dataRow);
$styleIndex = ($style->getId() + 1); // 1-based
$styleIndex = ($rowStyle->getId() + 1); // 1-based
$cellsCount = count($dataRow);
$this->maxNumColumns = max($this->maxNumColumns, $cellsCount);
$data = '<table:table-row table:style-name="ro1">';
@ -166,13 +135,14 @@ class Worksheet implements WorksheetInterface
$data .= '</table:table-row>';
$wasWriteSuccessful = fwrite($this->sheetFilePointer, $data);
$wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data);
if ($wasWriteSuccessful === false) {
throw new IOException("Unable to write data in {$this->worksheetFilePath}");
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
// only update the count if the write worked
$this->lastWrittenRowIndex++;
$lastWrittenRowIndex = $worksheet->getLastWrittenRowIndex();
$worksheet->setLastWrittenRowIndex($lastWrittenRowIndex + 1);
}
/**
@ -184,7 +154,7 @@ class Worksheet implements WorksheetInterface
* @return string The cell XML content
* @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
*/
protected function getCellXML($cellValue, $styleIndex, $numTimesValueRepeated)
private function getCellXML($cellValue, $styleIndex, $numTimesValueRepeated)
{
$data = '<table:table-cell table:style-name="ce' . $styleIndex . '"';
@ -228,14 +198,17 @@ class Worksheet implements WorksheetInterface
/**
* Closes the worksheet
*
* @param Worksheet $worksheet
* @return void
*/
public function close()
public function close(Worksheet $worksheet)
{
if (!is_resource($this->sheetFilePointer)) {
$worksheetFilePointer = $worksheet->getFilePointer();
if (!is_resource($worksheetFilePointer)) {
return;
}
fclose($this->sheetFilePointer);
fclose($worksheetFilePointer);
}
}
}

View File

@ -2,10 +2,8 @@
namespace Box\Spout\Writer\ODS;
use Box\Spout\Writer\AbstractMultiSheetsWriter;
use Box\Spout\Writer\Common;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\ODS\Internal\Workbook;
use Box\Spout\Writer\WriterMultiSheetsAbstract;
/**
* Class Writer
@ -13,14 +11,11 @@ use Box\Spout\Writer\ODS\Internal\Workbook;
*
* @package Box\Spout\Writer\ODS
*/
class Writer extends AbstractMultiSheetsWriter
class Writer extends WriterMultiSheetsAbstract
{
/** @var string Content-Type value for the header */
protected static $headerContentType = 'application/vnd.oasis.opendocument.spreadsheet';
/** @var Internal\Workbook The workbook for the ODS file */
protected $book;
/**
* Sets a custom temporary folder for creating intermediate files/folders.
* This must be set before opening the writer.
@ -37,54 +32,4 @@ class Writer extends AbstractMultiSheetsWriter
$this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder);
return $this;
}
/**
* Configures the write and sets the current sheet pointer to a new sheet.
*
* @return void
* @throws \Box\Spout\Common\Exception\IOException If unable to open the file for writing
*/
protected function openWriter()
{
$this->book = new Workbook($this->optionsManager);
$this->book->addNewSheetAndMakeItCurrent();
}
/**
* @return Internal\Workbook The workbook representing the file to be written
*/
protected function getWorkbook()
{
return $this->book;
}
/**
* Adds data to the currently opened writer.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param \Box\Spout\Writer\Style\Style $style Style to be applied to the row.
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow, $style)
{
$this->throwIfBookIsNotAvailable();
$this->book->addRowToCurrentWorksheet($dataRow, $style);
}
/**
* Closes the writer, preventing any additional writing.
*
* @return void
*/
protected function closeWriter()
{
if ($this->book) {
$this->book->close($this->filePointer);
}
}
}

View File

@ -2,8 +2,8 @@
namespace Box\Spout\Writer;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Common\Exception\InvalidArgumentException;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Common\Exception\SpoutException;
use Box\Spout\Common\Helper\FileSystemHelper;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
@ -12,12 +12,12 @@ use Box\Spout\Writer\Exception\WriterAlreadyOpenedException;
use Box\Spout\Writer\Exception\WriterNotOpenedException;
/**
* Class AbstractWriter
* Class WriterAbstract
*
* @package Box\Spout\Writer
* @abstract
*/
abstract class AbstractWriter implements WriterInterface
abstract class WriterAbstract implements WriterInterface
{
/** @var string Path to the output file */
protected $outputFilePath;
@ -80,7 +80,7 @@ abstract class AbstractWriter implements WriterInterface
* @see https://github.com/box/spout/issues/272
*
* @param Style\Style $defaultStyle
* @return AbstractWriter
* @return WriterAbstract
*/
public function setDefaultRowStyle($defaultStyle)
{
@ -91,7 +91,7 @@ abstract class AbstractWriter implements WriterInterface
/**
* @param \Box\Spout\Common\Helper\GlobalFunctionsHelper $globalFunctionsHelper
* @return AbstractWriter
* @return WriterAbstract
*/
public function setGlobalFunctionsHelper($globalFunctionsHelper)
{
@ -105,7 +105,7 @@ abstract class AbstractWriter implements WriterInterface
*
* @api
* @param string $outputFilePath Path of the output file that will contain the data
* @return AbstractWriter
* @return WriterAbstract
* @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened or if the given path is not writable
*/
public function openToFile($outputFilePath)
@ -129,7 +129,7 @@ abstract class AbstractWriter implements WriterInterface
*
* @api
* @param string $outputFileName Name of the output file that will contain the data. If a path is passed in, only the file name will be kept
* @return AbstractWriter
* @return WriterAbstract
* @throws \Box\Spout\Common\Exception\IOException If the writer cannot be opened
*/
public function openToBrowser($outputFileName)
@ -199,7 +199,7 @@ abstract class AbstractWriter implements WriterInterface
* If empty, no data is added (i.e. not even as a blank row)
* Example: $dataRow = ['data1', 1234, null, '', 'data5', false];
* @api
* @return AbstractWriter
* @return WriterAbstract
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
* @throws \Box\Spout\Common\Exception\SpoutException If anything else goes wrong while writing data
@ -234,7 +234,7 @@ abstract class AbstractWriter implements WriterInterface
* @api
* @param array $dataRow Array of array containing data to be streamed.
* @param Style\Style $style Style to be applied to the row.
* @return AbstractWriter
* @return WriterAbstract
* @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
@ -262,7 +262,7 @@ abstract class AbstractWriter implements WriterInterface
* ['data11', 12, , '', 'data13'],
* ['data21', 'data22', null, false],
* ];
* @return AbstractWriter
* @return WriterAbstract
* @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
@ -290,7 +290,7 @@ abstract class AbstractWriter implements WriterInterface
* @api
* @param array $dataRows Array of array containing data to be streamed.
* @param Style\Style $style Style to be applied to the rows.
* @return AbstractWriter
* @return WriterAbstract
* @throws \Box\Spout\Common\Exception\InvalidArgumentException If the input param is not valid
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
* @throws \Box\Spout\Common\Exception\IOException If unable to write data

View File

@ -5,6 +5,7 @@ namespace Box\Spout\Writer;
use Box\Spout\Common\Exception\UnsupportedTypeException;
use Box\Spout\Common\Helper\GlobalFunctionsHelper;
use Box\Spout\Common\Type;
use Box\Spout\Writer\Factory\EntityFactory;
use Box\Spout\Writer\Style\StyleBuilder;
/**
@ -64,8 +65,9 @@ class WriterFactory
{
$styleBuilder = new StyleBuilder();
$optionsManager = new XLSX\Manager\OptionsManager($styleBuilder);
$generalFactory = new XLSX\Factory\InternalFactory(new EntityFactory());
return new XLSX\Writer($optionsManager);
return new XLSX\Writer($optionsManager, $generalFactory);
}
/**
@ -75,7 +77,8 @@ class WriterFactory
{
$styleBuilder = new StyleBuilder();
$optionsManager = new ODS\Manager\OptionsManager($styleBuilder);
$generalFactory = new ODS\Factory\InternalFactory(new EntityFactory());
return new ODS\Writer($optionsManager);
return new ODS\Writer($optionsManager, $generalFactory);
}
}

View File

@ -0,0 +1,177 @@
<?php
namespace Box\Spout\Writer;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Entity\Worksheet;
use Box\Spout\Writer\Exception\WriterNotOpenedException;
use Box\Spout\Writer\Factory\InternalFactoryInterface;
use Box\Spout\Writer\Manager\WorkbookManagerInterface;
/**
* Class WriterMultiSheetsAbstract
*
* @package Box\Spout\Writer
* @abstract
*/
abstract class WriterMultiSheetsAbstract extends WriterAbstract
{
/** @var InternalFactoryInterface */
private $internalFactory;
/** @var WorkbookManagerInterface */
private $workbookManager;
/**
* @param OptionsManagerInterface $optionsManager
* @param InternalFactoryInterface $internalFactory
*/
public function __construct(OptionsManagerInterface $optionsManager, InternalFactoryInterface $internalFactory)
{
parent::__construct($optionsManager);
$this->internalFactory = $internalFactory;
}
/**
* Sets whether new sheets should be automatically created when the max rows limit per sheet is reached.
* This must be set before opening the writer.
*
* @api
* @param bool $shouldCreateNewSheetsAutomatically Whether new sheets should be automatically created when the max rows limit per sheet is reached
* @return WriterMultiSheetsAbstract
* @throws \Box\Spout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened
*/
public function setShouldCreateNewSheetsAutomatically($shouldCreateNewSheetsAutomatically)
{
$this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.');
$this->optionsManager->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, $shouldCreateNewSheetsAutomatically);
return $this;
}
/**
* Configures the write and sets the current sheet pointer to a new sheet.
*
* @return void
* @throws \Box\Spout\Common\Exception\IOException If unable to open the file for writing
*/
protected function openWriter()
{
if (!$this->workbookManager) {
$this->workbookManager = $this->internalFactory->createWorkbookManager($this->optionsManager);
$this->workbookManager->addNewSheetAndMakeItCurrent();
}
}
/**
* Returns all the workbook's sheets
*
* @api
* @return Common\Sheet[] All the workbook's sheets
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
*/
public function getSheets()
{
$this->throwIfWorkbookIsNotAvailable();
$externalSheets = [];
$worksheets = $this->workbookManager->getWorksheets();
/** @var Worksheet $worksheet */
foreach ($worksheets as $worksheet) {
$externalSheets[] = $worksheet->getExternalSheet();
}
return $externalSheets;
}
/**
* Creates a new sheet and make it the current sheet. The data will now be written to this sheet.
*
* @api
* @return Common\Sheet The created sheet
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
*/
public function addNewSheetAndMakeItCurrent()
{
$this->throwIfWorkbookIsNotAvailable();
$worksheet = $this->workbookManager->addNewSheetAndMakeItCurrent();
return $worksheet->getExternalSheet();
}
/**
* Returns the current sheet
*
* @api
* @return Common\Sheet The current sheet
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
*/
public function getCurrentSheet()
{
$this->throwIfWorkbookIsNotAvailable();
return $this->workbookManager->getCurrentWorksheet()->getExternalSheet();
}
/**
* Sets the given sheet as the current one. New data will be written to this sheet.
* The writing will resume where it stopped (i.e. data won't be truncated).
*
* @api
* @param Common\Sheet $sheet The sheet to set as current
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yet
* @throws \Box\Spout\Writer\Exception\SheetNotFoundException If the given sheet does not exist in the workbook
*/
public function setCurrentSheet($sheet)
{
$this->throwIfWorkbookIsNotAvailable();
$this->workbookManager->setCurrentSheet($sheet);
}
/**
* Checks if the workbook has been created. Throws an exception if not created yet.
*
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the workbook is not created yet
*/
protected function throwIfWorkbookIsNotAvailable()
{
if (!$this->workbookManager->getWorkbook()) {
throw new WriterNotOpenedException('The writer must be opened before performing this action.');
}
}
/**
* Adds data to the currently opened writer.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param \Box\Spout\Writer\Style\Style $style Style to be applied to the row.
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow, $style)
{
$this->throwIfWorkbookIsNotAvailable();
$this->workbookManager->addRowToCurrentWorksheet($dataRow, $style);
}
/**
* Closes the writer, preventing any additional writing.
*
* @return void
*/
protected function closeWriter()
{
if ($this->workbookManager) {
$this->workbookManager->close($this->filePointer);
}
}
}

View File

@ -0,0 +1,123 @@
<?php
namespace Box\Spout\Writer\XLSX\Factory;
use Box\Spout\Common\Escaper;
use Box\Spout\Common\Helper\StringHelper;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Factory\EntityFactory;
use Box\Spout\Writer\Factory\InternalFactoryInterface;
use Box\Spout\Writer\Factory\WorkbookFactory;
use Box\Spout\Writer\Factory\WorksheetFactory;
use Box\Spout\Writer\XLSX\Helper\FileSystemHelper;
use Box\Spout\Writer\XLSX\Helper\SharedStringsHelper;
use Box\Spout\Writer\XLSX\Helper\StyleHelper;
use Box\Spout\Writer\XLSX\Manager\WorkbookManager;
use Box\Spout\Writer\XLSX\Manager\WorksheetManager;
/**
* Class InternalFactory
* Factory for all useful types of objects needed by the XLSX Writer
*
* @package Box\Spout\Writer\XLSX\Factory
*/
class InternalFactory implements InternalFactoryInterface
{
/** @var EntityFactory */
private $entityFactory;
/**
* InternalFactory constructor.
*
* @param EntityFactory $entityFactory
*/
public function __construct(EntityFactory $entityFactory)
{
$this->entityFactory = $entityFactory;
}
/**
* @param OptionsManagerInterface $optionsManager
* @return WorkbookManager
*/
public function createWorkbookManager(OptionsManagerInterface $optionsManager)
{
$workbook = $this->entityFactory->createWorkbook();
$fileSystemHelper = $this->createFileSystemHelper($optionsManager);
$fileSystemHelper->createBaseFilesAndFolders();
$xlFolder = $fileSystemHelper->getXlFolder();
$sharedStringsHelper = $this->createSharedStringsHelper($xlFolder);
$styleHelper = $this->createStyleHelper($optionsManager);
$worksheetManager = $this->createWorksheetManager($optionsManager, $sharedStringsHelper, $styleHelper);
return new WorkbookManager($workbook, $optionsManager, $worksheetManager, $styleHelper, $fileSystemHelper, $this->entityFactory);
}
/**
* @param OptionsManagerInterface $optionsManager
* @param SharedStringsHelper $sharedStringsHelper
* @param StyleHelper $styleHelper
* @return WorksheetManager
*/
private function createWorksheetManager(
OptionsManagerInterface $optionsManager,
SharedStringsHelper $sharedStringsHelper,
StyleHelper $styleHelper
)
{
$stringsEscaper = $this->createStringsEscaper();
$stringsHelper = $this->createStringHelper();
return new WorksheetManager($optionsManager, $sharedStringsHelper, $styleHelper, $stringsEscaper, $stringsHelper);
}
/**
* @param string $xlFolder Path to the "xl" folder
* @return SharedStringsHelper
*/
private function createSharedStringsHelper($xlFolder)
{
return new SharedStringsHelper($xlFolder);
}
/**
* @param OptionsManagerInterface $optionsManager
* @return FileSystemHelper
*/
private function createFileSystemHelper(OptionsManagerInterface $optionsManager)
{
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
return new FileSystemHelper($tempFolder);
}
/**
* @param OptionsManagerInterface $optionsManager
* @return StyleHelper
*/
private function createStyleHelper(OptionsManagerInterface $optionsManager)
{
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
return new StyleHelper($defaultRowStyle);
}
/**
* @return Escaper\XLSX
*/
private function createStringsEscaper()
{
return Escaper\XLSX::getInstance();
}
/**
* @return StringHelper
*/
private function createStringHelper()
{
return new StringHelper();
}
}

View File

@ -2,8 +2,9 @@
namespace Box\Spout\Writer\XLSX\Helper;
use Box\Spout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface;
use Box\Spout\Writer\Common\Helper\ZipHelper;
use Box\Spout\Writer\XLSX\Internal\Worksheet;
use Box\Spout\Writer\Entity\Worksheet;
/**
* Class FileSystemHelper
@ -12,7 +13,7 @@ use Box\Spout\Writer\XLSX\Internal\Worksheet;
*
* @package Box\Spout\Writer\XLSX\Helper
*/
class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper implements FileSystemWithRootFolderHelperInterface
{
const APP_NAME = 'Spout';
@ -30,22 +31,22 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
const STYLES_XML_FILE_NAME = 'styles.xml';
/** @var string Path to the root folder inside the temp folder where the files to create the XLSX will be stored */
protected $rootFolder;
private $rootFolder;
/** @var string Path to the "_rels" folder inside the root folder */
protected $relsFolder;
private $relsFolder;
/** @var string Path to the "docProps" folder inside the root folder */
protected $docPropsFolder;
private $docPropsFolder;
/** @var string Path to the "xl" folder inside the root folder */
protected $xlFolder;
private $xlFolder;
/** @var string Path to the "_rels" folder inside the "xl" folder */
protected $xlRelsFolder;
private $xlRelsFolder;
/** @var string Path to the "worksheets" folder inside the "xl" folder */
protected $xlWorksheetsFolder;
private $xlWorksheetsFolder;
/**
* @return string
@ -92,7 +93,7 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
*/
protected function createRootFolder()
private function createRootFolder()
{
$this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('xlsx', true));
return $this;
@ -104,7 +105,7 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or the ".rels" file
*/
protected function createRelsFolderAndFile()
private function createRelsFolderAndFile()
{
$this->relsFolder = $this->createFolder($this->rootFolder, self::RELS_FOLDER_NAME);
@ -119,7 +120,7 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the file
*/
protected function createRelsFile()
private function createRelsFile()
{
$relsFileContents = <<<EOD
<?xml version="1.0" encoding="UTF-8"?>
@ -141,7 +142,7 @@ EOD;
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or one of the files
*/
protected function createDocPropsFolderAndFiles()
private function createDocPropsFolderAndFiles()
{
$this->docPropsFolder = $this->createFolder($this->rootFolder, self::DOC_PROPS_FOLDER_NAME);
@ -157,7 +158,7 @@ EOD;
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the file
*/
protected function createAppXmlFile()
private function createAppXmlFile()
{
$appName = self::APP_NAME;
$appXmlFileContents = <<<EOD
@ -179,7 +180,7 @@ EOD;
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the file
*/
protected function createCoreXmlFile()
private function createCoreXmlFile()
{
$createdDate = (new \DateTime())->format(\DateTime::W3C);
$coreXmlFileContents = <<<EOD
@ -202,7 +203,7 @@ EOD;
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the folders
*/
protected function createXlFolderAndSubFolders()
private function createXlFolderAndSubFolders()
{
$this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
$this->createXlRelsFolder();
@ -217,7 +218,7 @@ EOD;
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
*/
protected function createXlRelsFolder()
private function createXlRelsFolder()
{
$this->xlRelsFolder = $this->createFolder($this->xlFolder, self::RELS_FOLDER_NAME);
return $this;
@ -229,7 +230,7 @@ EOD;
* @return FileSystemHelper
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
*/
protected function createXlWorksheetsFolder()
private function createXlWorksheetsFolder()
{
$this->xlWorksheetsFolder = $this->createFolder($this->xlFolder, self::WORKSHEETS_FOLDER_NAME);
return $this;

View File

@ -2,7 +2,7 @@
namespace Box\Spout\Writer\XLSX\Helper;
use Box\Spout\Writer\Common\Helper\AbstractStyleHelper;
use Box\Spout\Writer\Common\Helper\StyleHelperAbstract;
use Box\Spout\Writer\Style\Color;
use Box\Spout\Writer\Style\Style;
@ -12,7 +12,7 @@ use Box\Spout\Writer\Style\Style;
*
* @package Box\Spout\Writer\XLSX\Helper
*/
class StyleHelper extends AbstractStyleHelper
class StyleHelper extends StyleHelperAbstract
{
/**
* @var array

View File

@ -1,135 +0,0 @@
<?php
namespace Box\Spout\Writer\XLSX\Internal;
use Box\Spout\Writer\Common\Internal\AbstractWorkbook;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\XLSX\Helper\FileSystemHelper;
use Box\Spout\Writer\XLSX\Helper\SharedStringsHelper;
use Box\Spout\Writer\XLSX\Helper\StyleHelper;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\XLSX\Manager\OptionsManager;
/**
* Class Workbook
* Represents a workbook within a XLSX file.
* It provides the functions to work with worksheets.
*
* @package Box\Spout\Writer\XLSX\Internal
*/
class Workbook extends AbstractWorkbook
{
/**
* Maximum number of rows a XLSX sheet can contain
* @see http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx
*/
protected static $maxRowsPerWorksheet = 1048576;
/** @var bool Whether inline or shared strings should be used */
protected $shouldUseInlineStrings;
/** @var \Box\Spout\Writer\XLSX\Helper\FileSystemHelper Helper to perform file system operations */
protected $fileSystemHelper;
/** @var \Box\Spout\Writer\XLSX\Helper\SharedStringsHelper Helper to write shared strings */
protected $sharedStringsHelper;
/** @var \Box\Spout\Writer\XLSX\Helper\StyleHelper Helper to apply styles */
protected $styleHelper;
/**
* @param \Box\Spout\Writer\Common\Manager\OptionsManagerInterface $optionsManager Options manager
* @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the base folders
*/
public function __construct(OptionsManagerInterface $optionsManager)
{
parent::__construct($optionsManager);
$tempFolder = $optionsManager->getOption(Options::TEMP_FOLDER);
$this->fileSystemHelper = new FileSystemHelper($tempFolder);
$this->fileSystemHelper->createBaseFilesAndFolders();
$defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE);
$this->styleHelper = new StyleHelper($defaultRowStyle);
// This helper will be shared by all sheets
$xlFolder = $this->fileSystemHelper->getXlFolder();
$this->sharedStringsHelper = new SharedStringsHelper($xlFolder);
}
/**
* @return \Box\Spout\Writer\XLSX\Helper\StyleHelper Helper to apply styles to XLSX files
*/
protected function getStyleHelper()
{
return $this->styleHelper;
}
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
protected function getMaxRowsPerWorksheet()
{
return self::$maxRowsPerWorksheet;
}
/**
* Creates a new sheet in the workbook. The current sheet remains unchanged.
*
* @return Worksheet The created sheet
* @throws \Box\Spout\Common\Exception\IOException If unable to open the sheet for writing
*/
public function addNewSheet()
{
$newSheetIndex = count($this->worksheets);
$sheet = new Sheet($newSheetIndex, $this->internalId);
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
$worksheet = new Worksheet($sheet, $worksheetFilesFolder, $this->sharedStringsHelper, $this->styleHelper, $this->optionManager);
$this->worksheets[] = $worksheet;
return $worksheet;
}
/**
* Closes the workbook and all its associated sheets.
* All the necessary files are written to disk and zipped together to create the XLSX file.
* All the temporary files are then deleted.
*
* @param resource $finalFilePointer Pointer to the XLSX that will be created
* @return void
*/
public function close($finalFilePointer)
{
/** @var Worksheet[] $worksheets */
$worksheets = $this->worksheets;
foreach ($worksheets as $worksheet) {
$worksheet->close();
}
$this->sharedStringsHelper->close();
// Finish creating all the necessary files before zipping everything together
$this->fileSystemHelper
->createContentTypesFile($worksheets)
->createWorkbookFile($worksheets)
->createWorkbookRelsFile($worksheets)
->createStylesFile($this->styleHelper)
->zipRootFolderAndCopyToStream($finalFilePointer);
$this->cleanupTempFolder();
}
/**
* Deletes the root folder created in the temp folder and all its contents.
*
* @return void
*/
protected function cleanupTempFolder()
{
$xlsxRootFolder = $this->fileSystemHelper->getRootFolder();
$this->fileSystemHelper->deleteFolderRecursively($xlsxRootFolder);
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace Box\Spout\Writer\XLSX\Manager;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Manager\WorkbookManagerAbstract;
use Box\Spout\Writer\XLSX\Helper\FileSystemHelper;
use Box\Spout\Writer\XLSX\Helper\StyleHelper;
/**
* Class WorkbookManager
* XLSX workbook manager, providing the interfaces to work with workbook.
*
* @package Box\Spout\Writer\XLSX\Manager
*/
class WorkbookManager extends WorkbookManagerAbstract
{
/**
* Maximum number of rows a XLSX sheet can contain
* @see http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP010073849.aspx
*/
protected static $maxRowsPerWorksheet = 1048576;
/** @var WorksheetManager Object used to manage worksheets */
protected $worksheetManager;
/** @var FileSystemHelper Helper to perform file system operations */
protected $fileSystemHelper;
/** @var StyleHelper Helper to apply styles */
protected $styleHelper;
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
protected function getMaxRowsPerWorksheet()
{
return self::$maxRowsPerWorksheet;
}
/**
* @param Sheet $sheet
* @return string The file path where the data for the given sheet will be stored
*/
public function getWorksheetFilePath(Sheet $sheet)
{
$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
return $worksheetFilesFolder . '/' . strtolower($sheet->getName()) . '.xml';
}
/**
* Closes custom objects that are still opened
*
* @return void
*/
protected function closeRemainingObjects()
{
$this->worksheetManager->getSharedStringsHelper()->close();
}
/**
* Writes all the necessary files to disk and zip them together to create the final file.
*
* @param resource $finalFilePointer Pointer to the spreadsheet that will be created
* @return void
*/
protected function writeAllFilesToDiskAndZipThem($finalFilePointer)
{
$worksheets = $this->getWorksheets();
$this->fileSystemHelper
->createContentTypesFile($worksheets)
->createWorkbookFile($worksheets)
->createWorkbookRelsFile($worksheets)
->createStylesFile($this->styleHelper)
->zipRootFolderAndCopyToStream($finalFilePointer);
}
}

View File

@ -1,24 +1,27 @@
<?php
namespace Box\Spout\Writer\XLSX\Internal;
namespace Box\Spout\Writer\XLSX\Manager;
use Box\Spout\Common\Exception\InvalidArgumentException;
use Box\Spout\Common\Exception\IOException;
use Box\Spout\Common\Helper\StringHelper;
use Box\Spout\Writer\Common\Cell;
use Box\Spout\Writer\Common\Helper\CellHelper;
use Box\Spout\Writer\Common\Internal\WorksheetInterface;
use Box\Spout\Writer\Common\Manager\OptionsManagerInterface;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Entity\Worksheet;
use Box\Spout\Writer\Manager\WorksheetManagerInterface;
use Box\Spout\Writer\Style\Style;
use Box\Spout\Writer\XLSX\Helper\SharedStringsHelper;
use Box\Spout\Writer\XLSX\Helper\StyleHelper;
/**
* Class Worksheet
* Represents a worksheet within a XLSX file. The difference with the Sheet object is
* that this class provides an interface to write data
* Class WorksheetManager
* XLSX worksheet manager, providing the interfaces to work with XLSX worksheets.
*
* @package Box\Spout\Writer\XLSX\Internal
* @package Box\Spout\Writer\XLSX\Manager
*/
class Worksheet implements WorksheetInterface
class WorksheetManager implements WorksheetManagerInterface
{
/**
* Maximum number of characters a cell can contain
@ -33,126 +36,103 @@ class Worksheet implements WorksheetInterface
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
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 SharedStringsHelper Helper to write shared strings */
private $sharedStringsHelper;
/** @var StyleHelper Helper to work with styles */
private $styleHelper;
/** @var \Box\Spout\Common\Escaper\XLSX Strings escaper */
protected $stringsEscaper;
private $stringsEscaper;
/** @var \Box\Spout\Common\Helper\StringHelper String helper */
protected $stringHelper;
/** @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;
/** @var StringHelper String helper */
private $stringHelper;
/**
* @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 $styleHelper Helper to work with styles
* @param \Box\Spout\Writer\Common\Manager\OptionsManagerInterface $optionsManager Options manager
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
* WorksheetManager constructor.
*
* @param OptionsManagerInterface $optionsManager
* @param SharedStringsHelper $sharedStringsHelper
* @param StyleHelper $styleHelper
* @param \Box\Spout\Common\Escaper\XLSX $stringsEscaper
* @param StringHelper $stringHelper
*/
public function __construct($externalSheet, $worksheetFilesFolder, $sharedStringsHelper, $styleHelper, OptionsManagerInterface $optionsManager)
public function __construct(
OptionsManagerInterface $optionsManager,
SharedStringsHelper $sharedStringsHelper,
StyleHelper $styleHelper,
\Box\Spout\Common\Escaper\XLSX $stringsEscaper,
StringHelper $stringHelper)
{
$this->externalSheet = $externalSheet;
$this->shouldUseInlineStrings = $optionsManager->getOption(Options::SHOULD_USE_INLINE_STRINGS);
$this->sharedStringsHelper = $sharedStringsHelper;
$this->styleHelper = $styleHelper;
$this->shouldUseInlineStrings = $optionsManager->getOption(Options::SHOULD_USE_INLINE_STRINGS);
/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
$this->stringsEscaper = \Box\Spout\Common\Escaper\XLSX::getInstance();
$this->stringHelper = new StringHelper();
$this->worksheetFilePath = $worksheetFilesFolder . '/' . strtolower($this->externalSheet->getName()) . '.xml';
$this->startSheet();
$this->stringsEscaper = $stringsEscaper;
$this->stringHelper = $stringHelper;
}
/**
* @return SharedStringsHelper
*/
public function getSharedStringsHelper()
{
return $this->sharedStringsHelper;
}
/**
* Prepares the worksheet to accept data
*
* @param Worksheet $worksheet The worksheet to start
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
*/
protected function startSheet()
public function startSheet(Worksheet $worksheet)
{
$this->sheetFilePointer = fopen($this->worksheetFilePath, 'w');
$this->throwIfSheetFilePointerIsNotAvailable();
$sheetFilePointer = fopen($worksheet->getFilePath(), 'w');
$this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer);
fwrite($this->sheetFilePointer, self::SHEET_XML_FILE_HEADER);
fwrite($this->sheetFilePointer, '<sheetData>');
$worksheet->setFilePointer($sheetFilePointer);
fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER);
fwrite($sheetFilePointer, '<sheetData>');
}
/**
* Checks if the book has been created. Throws an exception if not created yet.
* Checks if the sheet has been sucessfully created. Throws an exception if not.
*
* @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file
* @return void
* @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
* @throws IOException If the sheet data file cannot be opened for writing
*/
protected function throwIfSheetFilePointerIsNotAvailable()
private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer)
{
if (!$this->sheetFilePointer) {
if (!$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.
* Adds data to the given worksheet.
*
* @param Worksheet $worksheet The worksheet to add the row to
* @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.
* @param Style $rowStyle 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
* @throws IOException If the data cannot be written
* @throws InvalidArgumentException If a cell value's type is not supported
*/
public function addRow($dataRow, $style)
public function addRow(Worksheet $worksheet, $dataRow, $rowStyle)
{
if (!$this->isEmptyRow($dataRow)) {
$this->addNonEmptyRow($dataRow, $style);
$this->addNonEmptyRow($worksheet, $dataRow, $rowStyle);
}
$this->lastWrittenRowIndex++;
$worksheet->setLastWrittenRowIndex($worksheet->getLastWrittenRowIndex() + 1);
}
/**
@ -172,6 +152,7 @@ EOD;
/**
* Adds non empty row to the worksheet.
*
* @param Worksheet $worksheet The worksheet to add the row to
* @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.
@ -179,10 +160,10 @@ EOD;
* @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)
private function addNonEmptyRow(Worksheet $worksheet, $dataRow, $style)
{
$cellNumber = 0;
$rowIndex = $this->lastWrittenRowIndex + 1;
$rowIndex = $worksheet->getLastWrittenRowIndex() + 1;
$numCells = count($dataRow);
$rowXML = '<row r="' . $rowIndex . '" spans="1:' . $numCells . '">';
@ -194,9 +175,9 @@ EOD;
$rowXML .= '</row>';
$wasWriteSuccessful = fwrite($this->sheetFilePointer, $rowXML);
$wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $rowXML);
if ($wasWriteSuccessful === false) {
throw new IOException("Unable to write data in {$this->worksheetFilePath}");
throw new IOException("Unable to write data in {$worksheet->getFilePath()}");
}
}
@ -270,16 +251,19 @@ EOD;
/**
* Closes the worksheet
*
* @param Worksheet $worksheet
* @return void
*/
public function close()
public function close(Worksheet $worksheet)
{
if (!is_resource($this->sheetFilePointer)) {
$worksheetFilePointer = $worksheet->getFilePointer();
if (!is_resource($worksheetFilePointer)) {
return;
}
fwrite($this->sheetFilePointer, '</sheetData>');
fwrite($this->sheetFilePointer, '</worksheet>');
fclose($this->sheetFilePointer);
fwrite($worksheetFilePointer, '</sheetData>');
fwrite($worksheetFilePointer, '</worksheet>');
fclose($worksheetFilePointer);
}
}
}

View File

@ -2,10 +2,8 @@
namespace Box\Spout\Writer\XLSX;
use Box\Spout\Writer\AbstractMultiSheetsWriter;
use Box\Spout\Writer\Common\Options;
use Box\Spout\Writer\Style\StyleBuilder;
use Box\Spout\Writer\XLSX\Internal\Workbook;
use Box\Spout\Writer\WriterMultiSheetsAbstract;
/**
* Class Writer
@ -13,14 +11,11 @@ use Box\Spout\Writer\XLSX\Internal\Workbook;
*
* @package Box\Spout\Writer\XLSX
*/
class Writer extends AbstractMultiSheetsWriter
class Writer extends WriterMultiSheetsAbstract
{
/** @var string Content-Type value for the header */
protected static $headerContentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
/** @var Internal\Workbook The workbook for the XLSX file */
protected $book;
/**
* Sets a custom temporary folder for creating intermediate files/folders.
* This must be set before opening the writer.
@ -54,56 +49,4 @@ class Writer extends AbstractMultiSheetsWriter
$this->optionsManager->setOption(Options::SHOULD_USE_INLINE_STRINGS, $shouldUseInlineStrings);
return $this;
}
/**
* Configures the write and sets the current sheet pointer to a new sheet.
*
* @return void
* @throws \Box\Spout\Common\Exception\IOException If unable to open the file for writing
*/
protected function openWriter()
{
if (!$this->book) {
$this->book = new Workbook($this->optionsManager);
$this->book->addNewSheetAndMakeItCurrent();
}
}
/**
* @return Internal\Workbook The workbook representing the file to be written
*/
protected function getWorkbook()
{
return $this->book;
}
/**
* Adds data to the currently opened writer.
* If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination
* with the creation of new worksheets if one worksheet has reached its maximum capicity.
*
* @param array $dataRow Array containing data to be written.
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
* @param \Box\Spout\Writer\Style\Style $style Style to be applied to the row.
* @return void
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
*/
protected function addRowToWriter(array $dataRow, $style)
{
$this->throwIfBookIsNotAvailable();
$this->book->addRowToCurrentWorksheet($dataRow, $style);
}
/**
* Closes the writer, preventing any additional writing.
*
* @return void
*/
protected function closeWriter()
{
if ($this->book) {
$this->book->close($this->filePointer);
}
}
}

View File

@ -62,6 +62,7 @@ class SheetTest extends \PHPUnit_Framework_TestCase
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\ODS\Writer $writer */
$writer = WriterFactory::create(Type::ODS);
$writer->openToFile($resourcePath);
@ -85,6 +86,7 @@ class SheetTest extends \PHPUnit_Framework_TestCase
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\ODS\Writer $writer */
$writer = WriterFactory::create(Type::ODS);
$writer->openToFile($resourcePath);

View File

@ -155,6 +155,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var Writer $writer */
$writer = WriterFactory::create(Type::ODS);
$writer->openToFile($resourcePath);
@ -376,7 +377,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
];
// set the maxRowsPerSheet limit to 2
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\ODS\Internal\Workbook', 'maxRowsPerWorksheet', 2);
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\ODS\Manager\WorkbookManager', 'maxRowsPerWorksheet', 2);
$writer = $this->writeToODSFile($dataRows, $fileName, $shouldCreateSheetsAutomatically = true);
$this->assertEquals(2, count($writer->getSheets()), '2 sheets should have been created.');
@ -400,7 +401,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
];
// set the maxRowsPerSheet limit to 2
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\ODS\Internal\Workbook', 'maxRowsPerWorksheet', 2);
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\ODS\Manager\WorkbookManager', 'maxRowsPerWorksheet', 2);
$writer = $this->writeToODSFile($dataRows, $fileName, $shouldCreateSheetsAutomatically = false);
$this->assertEquals(1, count($writer->getSheets()), 'Only 1 sheet should have been created.');
@ -500,7 +501,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
foreach ($dataRows as $dataRow) {
/** @var Cell $cell */
foreach ($dataRow as $cell) {
$this->assertValueWasWritten($fileName, (string)$cell->getValue(), '', true);
$this->assertValueWasWritten($fileName, (string)$cell->getValue(), '');
}
}
}

View File

@ -7,7 +7,7 @@ use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
use Box\Spout\Writer\Common\Cell;
use Box\Spout\Writer\WriterFactory;
use Box\Spout\Writer\XLSX\Internal\Worksheet;
use Box\Spout\Writer\XLSX\Manager\WorksheetManager;
/**
* Class WriterTest
@ -101,7 +101,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
{
$fileName = 'test_add_row_should_throw_exception_if_unsupported_data_type_passed_in.xlsx';
$dataRows = [
[str_repeat('a', Worksheet::MAX_CHARACTERS_PER_CELL + 1)],
[str_repeat('a', WorksheetManager::MAX_CHARACTERS_PER_CELL + 1)],
];
$this->writeToXLSXFile($dataRows, $fileName);
@ -428,7 +428,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
];
// set the maxRowsPerSheet limit to 2
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\XLSX\Internal\Workbook', 'maxRowsPerWorksheet', 2);
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\XLSX\Manager\WorkbookManager', 'maxRowsPerWorksheet', 2);
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = true);
$this->assertEquals(2, count($writer->getSheets()), '2 sheets should have been created.');
@ -452,7 +452,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
];
// set the maxRowsPerSheet limit to 2
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\XLSX\Internal\Workbook', 'maxRowsPerWorksheet', 2);
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\XLSX\Manager\WorkbookManager', 'maxRowsPerWorksheet', 2);
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = false);
$this->assertEquals(1, count($writer->getSheets()), 'Only 1 sheet should have been created.');