diff --git a/Classes/PHPExcel/CachedObjectStorage/CacheBase.php b/Classes/PHPExcel/CachedObjectStorage/CacheBase.php index 6119a348..4dd06fdd 100644 --- a/Classes/PHPExcel/CachedObjectStorage/CacheBase.php +++ b/Classes/PHPExcel/CachedObjectStorage/CacheBase.php @@ -85,7 +85,6 @@ class PHPExcel_CachedObjectStorage_CacheBase { * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? * * @param string $pCoord Coordinate address of the cell to check - * @return void * @return boolean */ public function isDataSet($pCoord) { @@ -146,7 +145,7 @@ class PHPExcel_CachedObjectStorage_CacheBase { */ public function getSortedCellList() { $sortKeys = array(); - foreach (array_keys($this->_cellCache) as $coord) { + foreach ($this->getCellList() as $coord) { list($column,$row) = sscanf($coord,'%[A-Z]%d'); $sortKeys[sprintf('%09d%3s',$row,$column)] = $coord; } diff --git a/Classes/PHPExcel/CachedObjectStorage/SQLite.php b/Classes/PHPExcel/CachedObjectStorage/SQLite.php new file mode 100644 index 00000000..6a74f580 --- /dev/null +++ b/Classes/PHPExcel/CachedObjectStorage/SQLite.php @@ -0,0 +1,211 @@ +_currentCellIsDirty) { + $this->_currentObject->detach(); + + $this->_DBHandle->queryExec("INSERT OR REPLACE INTO kvp_".$this->_TableName." VALUES('".$this->_currentObjectID."','".sqlite_escape_string(serialize($this->_currentObject))."')"); + $this->_currentCellIsDirty = false; + } + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + $this->_currentCellIsDirty = true; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws 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(); + + $query = "SELECT value FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; + $cellResultSet = $this->_DBHandle->query($query,SQLITE_ASSOC); + if ($cellResultSet->numRows() == 0) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + + $cellResult = $cellResultSet->fetchSingle(); + $this->_currentObject = unserialize($cellResult); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Is a value set for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return boolean + */ + public function isDataSet($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return true; + } + + // Check if the requested entry exists in the cache + $query = "SELECT id FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; + $cellResultSet = $this->_DBHandle->query($query,SQLITE_ASSOC); + if ($cellResultSet->numRows() == 0) { + // Return null if requested entry doesn't exist in cache + return false; + } + return true; + } // function isDataSet() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + $this->_currentObject->detach(); + $this->_currentObjectID = $this->_currentObject = null; + } + + // Check if the requested entry exists in the cache + $query = "DELETE FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; + $this->_DBHandle->queryExec($query); + + $this->_currentCellIsDirty = false; + } // function deleteCacheData() + + + /** + * Get a list of all cell addresses currently held in cache + * + * @return array of string + */ + public function getCellList() { + $query = "SELECT id FROM kvp_".$this->_TableName; + $cellIdsResult = $this->_DBHandle->unbufferedQuery($query,SQLITE_ASSOC); + + $cellKeys = array(); + foreach($cellIdsResult as $row) { + $cellKeys[] = $row['id']; + } + + return $cellKeys; + } // function getCellList() + + + /** + * Clone the cell collection + * + * @return void + */ + public function copyCellCollection(PHPExcel_Worksheet $parent) { + // Get a new id for the new table name + $tableName = str_replace('.','_',$this->_getUniqueID()); + $this->_DBHandle->queryExec('CREATE TABLE kvp_'.$tableName.' (id VARCHAR(12) PRIMARY KEY, value BLOB) + AS SELECT * FROM kvp_'.$this->_TableName); + + // Copy the existing cell cache file + $this->_TableName = $tableName; + } // function copyCellCollection() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + + // Close down the temporary cache file + $this->__destruct(); + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent) { + parent::__construct($parent); + if (is_null($this->_DBHandle)) { + $this->_TableName = str_replace('.','_',$this->_getUniqueID()); + $_DBName = ':memory:'; + + $this->_DBHandle = new SQLiteDatabase($_DBName); + $this->_DBHandle->queryExec('CREATE TABLE kvp_'.$this->_TableName.' (id VARCHAR(12) PRIMARY KEY, value BLOB)'); + } + } // function __construct() + + + public function __destruct() { + $this->_DBHandle = null; + } // function __destruct() + +} diff --git a/Classes/PHPExcel/CachedObjectStorage/SQLite3.php b/Classes/PHPExcel/CachedObjectStorage/SQLite3.php new file mode 100644 index 00000000..0a495209 --- /dev/null +++ b/Classes/PHPExcel/CachedObjectStorage/SQLite3.php @@ -0,0 +1,219 @@ +_currentCellIsDirty) { + $this->_currentObject->detach(); + + $query = $this->_DBHandle->prepare("INSERT OR REPLACE INTO kvp_".$this->_TableName." VALUES(:id,:data)"); + $query->bindValue('id',$this->_currentObjectID,SQLITE3_TEXT); + $query->bindValue('data',serialize($this->_currentObject),SQLITE3_BLOB); + $query->execute(); + $this->_currentCellIsDirty = false; + } + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + $this->_currentCellIsDirty = true; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws 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(); + + $query = "SELECT value FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; + $cellResult = $this->_DBHandle->querySingle($query); + if (is_null($cellResult)) { + // 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($cellResult); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Is a value set for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return boolean + */ + public function isDataSet($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return true; + } + + // Check if the requested entry exists in the cache + $query = "SELECT id FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; + $cellResult = $this->_DBHandle->querySingle($query); + if (is_null($cellResult)) { + // Return null if requested entry doesn't exist in cache + return false; + } + return true; + } // function isDataSet() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + $this->_currentObject->detach(); + $this->_currentObjectID = $this->_currentObject = null; + } + + // Check if the requested entry exists in the cache + $query = "DELETE FROM kvp_".$this->_TableName." WHERE id='".$pCoord."'"; + $this->_DBHandle->exec($query); + + $this->_currentCellIsDirty = false; + } // function deleteCacheData() + + + /** + * Get a list of all cell addresses currently held in cache + * + * @return array of string + */ + public function getCellList() { + $query = "SELECT id FROM kvp_".$this->_TableName; + $cellIdsResult = $this->_DBHandle->query($query); + + $cellKeys = array(); + while ($row = $cellIdsResult->fetchArray(SQLITE3_ASSOC)) { + $cellKeys[] = $row['id']; + } + + return $cellKeys; + } // function getCellList() + + + /** + * Clone the cell collection + * + * @return void + */ + public function copyCellCollection(PHPExcel_Worksheet $parent) { + // Get a new id for the new table name + $tableName = str_replace('.','_',$this->_getUniqueID()); + $this->_DBHandle->exec('CREATE TABLE kvp_'.$tableName.' (id VARCHAR(12) PRIMARY KEY, value BLOB) + AS SELECT * FROM kvp_'.$this->_TableName); + + // Copy the existing cell cache file + $this->_TableName = $tableName; + } // function copyCellCollection() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + + // Close down the temporary cache file + $this->__destruct(); + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent) { + parent::__construct($parent); + if (is_null($this->_DBHandle)) { + $this->_TableName = str_replace('.','_',$this->_getUniqueID()); + $_DBName = ':memory:'; +// $_DBName = PHPExcel_Shared_File::sys_get_temp_dir().'/PHPExcel.sqlite3'; + + $this->_DBHandle = new SQLite3($_DBName); + $this->_DBHandle->exec('CREATE TABLE kvp_'.$this->_TableName.' (id VARCHAR(12) PRIMARY KEY, value BLOB)'); + } + } // function __construct() + + + public function __destruct() { + if (!is_null($this->_DBHandle)) { +// $this->_DBHandle->exec('DROP TABLE kvp_'.$this->_TableName); + + $this->_DBHandle->close(); + } + $this->_DBHandle = null; + } // function __destruct() + +} diff --git a/Classes/PHPExcel/CachedObjectStorageFactory.php b/Classes/PHPExcel/CachedObjectStorageFactory.php index cd402b23..d6e39b02 100644 --- a/Classes/PHPExcel/CachedObjectStorageFactory.php +++ b/Classes/PHPExcel/CachedObjectStorageFactory.php @@ -9,6 +9,8 @@ class PHPExcel_CachedObjectStorageFactory { const cache_to_memcache = 'Memcache'; const cache_to_phpTemp = 'PHPTemp'; const cache_to_wincache = 'Wincache'; + const cache_to_sqlite = 'SQLite'; + const cache_to_sqlite3 = 'SQLite3'; private static $_cacheStorageMethod = null; @@ -25,6 +27,8 @@ class PHPExcel_CachedObjectStorageFactory { self::cache_to_apc, self::cache_to_memcache, self::cache_to_wincache, + self::cache_to_sqlite, + self::cache_to_sqlite3, ); @@ -46,7 +50,11 @@ class PHPExcel_CachedObjectStorageFactory { 'cacheTime' => 600 ), self::cache_to_wincache => array( 'cacheTime' => 600 - ) + ), + self::cache_to_sqlite => array( + ), + self::cache_to_sqlite3 => array( + ), ); @@ -98,6 +106,16 @@ class PHPExcel_CachedObjectStorageFactory { return false; } break; + case self::cache_to_sqlite : + if (!function_exists('sqlite_open')) { + return false; + } + break; + case self::cache_to_sqlite3 : + if (!class_exists('SQLite3')) { + return false; + } + break; } self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method]; diff --git a/Documentation/PHPExcel developer documentation.doc b/Documentation/PHPExcel developer documentation.doc index 02b7839d..cfa95e53 100644 Binary files a/Documentation/PHPExcel developer documentation.doc and b/Documentation/PHPExcel developer documentation.doc differ diff --git a/changelog.txt b/changelog.txt index 6535e0f2..2a29defe 100644 --- a/changelog.txt +++ b/changelog.txt @@ -24,6 +24,7 @@ Fixed in SVN: +- Feature: (MBaker) Option for cell caching using SQLite. - General: (MBaker) Fix to build to ensure that Examples are included with the documentation - General: (MBaker) Reduce cell caching overhead using dirty flag to ensure that cells are only rewritten to the cache if they have actually been changed - General: (MBaker) Improved memory usage in CSV Writer