Skip to content

Commit

Permalink
Enforce sheet name uniqueness per workbook (box#397)
Browse files Browse the repository at this point in the history
Instead of across all workbooks (in case of multiple spreadsheets being created at the same time).
  • Loading branch information
adrilo authored Mar 27, 2017
1 parent 36d3596 commit 33c9d2f
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 13 deletions.
4 changes: 4 additions & 0 deletions src/Spout/Writer/Common/Internal/AbstractWorkbook.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ abstract class AbstractWorkbook implements WorkbookInterface
/** @var bool Whether new sheets should be automatically created when the max rows limit per sheet is reached */
protected $shouldCreateNewSheetsAutomatically;

/** @var string Timestamp based unique ID identifying the workbook */
protected $internalId;

/** @var WorksheetInterface[] Array containing the workbook's sheets */
protected $worksheets = [];

Expand All @@ -30,6 +33,7 @@ abstract class AbstractWorkbook implements WorkbookInterface
public function __construct($shouldCreateNewSheetsAutomatically, $defaultRowStyle)
{
$this->shouldCreateNewSheetsAutomatically = $shouldCreateNewSheetsAutomatically;
$this->internalId = uniqid();
}

/**
Expand Down
19 changes: 14 additions & 5 deletions src/Spout/Writer/Common/Sheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

/**
* Class Sheet
* External representation of a worksheet within a ODS file
* External representation of a worksheet
*
* @package Box\Spout\Writer\Common
*/
Expand All @@ -21,12 +21,15 @@ class Sheet
/** @var array Invalid characters that cannot be contained in the sheet name */
private static $INVALID_CHARACTERS_IN_SHEET_NAME = ['\\', '/', '?', '*', ':', '[', ']'];

/** @var array Associative array [SHEET_INDEX] => [SHEET_NAME] keeping track of sheets' name to enforce uniqueness */
/** @var array Associative array [WORKBOOK_ID] => [[SHEET_INDEX] => [SHEET_NAME]] keeping track of sheets' name to enforce uniqueness per workbook */
protected static $SHEETS_NAME_USED = [];

/** @var int Index of the sheet, based on order in the workbook (zero-based) */
protected $index;

/** @var string ID of the sheet's associated workbook. Used to restrict sheet name uniqueness enforcement to a single workbook */
protected $associatedWorkbookId;

/** @var string Name of the sheet */
protected $name;

Expand All @@ -35,10 +38,16 @@ class Sheet

/**
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
* @param string $associatedWorkbookId ID of the sheet's associated workbook
*/
public function __construct($sheetIndex)
public function __construct($sheetIndex, $associatedWorkbookId)
{
$this->index = $sheetIndex;
$this->associatedWorkbookId = $associatedWorkbookId;
if (!isset(self::$SHEETS_NAME_USED[$associatedWorkbookId])) {
self::$SHEETS_NAME_USED[$associatedWorkbookId] = [];
}

$this->stringHelper = new StringHelper();
$this->setName(self::DEFAULT_SHEET_NAME_PREFIX . ($sheetIndex + 1));
}
Expand Down Expand Up @@ -78,7 +87,7 @@ public function setName($name)
$this->throwIfNameIsInvalid($name);

$this->name = $name;
self::$SHEETS_NAME_USED[$this->index] = $name;
self::$SHEETS_NAME_USED[$this->associatedWorkbookId][$this->index] = $name;

return $this;
}
Expand Down Expand Up @@ -163,7 +172,7 @@ protected function doesStartOrEndWithSingleQuote($name)
*/
protected function isNameUnique($name)
{
foreach (self::$SHEETS_NAME_USED as $sheetIndex => $sheetName) {
foreach (self::$SHEETS_NAME_USED[$this->associatedWorkbookId] as $sheetIndex => $sheetName) {
if ($sheetIndex !== $this->index && $sheetName === $name) {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Spout/Writer/ODS/Internal/Workbook.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ protected function getMaxRowsPerWorksheet()
public function addNewSheet()
{
$newSheetIndex = count($this->worksheets);
$sheet = new Sheet($newSheetIndex);
$sheet = new Sheet($newSheetIndex, $this->internalId);

$sheetsContentTempFolder = $this->fileSystemHelper->getSheetsContentTempFolder();
$worksheet = new Worksheet($sheet, $sheetsContentTempFolder);
Expand Down
2 changes: 1 addition & 1 deletion src/Spout/Writer/XLSX/Internal/Workbook.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ protected function getMaxRowsPerWorksheet()
public function addNewSheet()
{
$newSheetIndex = count($this->worksheets);
$sheet = new Sheet($newSheetIndex);
$sheet = new Sheet($newSheetIndex, $this->internalId);

$worksheetFilesFolder = $this->fileSystemHelper->getXlWorksheetsFolder();
$worksheet = new Worksheet($sheet, $worksheetFilesFolder, $this->sharedStringsHelper, $this->styleHelper, $this->shouldUseInlineStrings);
Expand Down
29 changes: 23 additions & 6 deletions tests/Spout/Writer/Common/SheetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class SheetTest extends \PHPUnit_Framework_TestCase
*/
public function testGetSheetName()
{
$sheets = [new Sheet(0), new Sheet(1)];
$sheets = [new Sheet(0, 'workbookId1'), new Sheet(1, 'workbookId1')];

$this->assertEquals('Sheet1', $sheets[0]->getName(), 'Invalid name for the first sheet');
$this->assertEquals('Sheet2', $sheets[1]->getName(), 'Invalid name for the second sheet');
Expand All @@ -26,7 +26,7 @@ public function testGetSheetName()
public function testSetSheetNameShouldCreateSheetWithCustomName()
{
$customSheetName = 'CustomName';
$sheet = new Sheet(0);
$sheet = new Sheet(0, 'workbookId1');
$sheet->setName($customSheetName);

$this->assertEquals($customSheetName, $sheet->getName(), "The sheet name should have been changed to '$customSheetName'");
Expand Down Expand Up @@ -63,7 +63,7 @@ public function dataProviderForInvalidSheetNames()
*/
public function testSetSheetNameShouldThrowOnInvalidName($customSheetName)
{
(new Sheet(0))->setName($customSheetName);
(new Sheet(0, 'workbookId1'))->setName($customSheetName);
}

/**
Expand All @@ -72,7 +72,7 @@ public function testSetSheetNameShouldThrowOnInvalidName($customSheetName)
public function testSetSheetNameShouldNotThrowWhenSettingSameNameAsCurrentOne()
{
$customSheetName = 'Sheet name';
$sheet = new Sheet(0);
$sheet = new Sheet(0, 'workbookId1');
$sheet->setName($customSheetName);
$sheet->setName($customSheetName);
}
Expand All @@ -85,10 +85,27 @@ public function testSetSheetNameShouldThrowWhenNameIsAlreadyUsed()
{
$customSheetName = 'Sheet name';

$sheet = new Sheet(0);
$sheet = new Sheet(0, 'workbookId1');
$sheet->setName($customSheetName);

$sheet = new Sheet(1);
$sheet = new Sheet(1, 'workbookId1');
$sheet->setName($customSheetName);
}

/**
* @return void
*/
public function testSetSheetNameShouldNotThrowWhenSameNameUsedInDifferentWorkbooks()
{
$customSheetName = 'Sheet name';

$sheet = new Sheet(0, 'workbookId1');
$sheet->setName($customSheetName);

$sheet = new Sheet(0, 'workbookId2');
$sheet->setName($customSheetName);

$sheet = new Sheet(1, 'workbookId3');
$sheet->setName($customSheetName);
}
}

0 comments on commit 33c9d2f

Please sign in to comment.