From 3c3d949a5d1899521a6b45aecc5dc721ff5524d5 Mon Sep 17 00:00:00 2001 From: aswinkumar863 Date: Sun, 3 Apr 2022 18:23:13 +0530 Subject: [PATCH] Added table name validation Validation added for - invalid characters - invalid names ("C", "c", "R", or "r") - cell references - space separate words - maxlength of 255 characters - unique table names across worksheet --- samples/Table/01_Table.php | 2 +- src/PhpSpreadsheet/Worksheet/Table.php | 36 +++++++++- .../Worksheet/Table/TableTest.php | 71 +++++++++++++++++-- 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/samples/Table/01_Table.php b/samples/Table/01_Table.php index 405c07ae..c8301680 100644 --- a/samples/Table/01_Table.php +++ b/samples/Table/01_Table.php @@ -53,7 +53,7 @@ $spreadsheet->getActiveSheet()->fromArray($dataArray, null, 'A2'); // Create Table $helper->log('Create Table'); $table = new Table(); -$table->setName('Sales Data'); +$table->setName('Sales_Data'); $table->setRange('A1:D17'); // Create Columns diff --git a/src/PhpSpreadsheet/Worksheet/Table.php b/src/PhpSpreadsheet/Worksheet/Table.php index 30d659f1..950fe74c 100644 --- a/src/PhpSpreadsheet/Worksheet/Table.php +++ b/src/PhpSpreadsheet/Worksheet/Table.php @@ -88,7 +88,29 @@ class Table */ public function setName(string $name) { - $this->name = preg_replace('/\s+/', '_', trim($name)) ?? ''; + $name = trim($name); + + if (strlen($name) == 1 && in_array($name, ['C', 'c', 'R', 'r'])) { + throw new PhpSpreadsheetException('The table name is invalid'); + } + if (strlen($name) > 255) { + throw new PhpSpreadsheetException('The table name cannot be longer than 255 characters'); + } + // Check for A1 or R1C1 cell reference notation + if ( + preg_match(Coordinate::A1_COORDINATE_REGEX, $name) || + preg_match('/^R\[?\-?[0-9]*\]?C\[?\-?[0-9]*\]?$/i', $name) + ) { + throw new PhpSpreadsheetException('The table name can\'t be the same as a cell reference'); + } + if (!preg_match('/^[A-Z_\\\\]/i', $name)) { + throw new PhpSpreadsheetException('The table name must begin a name with a letter, an underscore character (_), or a backslash (\)'); + } + if (!preg_match('/^[A-Z_\\\\][A-Z0-9\._]+$/i', $name)) { + throw new PhpSpreadsheetException('The table name contains invalid characters'); + } + + $this->name = $name; return $this; } @@ -216,6 +238,18 @@ class Table */ public function setWorksheet(?Worksheet $worksheet = null) { + if ($this->name != '' && $worksheet != null) { + $spreadsheet = $worksheet->getParent(); + + foreach ($spreadsheet->getWorksheetIterator() as $sheet) { + foreach ($sheet->getTableCollection() as $table) { + if ($table->getName() == $this->name) { + throw new PhpSpreadsheetException("Workbook already contains a table named '{$this->name}'"); + } + } + } + } + $this->workSheet = $worksheet; return $this; diff --git a/tests/PhpSpreadsheetTests/Worksheet/Table/TableTest.php b/tests/PhpSpreadsheetTests/Worksheet/Table/TableTest.php index c4365f7a..106955a8 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/Table/TableTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/Table/TableTest.php @@ -22,15 +22,78 @@ class TableTest extends SetupTeardown self::assertEquals($expectedResult, $result); } - public function testVariousSets(): void + /** + * @dataProvider validTableNamesProvider + */ + public function testValidTableNames(string $name, string $expected): void { $sheet = $this->getSheet(); $table = new Table(self::INITIAL_RANGE, $sheet); - $result = $table->setName('Table 1'); + $result = $table->setName($name); self::assertInstanceOf(Table::class, $result); - // Spaces will be converted to underscore - self::assertEquals('Table_1', $table->getName()); + self::assertEquals($expected, $table->getName()); + } + + public function validTableNamesProvider(): array + { + return [ + ['Table_1', 'Table_1'], + ['_table_2', '_table_2'], + ['\table_3', '\table_3'], + [" Table_4 \n", 'Table_4'], + ]; + } + + /** + * @dataProvider invalidTableNamesProvider + */ + public function testInvalidTableNames(string $name): void + { + $sheet = $this->getSheet(); + $table = new Table(self::INITIAL_RANGE, $sheet); + + $this->expectException(PhpSpreadsheetException::class); + + $table->setName($name); + } + + public function invalidTableNamesProvider(): array + { + return [ + ['C'], + ['c'], + ['R'], + ['r'], + ['Z100'], + ['Z$100'], + ['R1C1'], + ['R1C'], + ['R11C11'], + ['123'], + ['=Table'], + [bin2hex(random_bytes(255))], // random string with length greater than 255 + ]; + } + + public function testUniqueTableName(): void + { + $this->expectException(PhpSpreadsheetException::class); + $sheet = $this->getSheet(); + + $table1 = new Table(); + $table1->setName('Table_1'); + $sheet->addTable($table1); + + $table2 = new Table(); + $table2->setName('Table_1'); + $sheet->addTable($table2); + } + + public function testVariousSets(): void + { + $sheet = $this->getSheet(); + $table = new Table(self::INITIAL_RANGE, $sheet); $result = $table->setShowHeaderRow(false); self::assertInstanceOf(Table::class, $result);