Skip to content

Commit 3c3d949

Browse files
committed
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
1 parent 0e3dbb7 commit 3c3d949

File tree

3 files changed

+103
-6
lines changed

3 files changed

+103
-6
lines changed

samples/Table/01_Table.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
// Create Table
5454
$helper->log('Create Table');
5555
$table = new Table();
56-
$table->setName('Sales Data');
56+
$table->setName('Sales_Data');
5757
$table->setRange('A1:D17');
5858

5959
// Create Columns

src/PhpSpreadsheet/Worksheet/Table.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,29 @@ public function getName()
8888
*/
8989
public function setName(string $name)
9090
{
91-
$this->name = preg_replace('/\s+/', '_', trim($name)) ?? '';
91+
$name = trim($name);
92+
93+
if (strlen($name) == 1 && in_array($name, ['C', 'c', 'R', 'r'])) {
94+
throw new PhpSpreadsheetException('The table name is invalid');
95+
}
96+
if (strlen($name) > 255) {
97+
throw new PhpSpreadsheetException('The table name cannot be longer than 255 characters');
98+
}
99+
// Check for A1 or R1C1 cell reference notation
100+
if (
101+
preg_match(Coordinate::A1_COORDINATE_REGEX, $name) ||
102+
preg_match('/^R\[?\-?[0-9]*\]?C\[?\-?[0-9]*\]?$/i', $name)
103+
) {
104+
throw new PhpSpreadsheetException('The table name can\'t be the same as a cell reference');
105+
}
106+
if (!preg_match('/^[A-Z_\\\\]/i', $name)) {
107+
throw new PhpSpreadsheetException('The table name must begin a name with a letter, an underscore character (_), or a backslash (\)');
108+
}
109+
if (!preg_match('/^[A-Z_\\\\][A-Z0-9\._]+$/i', $name)) {
110+
throw new PhpSpreadsheetException('The table name contains invalid characters');
111+
}
112+
113+
$this->name = $name;
92114

93115
return $this;
94116
}
@@ -216,6 +238,18 @@ public function getWorksheet()
216238
*/
217239
public function setWorksheet(?Worksheet $worksheet = null)
218240
{
241+
if ($this->name != '' && $worksheet != null) {
242+
$spreadsheet = $worksheet->getParent();
243+
244+
foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
245+
foreach ($sheet->getTableCollection() as $table) {
246+
if ($table->getName() == $this->name) {
247+
throw new PhpSpreadsheetException("Workbook already contains a table named '{$this->name}'");
248+
}
249+
}
250+
}
251+
}
252+
219253
$this->workSheet = $worksheet;
220254

221255
return $this;

tests/PhpSpreadsheetTests/Worksheet/Table/TableTest.php

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,78 @@ public function testToString(): void
2222
self::assertEquals($expectedResult, $result);
2323
}
2424

25-
public function testVariousSets(): void
25+
/**
26+
* @dataProvider validTableNamesProvider
27+
*/
28+
public function testValidTableNames(string $name, string $expected): void
2629
{
2730
$sheet = $this->getSheet();
2831
$table = new Table(self::INITIAL_RANGE, $sheet);
2932

30-
$result = $table->setName('Table 1');
33+
$result = $table->setName($name);
3134
self::assertInstanceOf(Table::class, $result);
32-
// Spaces will be converted to underscore
33-
self::assertEquals('Table_1', $table->getName());
35+
self::assertEquals($expected, $table->getName());
36+
}
37+
38+
public function validTableNamesProvider(): array
39+
{
40+
return [
41+
['Table_1', 'Table_1'],
42+
['_table_2', '_table_2'],
43+
['\table_3', '\table_3'],
44+
[" Table_4 \n", 'Table_4'],
45+
];
46+
}
47+
48+
/**
49+
* @dataProvider invalidTableNamesProvider
50+
*/
51+
public function testInvalidTableNames(string $name): void
52+
{
53+
$sheet = $this->getSheet();
54+
$table = new Table(self::INITIAL_RANGE, $sheet);
55+
56+
$this->expectException(PhpSpreadsheetException::class);
57+
58+
$table->setName($name);
59+
}
60+
61+
public function invalidTableNamesProvider(): array
62+
{
63+
return [
64+
['C'],
65+
['c'],
66+
['R'],
67+
['r'],
68+
['Z100'],
69+
['Z$100'],
70+
['R1C1'],
71+
['R1C'],
72+
['R11C11'],
73+
['123'],
74+
['=Table'],
75+
[bin2hex(random_bytes(255))], // random string with length greater than 255
76+
];
77+
}
78+
79+
public function testUniqueTableName(): void
80+
{
81+
$this->expectException(PhpSpreadsheetException::class);
82+
$sheet = $this->getSheet();
83+
84+
$table1 = new Table();
85+
$table1->setName('Table_1');
86+
$sheet->addTable($table1);
87+
88+
$table2 = new Table();
89+
$table2->setName('Table_1');
90+
$sheet->addTable($table2);
91+
}
92+
93+
public function testVariousSets(): void
94+
{
95+
$sheet = $this->getSheet();
96+
$table = new Table(self::INITIAL_RANGE, $sheet);
3497

3598
$result = $table->setShowHeaderRow(false);
3699
self::assertInstanceOf(Table::class, $result);

0 commit comments

Comments
 (0)