diff --git a/Classes/PHPExcel/CachedObjectStorage/APCu.php b/Classes/PHPExcel/CachedObjectStorage/APCu.php new file mode 100644 index 000000000..57d02949b --- /dev/null +++ b/Classes/PHPExcel/CachedObjectStorage/APCu.php @@ -0,0 +1,290 @@ +currentCellIsDirty && !empty($this->currentObjectID)) { + $this->currentObject->detach(); + + if (!apcu_store( + $this->cachePrefix . $this->currentObjectID . '.cache', + serialize($this->currentObject), + $this->cacheTime + )) { + $this->__destruct(); + throw new PHPExcel_Exception('Failed to store cell ' . $this->currentObjectID . ' in APCu'); + } + $this->currentCellIsDirty = false; + } + $this->currentObjectID = $this->currentObject = null; + } + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @access public + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return PHPExcel_Cell + * @throws PHPExcel_Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) + { + if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) { + $this->storeData(); + } + $this->cellCache[$pCoord] = true; + + $this->currentObjectID = $pCoord; + $this->currentObject = $cell; + $this->currentCellIsDirty = true; + + return $cell; + } + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @access public + * @param string $pCoord Coordinate address of the cell to check + * @throws PHPExcel_Exception + * @return boolean + */ + public function isDataSet($pCoord) + { + // Check if the requested entry is the current object, or exists in the cache + if (parent::isDataSet($pCoord)) { + if ($this->currentObjectID == $pCoord) { + return true; + } + // Check if the requested entry still exists in apc + $success = apcu_fetch($this->cachePrefix.$pCoord.'.cache'); + if ($success === false) { + // Entry no longer exists in APCu, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new PHPExcel_Exception('Cell entry '.$pCoord.' no longer exists in APCu cache'); + } + return true; + } + return false; + } + + /** + * Get cell at a specific coordinate + * + * @access public + * @param string $pCoord Coordinate of the cell + * @throws PHPExcel_Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) + { + if ($pCoord === $this->currentObjectID) { + return $this->currentObject; + } + $this->storeData(); + + // Check if the entry that has been requested actually exists + if (parent::isDataSet($pCoord)) { + $obj = apcu_fetch($this->cachePrefix . $pCoord . '.cache'); + if ($obj === false) { + // Entry no longer exists in APCu, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new PHPExcel_Exception('Cell entry '.$pCoord.' no longer exists in APCu cache'); + } + } else { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->currentObjectID = $pCoord; + $this->currentObject = unserialize($obj); + // Re-attach this as the cell's parent + $this->currentObject->attach($this); + + // Return requested entry + return $this->currentObject; + } + + /** + * Get a list of all cell addresses currently held in cache + * + * @return string[] + */ + public function getCellList() + { + if ($this->currentObjectID !== null) { + $this->storeData(); + } + + return parent::getCellList(); + } + + /** + * Delete a cell in cache identified by coordinate address + * + * @access public + * @param string $pCoord Coordinate address of the cell to delete + * @throws PHPExcel_Exception + */ + public function deleteCacheData($pCoord) + { + // Delete the entry from APCu + apcu_delete($this->cachePrefix.$pCoord.'.cache'); + + // Delete the entry from our cell address array + parent::deleteCacheData($pCoord); + } + + /** + * Clone the cell collection + * + * @access public + * @param PHPExcel_Worksheet $parent The new worksheet + * @throws PHPExcel_Exception + * @return void + */ + public function copyCellCollection(PHPExcel_Worksheet $parent) + { + parent::copyCellCollection($parent); + // Get a new id for the new file name + $baseUnique = $this->getUniqueID(); + $newCachePrefix = substr(md5($baseUnique), 0, 8) . '.'; + $cacheList = $this->getCellList(); + foreach ($cacheList as $cellID) { + if ($cellID != $this->currentObjectID) { + $obj = apcu_fetch($this->cachePrefix . $cellID . '.cache'); + if ($obj === false) { + // Entry no longer exists in APCu, so clear it from the cache array + parent::deleteCacheData($cellID); + throw new PHPExcel_Exception('Cell entry ' . $cellID . ' no longer exists in APCu'); + } + if (!apcu_store($newCachePrefix . $cellID . '.cache', $obj, $this->cacheTime)) { + $this->__destruct(); + throw new PHPExcel_Exception('Failed to store cell ' . $cellID . ' in APCu'); + } + } + } + $this->cachePrefix = $newCachePrefix; + } + + /** + * Clear the cell collection and disconnect from our parent + * + * @return void + */ + public function unsetWorksheetCells() + { + if ($this->currentObject !== null) { + $this->currentObject->detach(); + $this->currentObject = $this->currentObjectID = null; + } + + // Flush the APCu cache + $this->__destruct(); + + $this->cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->parent = null; + } + + /** + * Initialise this new cell collection + * + * @param PHPExcel_Worksheet $parent The worksheet for this cell collection + * @param array of mixed $arguments Additional initialisation arguments + */ + public function __construct(PHPExcel_Worksheet $parent, $arguments) + { + $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; + + if ($this->cachePrefix === null) { + $baseUnique = $this->getUniqueID(); + $this->cachePrefix = substr(md5($baseUnique), 0, 8) . '.'; + $this->cacheTime = $cacheTime; + + parent::__construct($parent); + } + } + + /** + * Destroy this cell collection + */ + public function __destruct() + { + $cacheList = $this->getCellList(); + foreach ($cacheList as $cellID) { + apcu_delete($this->cachePrefix . $cellID . '.cache'); + } + } + + /** + * Identify whether the caching method is currently available + * Some methods are dependent on the availability of certain extensions being enabled in the PHP build + * + * @return boolean + */ + public static function cacheMethodIsAvailable() + { + if (!function_exists('apcu_store')) { + return false; + } + if (apcu_sma_info() === false) { + return false; + } + + return true; + } +} diff --git a/Classes/PHPExcel/CachedObjectStorageFactory.php b/Classes/PHPExcel/CachedObjectStorageFactory.php index 2da92346e..c6f84e6e6 100644 --- a/Classes/PHPExcel/CachedObjectStorageFactory.php +++ b/Classes/PHPExcel/CachedObjectStorageFactory.php @@ -42,6 +42,7 @@ class PHPExcel_CachedObjectStorageFactory const cache_igbinary = 'Igbinary'; const cache_to_discISAM = 'DiscISAM'; const cache_to_apc = 'APC'; + const cache_to_apcu = 'APCu'; const cache_to_memcache = 'Memcache'; const cache_to_phpTemp = 'PHPTemp'; const cache_to_wincache = 'Wincache'; @@ -77,6 +78,7 @@ class PHPExcel_CachedObjectStorageFactory self::cache_to_phpTemp, self::cache_to_discISAM, self::cache_to_apc, + self::cache_to_apcu, self::cache_to_memcache, self::cache_to_wincache, self::cache_to_sqlite, @@ -104,6 +106,8 @@ class PHPExcel_CachedObjectStorageFactory ), self::cache_to_apc => array( 'cacheTime' => 600 ), + self::cache_to_apcu => array( 'cacheTime' => 600 + ), self::cache_to_memcache => array( 'memcacheServer' => 'localhost', 'memcachePort' => 11211, 'cacheTime' => 600