See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 3 namespace PhpOffice\PhpSpreadsheet\Worksheet; 4 5 use PhpOffice\PhpSpreadsheet\Cell\AddressRange; 6 use PhpOffice\PhpSpreadsheet\Cell\Coordinate; 7 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; 8 use PhpOffice\PhpSpreadsheet\Shared\StringHelper; 9 use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle; 10 11 class Table 12 { 13 /** 14 * Table Name. 15 * 16 * @var string 17 */ 18 private $name = ''; 19 20 /** 21 * Show Header Row. 22 * 23 * @var bool 24 */ 25 private $showHeaderRow = true; 26 27 /** 28 * Show Totals Row. 29 * 30 * @var bool 31 */ 32 private $showTotalsRow = false; 33 34 /** 35 * Table Range. 36 * 37 * @var string 38 */ 39 private $range = ''; 40 41 /** 42 * Table Worksheet. 43 * 44 * @var null|Worksheet 45 */ 46 private $workSheet; 47 48 /** 49 * Table Column. 50 * 51 * @var Table\Column[] 52 */ 53 private $columns = []; 54 55 /** 56 * Table Style. 57 * 58 * @var TableStyle 59 */ 60 private $style; 61 62 /** 63 * Create a new Table. 64 * 65 * @param AddressRange|array<int>|string $range 66 * A simple string containing a Cell range like 'A1:E10' is permitted 67 * or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]), 68 * or an AddressRange object. 69 * @param string $name (e.g. Table1) 70 */ 71 public function __construct($range = '', string $name = '') 72 { 73 $this->setRange($range); 74 $this->setName($name); 75 $this->style = new TableStyle(); 76 } 77 78 /** 79 * Get Table name. 80 */ 81 public function getName(): string 82 { 83 return $this->name; 84 } 85 86 /** 87 * Set Table name. 88 */ 89 public function setName(string $name): self 90 { 91 $name = trim($name); 92 93 if (!empty($name)) { 94 if (strlen($name) === 1 && in_array($name, ['C', 'c', 'R', 'r'])) { 95 throw new PhpSpreadsheetException('The table name is invalid'); 96 } 97 if (strlen($name) > 255) { 98 throw new PhpSpreadsheetException('The table name cannot be longer than 255 characters'); 99 } 100 // Check for A1 or R1C1 cell reference notation 101 if ( 102 preg_match(Coordinate::A1_COORDINATE_REGEX, $name) || 103 preg_match('/^R\[?\-?[0-9]*\]?C\[?\-?[0-9]*\]?$/i', $name) 104 ) { 105 throw new PhpSpreadsheetException('The table name can\'t be the same as a cell reference'); 106 } 107 if (!preg_match('/^[\p{L}_\\\\]/iu', $name)) { 108 throw new PhpSpreadsheetException('The table name must begin a name with a letter, an underscore character (_), or a backslash (\)'); 109 } 110 if (!preg_match('/^[\p{L}_\\\\][\p{L}\p{M}0-9\._]+$/iu', $name)) { 111 throw new PhpSpreadsheetException('The table name contains invalid characters'); 112 } 113 } 114 115 $this->name = $name; 116 117 return $this; 118 } 119 120 /** 121 * Get show Header Row. 122 */ 123 public function getShowHeaderRow(): bool 124 { 125 return $this->showHeaderRow; 126 } 127 128 /** 129 * Set show Header Row. 130 */ 131 public function setShowHeaderRow(bool $showHeaderRow): self 132 { 133 $this->showHeaderRow = $showHeaderRow; 134 135 return $this; 136 } 137 138 /** 139 * Get show Totals Row. 140 */ 141 public function getShowTotalsRow(): bool 142 { 143 return $this->showTotalsRow; 144 } 145 146 /** 147 * Set show Totals Row. 148 */ 149 public function setShowTotalsRow(bool $showTotalsRow): self 150 { 151 $this->showTotalsRow = $showTotalsRow; 152 153 return $this; 154 } 155 156 /** 157 * Get Table Range. 158 */ 159 public function getRange(): string 160 { 161 return $this->range; 162 } 163 164 /** 165 * Set Table Cell Range. 166 * 167 * @param AddressRange|array<int>|string $range 168 * A simple string containing a Cell range like 'A1:E10' is permitted 169 * or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]), 170 * or an AddressRange object. 171 */ 172 public function setRange($range = ''): self 173 { 174 // extract coordinate 175 if ($range !== '') { 176 [, $range] = Worksheet::extractSheetTitle(Validations::validateCellRange($range), true); 177 } 178 if (empty($range)) { 179 // Discard all column rules 180 $this->columns = []; 181 $this->range = ''; 182 183 return $this; 184 } 185 186 if (strpos($range, ':') === false) { 187 throw new PhpSpreadsheetException('Table must be set on a range of cells.'); 188 } 189 190 [$width, $height] = Coordinate::rangeDimension($range); 191 if ($width < 1 || $height < 2) { 192 throw new PhpSpreadsheetException('The table range must be at least 1 column and 2 rows'); 193 } 194 195 $this->range = $range; 196 // Discard any column ruless that are no longer valid within this range 197 [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); 198 foreach ($this->columns as $key => $value) { 199 $colIndex = Coordinate::columnIndexFromString($key); 200 if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) { 201 unset($this->columns[$key]); 202 } 203 } 204 205 return $this; 206 } 207 208 /** 209 * Set Table Cell Range to max row. 210 */ 211 public function setRangeToMaxRow(): self 212 { 213 if ($this->workSheet !== null) { 214 $thisrange = $this->range; 215 $range = (string) preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange); 216 if ($range !== $thisrange) { 217 $this->setRange($range); 218 } 219 } 220 221 return $this; 222 } 223 224 /** 225 * Get Table's Worksheet. 226 */ 227 public function getWorksheet(): ?Worksheet 228 { 229 return $this->workSheet; 230 } 231 232 /** 233 * Set Table's Worksheet. 234 */ 235 public function setWorksheet(?Worksheet $worksheet = null): self 236 { 237 if ($this->name !== '' && $worksheet !== null) { 238 $spreadsheet = $worksheet->getParent(); 239 $tableName = StringHelper::strToUpper($this->name); 240 241 foreach ($spreadsheet->getWorksheetIterator() as $sheet) { 242 foreach ($sheet->getTableCollection() as $table) { 243 if (StringHelper::strToUpper($table->getName()) === $tableName) { 244 throw new PhpSpreadsheetException("Workbook already contains a table named '{$this->name}'"); 245 } 246 } 247 } 248 } 249 250 $this->workSheet = $worksheet; 251 252 return $this; 253 } 254 255 /** 256 * Get all Table Columns. 257 * 258 * @return Table\Column[] 259 */ 260 public function getColumns(): array 261 { 262 return $this->columns; 263 } 264 265 /** 266 * Validate that the specified column is in the Table range. 267 * 268 * @param string $column Column name (e.g. A) 269 * 270 * @return int The column offset within the table range 271 */ 272 public function isColumnInRange(string $column): int 273 { 274 if (empty($this->range)) { 275 throw new PhpSpreadsheetException('No table range is defined.'); 276 } 277 278 $columnIndex = Coordinate::columnIndexFromString($column); 279 [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); 280 if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) { 281 throw new PhpSpreadsheetException('Column is outside of current table range.'); 282 } 283 284 return $columnIndex - $rangeStart[0]; 285 } 286 287 /** 288 * Get a specified Table Column Offset within the defined Table range. 289 * 290 * @param string $column Column name (e.g. A) 291 * 292 * @return int The offset of the specified column within the table range 293 */ 294 public function getColumnOffset($column): int 295 { 296 return $this->isColumnInRange($column); 297 } 298 299 /** 300 * Get a specified Table Column. 301 * 302 * @param string $column Column name (e.g. A) 303 */ 304 public function getColumn($column): Table\Column 305 { 306 $this->isColumnInRange($column); 307 308 if (!isset($this->columns[$column])) { 309 $this->columns[$column] = new Table\Column($column, $this); 310 } 311 312 return $this->columns[$column]; 313 } 314 315 /** 316 * Get a specified Table Column by it's offset. 317 * 318 * @param int $columnOffset Column offset within range (starting from 0) 319 */ 320 public function getColumnByOffset($columnOffset): Table\Column 321 { 322 [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); 323 $pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $columnOffset); 324 325 return $this->getColumn($pColumn); 326 } 327 328 /** 329 * Set Table. 330 * 331 * @param string|Table\Column $columnObjectOrString 332 * A simple string containing a Column ID like 'A' is permitted 333 */ 334 public function setColumn($columnObjectOrString): self 335 { 336 if ((is_string($columnObjectOrString)) && (!empty($columnObjectOrString))) { 337 $column = $columnObjectOrString; 338 } elseif (is_object($columnObjectOrString) && ($columnObjectOrString instanceof Table\Column)) { 339 $column = $columnObjectOrString->getColumnIndex(); 340 } else { 341 throw new PhpSpreadsheetException('Column is not within the table range.'); 342 } 343 $this->isColumnInRange($column); 344 345 if (is_string($columnObjectOrString)) { 346 $this->columns[$columnObjectOrString] = new Table\Column($columnObjectOrString, $this); 347 } else { 348 $columnObjectOrString->setTable($this); 349 $this->columns[$column] = $columnObjectOrString; 350 } 351 ksort($this->columns); 352 353 return $this; 354 } 355 356 /** 357 * Clear a specified Table Column. 358 * 359 * @param string $column Column name (e.g. A) 360 */ 361 public function clearColumn($column): self 362 { 363 $this->isColumnInRange($column); 364 365 if (isset($this->columns[$column])) { 366 unset($this->columns[$column]); 367 } 368 369 return $this; 370 } 371 372 /** 373 * Shift an Table Column Rule to a different column. 374 * 375 * Note: This method bypasses validation of the destination column to ensure it is within this Table range. 376 * Nor does it verify whether any column rule already exists at $toColumn, but will simply override any existing value. 377 * Use with caution. 378 * 379 * @param string $fromColumn Column name (e.g. A) 380 * @param string $toColumn Column name (e.g. B) 381 */ 382 public function shiftColumn($fromColumn, $toColumn): self 383 { 384 $fromColumn = strtoupper($fromColumn); 385 $toColumn = strtoupper($toColumn); 386 387 if (($fromColumn !== null) && (isset($this->columns[$fromColumn])) && ($toColumn !== null)) { 388 $this->columns[$fromColumn]->setTable(); 389 $this->columns[$fromColumn]->setColumnIndex($toColumn); 390 $this->columns[$toColumn] = $this->columns[$fromColumn]; 391 $this->columns[$toColumn]->setTable($this); 392 unset($this->columns[$fromColumn]); 393 394 ksort($this->columns); 395 } 396 397 return $this; 398 } 399 400 /** 401 * Get table Style. 402 */ 403 public function getStyle(): Table\TableStyle 404 { 405 return $this->style; 406 } 407 408 /** 409 * Set table Style. 410 */ 411 public function setStyle(TableStyle $style): self 412 { 413 $this->style = $style; 414 415 return $this; 416 } 417 418 /** 419 * Implement PHP __clone to create a deep clone, not just a shallow copy. 420 */ 421 public function __clone() 422 { 423 $vars = get_object_vars($this); 424 foreach ($vars as $key => $value) { 425 if (is_object($value)) { 426 if ($key === 'workSheet') { 427 // Detach from worksheet 428 $this->{$key} = null; 429 } else { 430 $this->{$key} = clone $value; 431 } 432 } elseif ((is_array($value)) && ($key === 'columns')) { 433 // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table objects 434 $this->{$key} = []; 435 foreach ($value as $k => $v) { 436 $this->{$key}[$k] = clone $v; 437 // attach the new cloned Column to this new cloned Table object 438 $this->{$key}[$k]->setTable($this); 439 } 440 } else { 441 $this->{$key} = $value; 442 } 443 } 444 } 445 446 /** 447 * toString method replicates previous behavior by returning the range if object is 448 * referenced as a property of its worksheet. 449 */ 450 public function __toString() 451 { 452 return (string) $this->range; 453 } 454 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body