ok
Direktori : /home/ngwcolle/www/admin/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/ |
Current File : /home/ngwcolle/www/admin/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php |
<?php namespace PhpOffice\PhpSpreadsheet\Collection; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Settings; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use Psr\SimpleCache\CacheInterface; class Cells { protected const MAX_COLUMN_ID = 16384; private CacheInterface $cache; /** * Parent worksheet. */ private ?Worksheet $parent; /** * The currently active Cell. */ private ?Cell $currentCell = null; /** * Coordinate of the currently active Cell. */ private ?string $currentCoordinate = null; /** * Flag indicating whether the currently active Cell requires saving. */ private bool $currentCellIsDirty = false; /** * An index of existing cells. int pointer to the coordinate (0-base-indexed row * 16,384 + 1-base indexed column) * indexed by their coordinate. * * @var int[] */ private array $index = []; /** * Prefix used to uniquely identify cache data for this worksheet. */ private string $cachePrefix; /** * Initialise this new cell collection. * * @param Worksheet $parent The worksheet for this cell collection */ public function __construct(Worksheet $parent, CacheInterface $cache) { // Set our parent worksheet. // This is maintained here to facilitate re-attaching it to Cell objects when // they are woken from a serialized state $this->parent = $parent; $this->cache = $cache; $this->cachePrefix = $this->getUniqueID(); } /** * Return the parent worksheet for this cell collection. */ public function getParent(): ?Worksheet { return $this->parent; } /** * Whether the collection holds a cell for the given coordinate. * * @param string $cellCoordinate Coordinate of the cell to check */ public function has(string $cellCoordinate): bool { return ($cellCoordinate === $this->currentCoordinate) || isset($this->index[$cellCoordinate]); } /** * Add or update a cell in the collection. * * @param Cell $cell Cell to update */ public function update(Cell $cell): Cell { return $this->add($cell->getCoordinate(), $cell); } /** * Delete a cell in cache identified by coordinate. * * @param string $cellCoordinate Coordinate of the cell to delete */ public function delete(string $cellCoordinate): void { if ($cellCoordinate === $this->currentCoordinate && $this->currentCell !== null) { $this->currentCell->detach(); $this->currentCoordinate = null; $this->currentCell = null; $this->currentCellIsDirty = false; } unset($this->index[$cellCoordinate]); // Delete the entry from cache $this->cache->delete($this->cachePrefix . $cellCoordinate); } /** * Get a list of all cell coordinates currently held in the collection. * * @return string[] */ public function getCoordinates(): array { return array_keys($this->index); } /** * Get a sorted list of all cell coordinates currently held in the collection by row and column. * * @return string[] */ public function getSortedCoordinates(): array { asort($this->index); return array_keys($this->index); } /** * Get a sorted list of all cell coordinates currently held in the collection by index (16384*row+column). * * @return int[] */ public function getSortedCoordinatesInt(): array { asort($this->index); return array_values($this->index); } /** * Return the cell coordinate of the currently active cell object. */ public function getCurrentCoordinate(): ?string { return $this->currentCoordinate; } /** * Return the column coordinate of the currently active cell object. */ public function getCurrentColumn(): string { $column = 0; $row = ''; sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row); return (string) $column; } /** * Return the row coordinate of the currently active cell object. */ public function getCurrentRow(): int { $column = 0; $row = ''; sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row); return (int) $row; } /** * Get highest worksheet column and highest row that have cell records. * * @return array Highest column name and highest row number */ public function getHighestRowAndColumn(): array { // Lookup highest column and highest row $maxRow = $maxColumn = 1; foreach ($this->index as $coordinate) { $row = (int) floor(($coordinate - 1) / self::MAX_COLUMN_ID) + 1; $maxRow = ($maxRow > $row) ? $maxRow : $row; $column = ($coordinate % self::MAX_COLUMN_ID) ?: self::MAX_COLUMN_ID; $maxColumn = ($maxColumn > $column) ? $maxColumn : $column; } return [ 'row' => $maxRow, 'column' => Coordinate::stringFromColumnIndex($maxColumn), ]; } /** * Get highest worksheet column. * * @param null|int|string $row Return the highest column for the specified row, * or the highest column of any row if no row number is passed * * @return string Highest column name */ public function getHighestColumn($row = null): string { if ($row === null) { return $this->getHighestRowAndColumn()['column']; } $row = (int) $row; if ($row <= 0) { throw new PhpSpreadsheetException('Row number must be a positive integer'); } $maxColumn = 1; $toRow = $row * self::MAX_COLUMN_ID; $fromRow = --$row * self::MAX_COLUMN_ID; foreach ($this->index as $coordinate) { if ($coordinate < $fromRow || $coordinate >= $toRow) { continue; } $column = ($coordinate % self::MAX_COLUMN_ID) ?: self::MAX_COLUMN_ID; $maxColumn = $maxColumn > $column ? $maxColumn : $column; } return Coordinate::stringFromColumnIndex($maxColumn); } /** * Get highest worksheet row. * * @param null|string $column Return the highest row for the specified column, * or the highest row of any column if no column letter is passed * * @return int Highest row number */ public function getHighestRow(?string $column = null): int { if ($column === null) { return $this->getHighestRowAndColumn()['row']; } $maxRow = 1; $columnIndex = Coordinate::columnIndexFromString($column); foreach ($this->index as $coordinate) { if ($coordinate % self::MAX_COLUMN_ID !== $columnIndex) { continue; } $row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1; $maxRow = ($maxRow > $row) ? $maxRow : $row; } return $maxRow; } /** * Generate a unique ID for cache referencing. * * @return string Unique Reference */ private function getUniqueID(): string { $cacheType = Settings::getCache(); return ($cacheType instanceof Memory\SimpleCache1 || $cacheType instanceof Memory\SimpleCache3) ? random_bytes(7) . ':' : uniqid('phpspreadsheet.', true) . '.'; } /** * Clone the cell collection. */ public function cloneCellCollection(Worksheet $worksheet): static { $this->storeCurrentCell(); $newCollection = clone $this; $newCollection->parent = $worksheet; $newCollection->cachePrefix = $newCollection->getUniqueID(); foreach ($this->index as $key => $value) { $newCollection->index[$key] = $value; $stored = $newCollection->cache->set( $newCollection->cachePrefix . $key, clone $this->cache->get($this->cachePrefix . $key) ); if ($stored === false) { $this->destructIfNeeded($newCollection, 'Failed to copy cells in cache'); } } return $newCollection; } /** * Remove a row, deleting all cells in that row. * * @param int|string $row Row number to remove */ public function removeRow($row): void { $this->storeCurrentCell(); $row = (int) $row; if ($row <= 0) { throw new PhpSpreadsheetException('Row number must be a positive integer'); } $toRow = $row * self::MAX_COLUMN_ID; $fromRow = --$row * self::MAX_COLUMN_ID; foreach ($this->index as $coordinate) { if ($coordinate >= $fromRow && $coordinate < $toRow) { $row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1; $column = Coordinate::stringFromColumnIndex($coordinate % self::MAX_COLUMN_ID); $this->delete("{$column}{$row}"); } } } /** * Remove a column, deleting all cells in that column. * * @param string $column Column ID to remove */ public function removeColumn(string $column): void { $this->storeCurrentCell(); $columnIndex = Coordinate::columnIndexFromString($column); foreach ($this->index as $coordinate) { if ($coordinate % self::MAX_COLUMN_ID === $columnIndex) { $row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1; $column = Coordinate::stringFromColumnIndex($coordinate % self::MAX_COLUMN_ID); $this->delete("{$column}{$row}"); } } } /** * Store cell data in cache for the current cell object if it's "dirty", * and the 'nullify' the current cell object. */ private function storeCurrentCell(): void { if ($this->currentCellIsDirty && isset($this->currentCoordinate, $this->currentCell)) { $this->currentCell->detach(); $stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell); if ($stored === false) { $this->destructIfNeeded($this, "Failed to store cell {$this->currentCoordinate} in cache"); } $this->currentCellIsDirty = false; } $this->currentCoordinate = null; $this->currentCell = null; } private function destructIfNeeded(self $cells, string $message): void { $cells->__destruct(); throw new PhpSpreadsheetException($message); } /** * Add or update a cell identified by its coordinate into the collection. * * @param string $cellCoordinate Coordinate of the cell to update * @param Cell $cell Cell to update */ public function add(string $cellCoordinate, Cell $cell): Cell { if ($cellCoordinate !== $this->currentCoordinate) { $this->storeCurrentCell(); } $column = 0; $row = ''; sscanf($cellCoordinate, '%[A-Z]%d', $column, $row); $this->index[$cellCoordinate] = (--$row * self::MAX_COLUMN_ID) + Coordinate::columnIndexFromString((string) $column); $this->currentCoordinate = $cellCoordinate; $this->currentCell = $cell; $this->currentCellIsDirty = true; return $cell; } /** * Get cell at a specific coordinate. * * @param string $cellCoordinate Coordinate of the cell * * @return null|Cell Cell that was found, or null if not found */ public function get(string $cellCoordinate): ?Cell { if ($cellCoordinate === $this->currentCoordinate) { return $this->currentCell; } $this->storeCurrentCell(); // Return null if requested entry doesn't exist in collection if ($this->has($cellCoordinate) === false) { return null; } // Check if the entry that has been requested actually exists in the cache $cell = $this->cache->get($this->cachePrefix . $cellCoordinate); if ($cell === null) { throw new PhpSpreadsheetException("Cell entry {$cellCoordinate} no longer exists in cache. This probably means that the cache was cleared by someone else."); } // Set current entry to the requested entry $this->currentCoordinate = $cellCoordinate; $this->currentCell = $cell; // Re-attach this as the cell's parent $this->currentCell->attach($this); // Return requested entry return $this->currentCell; } /** * Clear the cell collection and disconnect from our parent. */ public function unsetWorksheetCells(): void { if ($this->currentCell !== null) { $this->currentCell->detach(); $this->currentCell = null; $this->currentCoordinate = null; } // Flush the cache $this->__destruct(); $this->index = []; // detach ourself from the worksheet, so that it can then delete this object successfully $this->parent = null; } /** * Destroy this cell collection. */ public function __destruct() { $this->cache->deleteMultiple($this->getAllCacheKeys()); $this->parent = null; } /** * Returns all known cache keys. * * @return iterable<string> */ private function getAllCacheKeys(): iterable { foreach ($this->index as $coordinate => $value) { yield $this->cachePrefix . $coordinate; } } }