Refactor CachedObjectStorage to PSR-16

This drop a lot of non-core features code and delegate their maintainance
to third parties. Also it open the door to any missing implementation
out of the box, such as Redis for the moment.

Finally this consistently enforce a constraint where there can be one and
only one active cell at any point in time in code. This used to be true for
non-default implementation of cache, but it was not true for default
implementation where all cells were kept in-memory and thus were never
detached from their worksheet and thus were all kept functionnal at any
point in time.

This inconsistency of behavior between in-memory and off-memory could
lead to bugs when changing cache system if the end-user code was badly
written. Now end-user will never be able to write buggy code in the first
place, avoiding future headache when introducing caching.

Closes #3
This commit is contained in:
Adrien Crivelli 2017-04-14 14:22:33 +09:00
parent 2d630fbbb9
commit fd9c925a7b
No known key found for this signature in database
GPG Key ID: B182FD79DC6DE92E
39 changed files with 1077 additions and 3275 deletions

View File

@ -28,7 +28,8 @@
"ext-iconv": "*", "ext-iconv": "*",
"ext-xml": "*", "ext-xml": "*",
"ext-xmlwriter": "*", "ext-xmlwriter": "*",
"ext-zip": "*" "ext-zip": "*",
"psr/simple-cache": "^1.0"
}, },
"require-dev": { "require-dev": {
"mpdf/mpdf": "^6.1", "mpdf/mpdf": "^6.1",

53
composer.lock generated
View File

@ -4,8 +4,57 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "f2f5913aedbb6dbe0a6e6a97fde25039", "content-hash": "81b6f3fcd32849e29ea13dd37c9af17f",
"packages": [], "packages": [
{
"name": "psr/simple-cache",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
"reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24",
"reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\SimpleCache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for simple caching",
"keywords": [
"cache",
"caching",
"psr",
"psr-16",
"simple-cache"
],
"time": "2017-01-02T13:31:39+00:00"
}
],
"packages-dev": [ "packages-dev": [
{ {
"name": "doctrine/instantiator", "name": "doctrine/instantiator",

View File

@ -0,0 +1,108 @@
# Memory saving
PhpSpreadsheet uses an average of about 1k per cell in your worksheets, so
large workbooks can quickly use up available memory. Cell caching
provides a mechanism that allows PhpSpreadsheet to maintain the cell
objects in a smaller size of memory, or off-memory (eg: on disk, in APCu,
memcache or redis). This allows you to reduce the memory usage for large
workbooks, although at a cost of speed to access cell data.
By default, PhpSpreadsheet holds all cell objects in memory, but
you can specify alternatives by providing your own
[PSR-16](http://www.php-fig.org/psr/psr-16/) implementation. PhpSpreadsheet keys
are automatically namespaced, and cleaned up after use, so a single cache
instance may be shared across several usage of PhpSpreadsheet or even with other
cache usages.
To enable cell caching, you must provide your own implementation of cache like so:
``` php
$cache = new MyCustomPsr16Implementation();
\PhpOffice\PhpSpreadsheet\Settings::setCache($cache);
```
A separate cache is maintained for each individual worksheet, and is
automatically created when the worksheet is instantiated based on the
settings that you have configured. You cannot change
the configuration settings once you have started to read a workbook, or
have created your first worksheet.
## Beware of TTL
As opposed to common cache concept, PhpSpreadsheet data cannot be re-generated
from scratch. If some data is stored and later is not retrievable,
PhpSpreadsheet will throw an exception.
That means that the data stored in cache **must not be deleted** by a
third-party or via TTL mechanism.
So be sure that TTL is either de-activated or long enough to cover the entire
usage of PhpSpreadsheet.
## Common use cases
PhpSpreadsheet does not ship with alternative cache implementation. It is up to
you to select the most appropriate implementation for your environnement. You
can either implement [PSR-16](http://www.php-fig.org/psr/psr-16/) from scratch,
or use [pre-existing libraries](https://packagist.org/search/?q=psr-16).
One such library is [PHP Cache](http://www.php-cache.com/) which
provides a wide range of alternatives. Refers to their documentation for
details, but here are a few suggestions that should get you started.
### APCu
Require the packages into your project:
```sh
composer require cache/simple-cache-bridge cache/apcu-adapter
```
Configure PhpSpreadsheet with something like:
```php
$pool = new \Cache\Adapter\Apcu\ApcuCachePool();
$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool);
\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache);
```
### Redis
Require the packages into your project:
```sh
composer require cache/simple-cache-bridge cache/redis-adapter
```
Configure PhpSpreadsheet with something like:
```php
$client = new \Redis();
$client->connect('127.0.0.1', 6379);
$pool = new \Cache\Adapter\Redis\RedisCachePool($client);
$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool);
\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache);
```
### Memcache
Require the packages into your project:
```sh
composer require cache/simple-cache-bridge cache/memcache-adapter
```
Configure PhpSpreadsheet with something like:
```php
$client = new \Memcache();
$client->connect('localhost', 11211);
$pool = new \Cache\Adapter\Memcache\MemcacheCachePool($client);
$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool);
\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache);
```

View File

@ -136,7 +136,8 @@ $rendererName = \PhpOffice\PhpSpreadsheet\Settings::PDF_RENDERER_MPDF;
## PclZip and ZipArchive ## PclZip and ZipArchive
Support for PclZip were dropped in favor of the more complete and modern Support for PclZip were dropped in favor of the more complete and modern
PHP extension ZipArchive. So the following were removed: [PHP extension ZipArchive](http://php.net/manual/en/book.zip.php).
So the following were removed:
- `PclZip` - `PclZip`
- `PHPExcel_Settings::setZipClass()` - `PHPExcel_Settings::setZipClass()`
@ -144,3 +145,29 @@ PHP extension ZipArchive. So the following were removed:
- `PHPExcel_Shared_ZipArchive` - `PHPExcel_Shared_ZipArchive`
- `PHPExcel_Shared_ZipStreamWrapper` - `PHPExcel_Shared_ZipStreamWrapper`
## Cell caching
Cell caching was heavily refactored to leverage
[PSR-16](http://www.php-fig.org/psr/psr-16/). That means most classes
related to that feature were removed:
- `PHPExcel_CachedObjectStorage_APC`
- `PHPExcel_CachedObjectStorage_DiscISAM`
- `PHPExcel_CachedObjectStorage_ICache`
- `PHPExcel_CachedObjectStorage_Igbinary`
- `PHPExcel_CachedObjectStorage_Memcache`
- `PHPExcel_CachedObjectStorage_Memory`
- `PHPExcel_CachedObjectStorage_MemoryGZip`
- `PHPExcel_CachedObjectStorage_MemorySerialized`
- `PHPExcel_CachedObjectStorage_PHPTemp`
- `PHPExcel_CachedObjectStorage_SQLite`
- `PHPExcel_CachedObjectStorage_SQLite3`
- `PHPExcel_CachedObjectStorage_Wincache`
In addition to that, `\PhpOffice\PhpSpreadsheet::getCellCollection()` was renamed
to `\PhpOffice\PhpSpreadsheet::getCoordinates()` and
`\PhpOffice\PhpSpreadsheet::getCellCacheController()` to
`\PhpOffice\PhpSpreadsheet::getCellCollection()` for clarity.
Refer to [the new documentation](./memory_saving.md) to see how to migrate.

View File

@ -5,177 +5,20 @@ before instantiating a `Spreadsheet` object or loading a workbook file,
there are a number of configuration options that can be set which will there are a number of configuration options that can be set which will
affect the subsequent behaviour of the script. affect the subsequent behaviour of the script.
## Cell Caching ## Cell collection caching
PhpSpreadsheet uses an average of about 1k/cell in your worksheets, so By default, PhpSpreadsheet holds all cell objects in memory, but
large workbooks can quickly use up available memory. Cell caching you can specify alternatives to reduce memory consumption at the cost of speed.
provides a mechanism that allows PhpSpreadsheet to maintain the cell Read more about [memory saving](./memory_saving.md).
objects in a smaller size of memory, on disk, or in APC, memcache or
Wincache, rather than in PHP memory. This allows you to reduce the
memory usage for large workbooks, although at a cost of speed to access
cell data.
By default, PhpSpreadsheet still holds all cell objects in memory, but To enable cell caching, you must provide your own implementation of cache like so:
you can specify alternatives. To enable cell caching, you must call the
\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod() method,
passing in the caching method that you wish to use.
``` php ``` php
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_IN_MEMORY; $cache = new MyCustomPsr16Implementation();
\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod); \PhpOffice\PhpSpreadsheet\Settings::setCache($cache);
``` ```
setCacheStorageMethod() will return a boolean true on success, false on
failure (for example if trying to cache to APC when APC is not enabled).
A separate cache is maintained for each individual worksheet, and is
automatically created when the worksheet is instantiated based on the
caching method and settings that you have configured. You cannot change
the configuration settings once you have started to read a workbook, or
have created your first worksheet.
Currently, the following caching methods are available.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IN\_MEMORY
The default. If you don't initialise any caching method, then this is
the method that PhpSpreadsheet will use. Cell objects are maintained in
PHP memory as at present.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IN\_MEMORY\_SERIALIZED
Using this caching method, cells are held in PHP memory as an array of
serialized objects, which reduces the memory footprint with minimal
performance overhead.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IN\_MEMORY\_GZIP
Like cache\_in\_memory\_serialized, this method holds cells in PHP
memory as an array of serialized objects, but gzipped to reduce the
memory usage still further, although access to read or write a cell is
slightly slower.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_IGBINARY
Uses PHPs igbinary extension (if its available) to serialize cell
objects in memory. This is normally faster and uses less memory than
standard PHP serialization, but isnt available in most hosting
environments.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_DISCISAM
When using CACHE\_TO\_DISCISAM all cells are held in a temporary disk
file, with only an index to their location in that file maintained in
PHP memory. This is slower than any of the CACHE\_IN\_MEMORY methods,
but significantly reduces the memory footprint. By default,
PhpSpreadsheet will use PHP's temp directory for the cache file, but you
can specify a different directory when initialising CACHE\_TO\_DISCISAM.
``` php
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_DISCISAM;
$cacheSettings = array(
'dir' => '/usr/local/tmp'
);
\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
```
The temporary disk file is automatically deleted when your script
terminates.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_PHPTEMP
Like CACHE\_TO\_DISCISAM, when using CACHE\_TO\_PHPTEMP all cells are
held in the php://temp I/O stream, with only an index to their location
maintained in PHP memory. In PHP, the php://memory wrapper stores data
in the memory: php://temp behaves similarly, but uses a temporary file
for storing the data when a certain memory limit is reached. The default
is 1 MB, but you can change this when initialising CACHE\_TO\_PHPTEMP.
``` php
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_PHPTEMP;
$cacheSettings = array(
'memoryCacheSize' => '8MB'
);
\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
```
The php://temp file is automatically deleted when your script
terminates.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_APC
When using CACHE\_TO\_APC, cell objects are maintained in APC with only
an index maintained in PHP memory to identify that the cell exists. By
default, an APC cache timeout of 600 seconds is used, which should be
enough for most applications: although it is possible to change this
when initialising CACHE\_TO\_APC.
``` php
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_APC;
$cacheSettings = array(
'cacheTime' => 600
);
\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
```
When your script terminates all entries will be cleared from APC,
regardless of the cacheTime value, so it cannot be used for persistent
storage using this mechanism.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_MEMCACHE
When using CACHE\_TO\_MEMCACHE, cell objects are maintained in memcache
with only an index maintained in PHP memory to identify that the cell
exists.
By default, PhpSpreadsheet looks for a memcache server on localhost at
port 11211. It also sets a memcache timeout limit of 600 seconds. If you
are running memcache on a different server or port, then you can change
these defaults when you initialise CACHE\_TO\_MEMCACHE:
``` php
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_MEMCACHE;
$cacheSettings = array(
'memcacheServer' => 'localhost',
'memcachePort' => 11211,
'cacheTime' => 600
);
\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
```
When your script terminates all entries will be cleared from memcache,
regardless of the cacheTime value, so it cannot be used for persistent
storage using this mechanism.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_WINCACHE
When using CACHE\_TO\_WINCACHE, cell objects are maintained in Wincache
with only an index maintained in PHP memory to identify that the cell
exists. By default, a Wincache cache timeout of 600 seconds is used,
which should be enough for most applications: although it is possible to
change this when initialising CACHE\_TO\_WINCACHE.
``` php
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_WINCACHE;
$cacheSettings = array(
'cacheTime' => 600
);
\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
```
When your script terminates all entries will be cleared from Wincache,
regardless of the cacheTime value, so it cannot be used for persistent
storage using this mechanism.
### \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE\_TO\_SQLITE3;
Uses an SQLite 3 "in-memory" database for caching cell data. Unlike
other caching methods, neither cells nor an index are held in PHP memory
- an indexed database table makes it unnecessary to hold any index in
PHP memory, which makes this the most memory-efficient of the cell
caching methods.
## Language/Locale ## Language/Locale
Some localisation elements have been included in PhpSpreadsheet. You can Some localisation elements have been included in PhpSpreadsheet. You can

View File

@ -1,15 +0,0 @@
<?php
require __DIR__ . '/Header.php';
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_IN_MEMORY_GZIP;
if (\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod)) {
$helper->log('Enable Cell Caching using ' . $cacheMethod . ' method');
} else {
$helper->log('ERROR: Unable to set Cell Caching using ' . $cacheMethod . ' method, reverting to memory');
}
$spreadsheet = require __DIR__ . '/templates/largeSpreadsheet.php';
// Save
$helper->write($spreadsheet, __FILE__);

View File

@ -1,15 +0,0 @@
<?php
require __DIR__ . '/Header.php';
$cacheMethod = \PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory::CACHE_TO_SQLITE3;
if (\PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod($cacheMethod)) {
$helper->log('Enable Cell Caching using ' . $cacheMethod . ' method');
} else {
$helper->log('ERROR: Unable to set Cell Caching using ' . $cacheMethod . ' method, reverting to memory');
}
$spreadsheet = require __DIR__ . '/templates/largeSpreadsheet.php';
// Save
$helper->write($spreadsheet, __FILE__);

View File

@ -1,287 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class APC extends CacheBase implements ICache
{
/**
* Prefix used to uniquely identify cache data for this worksheet.
*
* @var string
*/
private $cachePrefix = null;
/**
* Cache timeout.
*
* @var int
*/
private $cacheTime = 600;
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
if (!apc_store(
$this->cachePrefix . $this->currentObjectID . '.cache',
serialize($this->currentObject),
$this->cacheTime
)) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $this->currentObjectID . ' in APC');
}
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\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 \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell?
*
* @param string $pCoord Coordinate address of the cell to check
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
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 = apc_fetch($this->cachePrefix . $pCoord . '.cache');
if ($success === false) {
// Entry no longer exists in APC, so clear it from the cache array
parent::deleteCacheData($pCoord);
throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in APC cache');
}
return true;
}
return false;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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 = apc_fetch($this->cachePrefix . $pCoord . '.cache');
if ($obj === false) {
// Entry no longer exists in APC, so clear it from the cache array
parent::deleteCacheData($pCoord);
throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in APC 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.
*
* @param string $pCoord Coordinate address of the cell to delete
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function deleteCacheData($pCoord)
{
// Delete the entry from APC
apc_delete($this->cachePrefix . $pCoord . '.cache');
// Delete the entry from our cell address array
parent::deleteCacheData($pCoord);
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\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 = apc_fetch($this->cachePrefix . $cellID . '.cache');
if ($obj === false) {
// Entry no longer exists in APC, so clear it from the cache array
parent::deleteCacheData($cellID);
throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $cellID . ' no longer exists in APC');
}
if (!apc_store($newCachePrefix . $cellID . '.cache', $obj, $this->cacheTime)) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $cellID . ' in APC');
}
}
}
$this->cachePrefix = $newCachePrefix;
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if ($this->currentObject !== null) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
// Flush the APC cache
$this->__destruct();
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
/**
* Initialise this new cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
* @param array $arguments Additional initialisation arguments
*/
public function __construct(\PhpOffice\PhpSpreadsheet\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) {
apc_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 bool
*/
public static function cacheMethodIsAvailable()
{
if (!function_exists('apc_store')) {
return false;
}
if (apc_sma_info() === false) {
return false;
}
return true;
}
}

View File

@ -1,377 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
abstract class CacheBase
{
/**
* Parent worksheet.
*
* @var \PhpOffice\PhpSpreadsheet\Worksheet
*/
protected $parent;
/**
* The currently active Cell.
*
* @var \PhpOffice\PhpSpreadsheet\Cell
*/
protected $currentObject = null;
/**
* Coordinate address of the currently active Cell.
*
* @var string
*/
protected $currentObjectID = null;
/**
* Flag indicating whether the currently active Cell requires saving.
*
* @var bool
*/
protected $currentCellIsDirty = true;
/**
* An array of cells or cell pointers for the worksheet cells held in this cache,
* and indexed by their coordinate address within the worksheet.
*
* @var array of mixed
*/
protected $cellCache = [];
/**
* Initialise this new cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
*/
public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
{
// Set our parent worksheet.
// This is maintained within the cache controller to facilitate re-attaching it to \PhpOffice\PhpSpreadsheet\Cell objects when
// they are woken from a serialized state
$this->parent = $parent;
}
/**
* Return the parent worksheet for this cell collection.
*
* @return \PhpOffice\PhpSpreadsheet\Worksheet
*/
public function getParent()
{
return $this->parent;
}
/**
* Is a value set in the current \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell?
*
* @param string $pCoord Coordinate address of the cell to check
*
* @return bool
*/
public function isDataSet($pCoord)
{
if ($pCoord === $this->currentObjectID) {
return true;
}
// Check if the requested entry exists in the cache
return isset($this->cellCache[$pCoord]);
}
/**
* Move a cell object from one address to another.
*
* @param string $fromAddress Current address of the cell to move
* @param string $toAddress Destination address of the cell to move
*
* @return bool
*/
public function moveCell($fromAddress, $toAddress)
{
if ($fromAddress === $this->currentObjectID) {
$this->currentObjectID = $toAddress;
}
$this->currentCellIsDirty = true;
if (isset($this->cellCache[$fromAddress])) {
$this->cellCache[$toAddress] = &$this->cellCache[$fromAddress];
unset($this->cellCache[$fromAddress]);
}
return true;
}
/**
* Add or Update a cell in cache.
*
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function updateCacheData(\PhpOffice\PhpSpreadsheet\Cell $cell)
{
return $this->addCacheData($cell->getCoordinate(), $cell);
}
/**
* Delete a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to delete
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function deleteCacheData($pCoord)
{
if ($pCoord === $this->currentObjectID && !is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObjectID = $this->currentObject = null;
}
if (is_object($this->cellCache[$pCoord])) {
$this->cellCache[$pCoord]->detach();
}
unset($this->cellCache[$pCoord]);
$this->currentCellIsDirty = false;
}
/**
* Get a list of all cell addresses currently held in cache.
*
* @return string[]
*/
public function getCellList()
{
return array_keys($this->cellCache);
}
/**
* Sort the list of all cell addresses currently held in cache by row and column.
*
* @return string[]
*/
public function getSortedCellList()
{
$sortKeys = [];
foreach ($this->getCellList() as $coord) {
sscanf($coord, '%[A-Z]%d', $column, $row);
$sortKeys[sprintf('%09d%3s', $row, $column)] = $coord;
}
ksort($sortKeys);
return array_values($sortKeys);
}
/**
* Get highest worksheet column and highest row that have cell records.
*
* @return array Highest column name and highest row number
*/
public function getHighestRowAndColumn()
{
// Lookup highest column and highest row
$col = ['A' => '1A'];
$row = [1];
foreach ($this->getCellList() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
$row[$r] = $r;
$col[$c] = strlen($c) . $c;
}
if (!empty($row)) {
// Determine highest column and row
$highestRow = max($row);
$highestColumn = substr(max($col), 1);
}
return [
'row' => $highestRow,
'column' => $highestColumn,
];
}
/**
* Return the cell address of the currently active cell object.
*
* @return string
*/
public function getCurrentAddress()
{
return $this->currentObjectID;
}
/**
* Return the column address of the currently active cell object.
*
* @return string
*/
public function getCurrentColumn()
{
sscanf($this->currentObjectID, '%[A-Z]%d', $column, $row);
return $column;
}
/**
* Return the row address of the currently active cell object.
*
* @return int
*/
public function getCurrentRow()
{
sscanf($this->currentObjectID, '%[A-Z]%d', $column, $row);
return (int) $row;
}
/**
* Get highest worksheet column.
*
* @param 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)
{
if ($row == null) {
$colRow = $this->getHighestRowAndColumn();
return $colRow['column'];
}
$columnList = [1];
foreach ($this->getCellList() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($r != $row) {
continue;
}
$columnList[] = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($c);
}
return \PhpOffice\PhpSpreadsheet\Cell::stringFromColumnIndex(max($columnList) - 1);
}
/**
* Get highest worksheet row.
*
* @param 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($column = null)
{
if ($column == null) {
$colRow = $this->getHighestRowAndColumn();
return $colRow['row'];
}
$rowList = [0];
foreach ($this->getCellList() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($c != $column) {
continue;
}
$rowList[] = $r;
}
return max($rowList);
}
/**
* Generate a unique ID for cache referencing.
*
* @return string Unique Reference
*/
protected function getUniqueID()
{
if (function_exists('posix_getpid')) {
$baseUnique = posix_getpid();
} else {
$baseUnique = mt_rand();
}
return uniqid($baseUnique, true);
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
{
$this->currentCellIsDirty;
$this->storeData();
$this->parent = $parent;
if (($this->currentObject !== null) && (is_object($this->currentObject))) {
$this->currentObject->attach($this);
}
}
/**
* Remove a row, deleting all cells in that row.
*
* @param string $row Row number to remove
*/
public function removeRow($row)
{
foreach ($this->getCellList() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($r == $row) {
$this->deleteCacheData($coord);
}
}
}
/**
* Remove a column, deleting all cells in that column.
*
* @param string $column Column ID to remove
*/
public function removeColumn($column)
{
foreach ($this->getCellList() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($c == $column) {
$this->deleteCacheData($coord);
}
}
}
/**
* 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 bool
*/
public static function cacheMethodIsAvailable()
{
return true;
}
}

View File

@ -1,209 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class DiscISAM extends CacheBase implements ICache
{
/**
* Name of the file for this cache.
*
* @var string
*/
private $fileName = null;
/**
* File handle for this cache file.
*
* @var resource
*/
private $fileHandle = null;
/**
* Directory/Folder where the cache file is located.
*
* @var string
*/
private $cacheDirectory = null;
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
fseek($this->fileHandle, 0, SEEK_END);
$this->cellCache[$this->currentObjectID] = [
'ptr' => ftell($this->fileHandle),
'sz' => fwrite($this->fileHandle, serialize($this->currentObject)),
];
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell)
{
if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) {
$this->storeData();
}
$this->currentObjectID = $pCoord;
$this->currentObject = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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 (!isset($this->cellCache[$pCoord])) {
// Return null if requested entry doesn't exist in cache
return null;
}
// Set current entry to the requested entry
$this->currentObjectID = $pCoord;
fseek($this->fileHandle, $this->cellCache[$pCoord]['ptr']);
$this->currentObject = unserialize(fread($this->fileHandle, $this->cellCache[$pCoord]['sz']));
// 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();
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
{
parent::copyCellCollection($parent);
// Get a new id for the new file name
$baseUnique = $this->getUniqueID();
$newFileName = $this->cacheDirectory . '/PhpSpreadsheet.' . $baseUnique . '.cache';
// Copy the existing cell cache file
copy($this->fileName, $newFileName);
$this->fileName = $newFileName;
// Open the copied cell cache file
$this->fileHandle = fopen($this->fileName, 'a+');
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if (!is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
$this->cellCache = [];
// 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();
}
/**
* Initialise this new cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
* @param array of mixed $arguments Additional initialisation arguments
*/
public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments)
{
$this->cacheDirectory = ((isset($arguments['dir'])) && ($arguments['dir'] !== null))
? $arguments['dir']
: \PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir();
parent::__construct($parent);
if (is_null($this->fileHandle)) {
$baseUnique = $this->getUniqueID();
$this->fileName = $this->cacheDirectory . '/PhpSpreadsheet.' . $baseUnique . '.cache';
$this->fileHandle = fopen($this->fileName, 'a+');
}
}
/**
* Destroy this cell collection.
*/
public function __destruct()
{
if (!is_null($this->fileHandle)) {
fclose($this->fileHandle);
unlink($this->fileName);
}
$this->fileHandle = null;
}
}

View File

@ -1,109 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
interface ICache
{
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell);
/**
* Add or Update a cell in cache.
*
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function updateCacheData(\PhpOffice\PhpSpreadsheet\Cell $cell);
/**
* Fetch a cell from cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to retrieve
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found
*/
public function getCacheData($pCoord);
/**
* Delete a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to delete
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function deleteCacheData($pCoord);
/**
* Is a value set in the current \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell?
*
* @param string $pCoord Coordinate address of the cell to check
*
* @return bool
*/
public function isDataSet($pCoord);
/**
* Get a list of all cell addresses currently held in cache.
*
* @return string[]
*/
public function getCellList();
/**
* Get the list of all cell addresses currently held in cache sorted by column and row.
*
* @return string[]
*/
public function getSortedCellList();
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent);
/**
* 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 bool
*/
public static function cacheMethodIsAvailable();
}

View File

@ -1,150 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class Igbinary extends CacheBase implements ICache
{
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
$this->cellCache[$this->currentObjectID] = igbinary_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 \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell)
{
if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) {
$this->storeData();
}
$this->currentObjectID = $pCoord;
$this->currentObject = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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 (!isset($this->cellCache[$pCoord])) {
// Return null if requested entry doesn't exist in cache
return null;
}
// Set current entry to the requested entry
$this->currentObjectID = $pCoord;
$this->currentObject = igbinary_unserialize($this->cellCache[$pCoord]);
// Re-attach this as the cell's parent
$this->currentObject->attach($this);
// Return requested entry
return $this->currentObject;
}
// function getCacheData()
/**
* 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();
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if (!is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
// function unsetWorksheetCells()
/**
* 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 bool
*/
public static function cacheMethodIsAvailable()
{
if (!function_exists('igbinary_serialize')) {
return false;
}
return true;
}
}

View File

@ -1,313 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class Memcache extends CacheBase implements ICache
{
/**
* Prefix used to uniquely identify cache data for this worksheet.
*
* @var string
*/
private $cachePrefix = null;
/**
* Cache timeout.
*
* @var int
*/
private $cacheTime = 600;
/**
* Memcache interface.
*
* @var resource
*/
private $memcache = null;
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
$obj = serialize($this->currentObject);
if (!$this->memcache->replace($this->cachePrefix . $this->currentObjectID . '.cache', $obj, null, $this->cacheTime)) {
if (!$this->memcache->add($this->cachePrefix . $this->currentObjectID . '.cache', $obj, null, $this->cacheTime)) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception("Failed to store cell {$this->currentObjectID} in MemCache");
}
}
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\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 \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell?
*
* @param string $pCoord Coordinate address of the cell to check
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
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 Memcache
$success = $this->memcache->get($this->cachePrefix . $pCoord . '.cache');
if ($success === false) {
// Entry no longer exists in Memcache, so clear it from the cache array
parent::deleteCacheData($pCoord);
throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in MemCache');
}
return true;
}
return false;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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 = $this->memcache->get($this->cachePrefix . $pCoord . '.cache');
if ($obj === false) {
// Entry no longer exists in Memcache, so clear it from the cache array
parent::deleteCacheData($pCoord);
throw new \PhpOffice\PhpSpreadsheet\Exception("Cell entry {$pCoord} no longer exists in MemCache");
}
} 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.
*
* @param string $pCoord Coordinate address of the cell to delete
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function deleteCacheData($pCoord)
{
// Delete the entry from Memcache
$this->memcache->delete($this->cachePrefix . $pCoord . '.cache');
// Delete the entry from our cell address array
parent::deleteCacheData($pCoord);
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\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 = $this->memcache->get($this->cachePrefix . $cellID . '.cache');
if ($obj === false) {
// Entry no longer exists in Memcache, so clear it from the cache array
parent::deleteCacheData($cellID);
throw new \PhpOffice\PhpSpreadsheet\Exception("Cell entry {$cellID} no longer exists in MemCache");
}
if (!$this->memcache->add($newCachePrefix . $cellID . '.cache', $obj, null, $this->cacheTime)) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception("Failed to store cell {$cellID} in MemCache");
}
}
}
$this->cachePrefix = $newCachePrefix;
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if (!is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
// Flush the Memcache cache
$this->__destruct();
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
/**
* Initialise this new cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
* @param mixed[] $arguments Additional initialisation arguments
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments)
{
$memcacheServer = (isset($arguments['memcacheServer'])) ? $arguments['memcacheServer'] : 'localhost';
$memcachePort = (isset($arguments['memcachePort'])) ? $arguments['memcachePort'] : 11211;
$cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600;
if (is_null($this->cachePrefix)) {
$baseUnique = $this->getUniqueID();
$this->cachePrefix = substr(md5($baseUnique), 0, 8) . '.';
// Set a new Memcache object and connect to the Memcache server
$this->memcache = new \Memcache();
if (!$this->memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, [$this, 'failureCallback'])) {
throw new \PhpOffice\PhpSpreadsheet\Exception("Could not connect to MemCache server at {$memcacheServer}:{$memcachePort}");
}
$this->cacheTime = $cacheTime;
parent::__construct($parent);
}
}
/**
* Memcache error handler.
*
* @param string $host Memcache server
* @param int $port Memcache port
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function failureCallback($host, $port)
{
throw new \PhpOffice\PhpSpreadsheet\Exception("memcache {$host}:{$port} failed");
}
/**
* Destroy this cell collection.
*/
public function __destruct()
{
$cacheList = $this->getCellList();
foreach ($cacheList as $cellID) {
$this->memcache->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 bool
*/
public static function cacheMethodIsAvailable()
{
if (!function_exists('memcache_add')) {
return false;
}
return true;
}
}

View File

@ -1,116 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class Memory extends CacheBase implements ICache
{
/**
* Dummy method callable from CacheBase, but unused by Memory cache.
*/
protected function storeData()
{
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell)
{
$this->cellCache[$pCoord] = $cell;
// Set current entry to the new/updated entry
$this->currentObjectID = $pCoord;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found
*/
public function getCacheData($pCoord)
{
// Check if the entry that has been requested actually exists
if (!isset($this->cellCache[$pCoord])) {
$this->currentObjectID = null;
// Return null if requested entry doesn't exist in cache
return null;
}
// Set current entry to the requested entry
$this->currentObjectID = $pCoord;
// Return requested entry
return $this->cellCache[$pCoord];
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
{
parent::copyCellCollection($parent);
$newCollection = [];
foreach ($this->cellCache as $k => &$cell) {
$newCollection[$k] = clone $cell;
$newCollection[$k]->attach($this);
}
$this->cellCache = $newCollection;
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
// Because cells are all stored as intact objects in memory, we need to detach each one from the parent
foreach ($this->cellCache as $k => &$cell) {
$cell->detach();
$this->cellCache[$k] = null;
}
unset($cell);
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
}

View File

@ -1,129 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class MemoryGZip extends CacheBase implements ICache
{
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
$this->cellCache[$this->currentObjectID] = gzdeflate(serialize($this->currentObject), 9);
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell)
{
if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) {
$this->storeData();
}
$this->currentObjectID = $pCoord;
$this->currentObject = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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 (!isset($this->cellCache[$pCoord])) {
// 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(gzinflate($this->cellCache[$pCoord]));
// 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();
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if (!is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
}

View File

@ -1,129 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class MemorySerialized extends CacheBase implements ICache
{
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
$this->cellCache[$this->currentObjectID] = serialize($this->currentObject);
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell)
{
if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) {
$this->storeData();
}
$this->currentObjectID = $pCoord;
$this->currentObject = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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 (!isset($this->cellCache[$pCoord])) {
// 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($this->cellCache[$pCoord]);
// 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();
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if (!is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
}

View File

@ -1,197 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class PHPTemp extends CacheBase implements ICache
{
/**
* Name of the file for this cache.
*
* @var string
*/
private $fileHandle = null;
/**
* Memory limit to use before reverting to file cache.
*
* @var int
*/
private $memoryCacheSize = null;
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
fseek($this->fileHandle, 0, SEEK_END);
$this->cellCache[$this->currentObjectID] = [
'ptr' => ftell($this->fileHandle),
'sz' => fwrite($this->fileHandle, serialize($this->currentObject)),
];
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell)
{
if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) {
$this->storeData();
}
$this->currentObjectID = $pCoord;
$this->currentObject = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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 (!isset($this->cellCache[$pCoord])) {
// Return null if requested entry doesn't exist in cache
return null;
}
// Set current entry to the requested entry
$this->currentObjectID = $pCoord;
fseek($this->fileHandle, $this->cellCache[$pCoord]['ptr']);
$this->currentObject = unserialize(fread($this->fileHandle, $this->cellCache[$pCoord]['sz']));
// 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();
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
{
parent::copyCellCollection($parent);
// Open a new stream for the cell cache data
$newFileHandle = fopen('php://temp/maxmemory:' . $this->memoryCacheSize, 'a+');
// Copy the existing cell cache data to the new stream
fseek($this->fileHandle, 0);
while (!feof($this->fileHandle)) {
fwrite($newFileHandle, fread($this->fileHandle, 1024));
}
$this->fileHandle = $newFileHandle;
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if (!is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
// Close down the php://temp file
$this->__destruct();
}
/**
* Initialise this new cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
* @param mixed[] $arguments Additional initialisation arguments
*/
public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments)
{
$this->memoryCacheSize = (isset($arguments['memoryCacheSize'])) ? $arguments['memoryCacheSize'] : 1 * 1024 * 1024;
parent::__construct($parent);
if (is_null($this->fileHandle)) {
$this->fileHandle = fopen('php://temp/maxmemory:' . $this->memoryCacheSize, 'a+');
}
}
/**
* Destroy this cell collection.
*/
public function __destruct()
{
if (!is_null($this->fileHandle)) {
fclose($this->fileHandle);
}
$this->fileHandle = null;
}
}

View File

@ -1,359 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class SQLite3 extends CacheBase implements ICache
{
/**
* Database table name.
*
* @var string
*/
private $TableName = null;
/**
* Database handle.
*
* @var resource
*/
private $DBHandle = null;
/**
* Prepared statement for a SQLite3 select query.
*
* @var SQLite3Stmt
*/
private $selectQuery;
/**
* Prepared statement for a SQLite3 insert query.
*
* @var SQLite3Stmt
*/
private $insertQuery;
/**
* Prepared statement for a SQLite3 update query.
*
* @var SQLite3Stmt
*/
private $updateQuery;
/**
* Prepared statement for a SQLite3 delete query.
*
* @var SQLite3Stmt
*/
private $deleteQuery;
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
$this->insertQuery->bindValue('id', $this->currentObjectID, SQLITE3_TEXT);
$this->insertQuery->bindValue('data', serialize($this->currentObject), SQLITE3_BLOB);
$result = $this->insertQuery->execute();
if ($result === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\Cell $cell)
{
if (($pCoord !== $this->currentObjectID) && ($this->currentObjectID !== null)) {
$this->storeData();
}
$this->currentObjectID = $pCoord;
$this->currentObject = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell Cell that was found, or null if not found
*/
public function getCacheData($pCoord)
{
if ($pCoord === $this->currentObjectID) {
return $this->currentObject;
}
$this->storeData();
$this->selectQuery->bindValue('id', $pCoord, SQLITE3_TEXT);
$cellResult = $this->selectQuery->execute();
if ($cellResult === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
$cellData = $cellResult->fetchArray(SQLITE3_ASSOC);
if ($cellData === false) {
// 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($cellData['value']);
// Re-attach this as the cell's parent
$this->currentObject->attach($this);
// Return requested entry
return $this->currentObject;
}
/**
* Is a value set for an indexed cell?
*
* @param string $pCoord Coordinate address of the cell to check
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
public function isDataSet($pCoord)
{
if ($pCoord === $this->currentObjectID) {
return true;
}
// Check if the requested entry exists in the cache
$this->selectQuery->bindValue('id', $pCoord, SQLITE3_TEXT);
$cellResult = $this->selectQuery->execute();
if ($cellResult === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
$cellData = $cellResult->fetchArray(SQLITE3_ASSOC);
return ($cellData === false) ? false : true;
}
/**
* Delete a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to delete
*
* @throws \PhpOffice\PhpSpreadsheet\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
$this->deleteQuery->bindValue('id', $pCoord, SQLITE3_TEXT);
$result = $this->deleteQuery->execute();
if ($result === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
$this->currentCellIsDirty = false;
}
/**
* Move a cell object from one address to another.
*
* @param string $fromAddress Current address of the cell to move
* @param string $toAddress Destination address of the cell to move
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
public function moveCell($fromAddress, $toAddress)
{
if ($fromAddress === $this->currentObjectID) {
$this->currentObjectID = $toAddress;
}
$this->deleteQuery->bindValue('id', $toAddress, SQLITE3_TEXT);
$result = $this->deleteQuery->execute();
if ($result === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
$this->updateQuery->bindValue('toid', $toAddress, SQLITE3_TEXT);
$this->updateQuery->bindValue('fromid', $fromAddress, SQLITE3_TEXT);
$result = $this->updateQuery->execute();
if ($result === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
return true;
}
/**
* Get a list of all cell addresses currently held in cache.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return string[]
*/
public function getCellList()
{
if ($this->currentObjectID !== null) {
$this->storeData();
}
$query = 'SELECT id FROM kvp_' . $this->TableName;
$cellIdsResult = $this->DBHandle->query($query);
if ($cellIdsResult === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
$cellKeys = [];
while ($row = $cellIdsResult->fetchArray(SQLITE3_ASSOC)) {
$cellKeys[] = $row['id'];
}
return $cellKeys;
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
{
$this->currentCellIsDirty;
$this->storeData();
// Get a new id for the new table name
$tableName = str_replace('.', '_', $this->getUniqueID());
if (!$this->DBHandle->exec('CREATE TABLE kvp_' . $tableName . ' (id VARCHAR(12) PRIMARY KEY, value BLOB)
AS SELECT * FROM kvp_' . $this->TableName)
) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
// Copy the existing cell cache file
$this->TableName = $tableName;
}
/**
* Clear the cell collection and disconnect from our parent.
*/
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();
}
/**
* Initialise this new cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent)
{
parent::__construct($parent);
if (is_null($this->DBHandle)) {
$this->TableName = str_replace('.', '_', $this->getUniqueID());
$_DBName = ':memory:';
$this->DBHandle = new \SQLite3($_DBName);
if ($this->DBHandle === false) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
if (!$this->DBHandle->exec('CREATE TABLE kvp_' . $this->TableName . ' (id VARCHAR(12) PRIMARY KEY, value BLOB)')) {
throw new \PhpOffice\PhpSpreadsheet\Exception($this->DBHandle->lastErrorMsg());
}
}
$this->selectQuery = $this->DBHandle->prepare('SELECT value FROM kvp_' . $this->TableName . ' WHERE id = :id');
$this->insertQuery = $this->DBHandle->prepare('INSERT OR REPLACE INTO kvp_' . $this->TableName . ' VALUES(:id,:data)');
$this->updateQuery = $this->DBHandle->prepare('UPDATE kvp_' . $this->TableName . ' SET id=:toId WHERE id=:fromId');
$this->deleteQuery = $this->DBHandle->prepare('DELETE FROM kvp_' . $this->TableName . ' WHERE id = :id');
}
/**
* Destroy this cell collection.
*/
public function __destruct()
{
if (!is_null($this->DBHandle)) {
$this->DBHandle->exec('DROP TABLE kvp_' . $this->TableName);
$this->DBHandle->close();
}
$this->DBHandle = null;
}
/**
* 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 bool
*/
public static function cacheMethodIsAvailable()
{
if (!class_exists('SQLite3', false)) {
return false;
}
return true;
}
}

View File

@ -1,292 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet\CachedObjectStorage;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class Wincache extends CacheBase implements ICache
{
/**
* Prefix used to uniquely identify cache data for this worksheet.
*
* @var string
*/
private $cachePrefix = null;
/**
* Cache timeout.
*
* @var int
*/
private $cacheTime = 600;
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
protected function storeData()
{
if ($this->currentCellIsDirty && !empty($this->currentObjectID)) {
$this->currentObject->detach();
$obj = serialize($this->currentObject);
if (wincache_ucache_exists($this->cachePrefix . $this->currentObjectID . '.cache')) {
if (!wincache_ucache_set($this->cachePrefix . $this->currentObjectID . '.cache', $obj, $this->cacheTime)) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $this->currentObjectID . ' in WinCache');
}
} else {
if (!wincache_ucache_add($this->cachePrefix . $this->currentObjectID . '.cache', $obj, $this->cacheTime)) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $this->currentObjectID . ' in WinCache');
}
}
$this->currentCellIsDirty = false;
}
$this->currentObjectID = $this->currentObject = null;
}
/**
* Add or Update a cell in cache identified by coordinate address.
*
* @param string $pCoord Coordinate address of the cell to update
* @param \PhpOffice\PhpSpreadsheet\Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\Cell
*/
public function addCacheData($pCoord, \PhpOffice\PhpSpreadsheet\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 \PhpOffice\PhpSpreadsheet\CachedObjectStorage\ICache for an indexed cell?
*
* @param string $pCoord Coordinate address of the cell to check
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return bool
*/
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 cache
$success = wincache_ucache_exists($this->cachePrefix . $pCoord . '.cache');
if ($success === false) {
// Entry no longer exists in Wincache, so clear it from the cache array
parent::deleteCacheData($pCoord);
throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in WinCache');
}
return true;
}
return false;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return \PhpOffice\PhpSpreadsheet\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
$obj = null;
if (parent::isDataSet($pCoord)) {
$success = false;
$obj = wincache_ucache_get($this->cachePrefix . $pCoord . '.cache', $success);
if ($success === false) {
// Entry no longer exists in WinCache, so clear it from the cache array
parent::deleteCacheData($pCoord);
throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $pCoord . ' no longer exists in WinCache');
}
} 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.
*
* @param string $pCoord Coordinate address of the cell to delete
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function deleteCacheData($pCoord)
{
// Delete the entry from Wincache
wincache_ucache_delete($this->cachePrefix . $pCoord . '.cache');
// Delete the entry from our cell address array
parent::deleteCacheData($pCoord);
}
/**
* Clone the cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The new worksheet that we're copying to
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function copyCellCollection(\PhpOffice\PhpSpreadsheet\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) {
$success = false;
$obj = wincache_ucache_get($this->cachePrefix . $cellID . '.cache', $success);
if ($success === false) {
// Entry no longer exists in WinCache, so clear it from the cache array
parent::deleteCacheData($cellID);
throw new \PhpOffice\PhpSpreadsheet\Exception('Cell entry ' . $cellID . ' no longer exists in Wincache');
}
if (!wincache_ucache_add($newCachePrefix . $cellID . '.cache', $obj, $this->cacheTime)) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to store cell ' . $cellID . ' in Wincache');
}
}
}
$this->cachePrefix = $newCachePrefix;
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells()
{
if (!is_null($this->currentObject)) {
$this->currentObject->detach();
$this->currentObject = $this->currentObjectID = null;
}
// Flush the WinCache cache
$this->__destruct();
$this->cellCache = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
/**
* Initialise this new cell collection.
*
* @param \PhpOffice\PhpSpreadsheet\Worksheet $parent The worksheet for this cell collection
* @param mixed[] $arguments Additional initialisation arguments
*/
public function __construct(\PhpOffice\PhpSpreadsheet\Worksheet $parent, $arguments)
{
$cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600;
if (is_null($this->cachePrefix)) {
$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) {
wincache_ucache_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 bool
*/
public static function cacheMethodIsAvailable()
{
if (!function_exists('wincache_ucache_add')) {
return false;
}
return true;
}
}

View File

@ -1,228 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheet;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class CachedObjectStorageFactory
{
const CACHE_IN_MEMORY = 'Memory';
const CACHE_IN_MEMORY_GZIP = 'MemoryGZip';
const CACHE_IN_MEMORY_SERIALIZED = 'MemorySerialized';
const CACHE_IGBINARY = 'Igbinary';
const CACHE_TO_DISCISAM = 'DiscISAM';
const CACHE_TO_APC = 'APC';
const CACHE_TO_MEMCACHE = 'Memcache';
const CACHE_TO_PHPTEMP = 'PHPTemp';
const CACHE_TO_WINCACHE = 'Wincache';
const CACHE_TO_SQLITE3 = 'SQLite3';
/**
* Name of the method used for cell cacheing.
*
* @var string
*/
private static $cacheStorageMethod;
/**
* Name of the class used for cell cacheing.
*
* @var string
*/
private static $cacheStorageClass;
/**
* List of all possible cache storage methods.
*
* @var string[]
*/
private static $storageMethods = [
self::CACHE_IN_MEMORY,
self::CACHE_IN_MEMORY_GZIP,
self::CACHE_IN_MEMORY_SERIALIZED,
self::CACHE_IGBINARY,
self::CACHE_TO_PHPTEMP,
self::CACHE_TO_DISCISAM,
self::CACHE_TO_APC,
self::CACHE_TO_MEMCACHE,
self::CACHE_TO_WINCACHE,
self::CACHE_TO_SQLITE3,
];
/**
* Default arguments for each cache storage method.
*
* @var array of mixed array
*/
private static $storageMethodDefaultParameters = [
self::CACHE_IN_MEMORY => [],
self::CACHE_IN_MEMORY_GZIP => [],
self::CACHE_IN_MEMORY_SERIALIZED => [],
self::CACHE_IGBINARY => [],
self::CACHE_TO_PHPTEMP => [
'memoryCacheSize' => '1MB',
],
self::CACHE_TO_DISCISAM => [
'dir' => null,
],
self::CACHE_TO_APC => [
'cacheTime' => 600,
],
self::CACHE_TO_MEMCACHE => [
'memcacheServer' => 'localhost',
'memcachePort' => 11211,
'cacheTime' => 600,
],
self::CACHE_TO_WINCACHE => [
'cacheTime' => 600,
],
self::CACHE_TO_SQLITE3 => [],
];
/**
* Arguments for the active cache storage method.
*
* @var mixed[]
*/
private static $storageMethodParameters = [];
/**
* Return the current cache storage method.
*
* @return string|null
**/
public static function getCacheStorageMethod()
{
return self::$cacheStorageMethod;
}
/**
* Return the current cache storage class.
*
* @return string
**/
public static function getCacheStorageClass()
{
return self::$cacheStorageClass;
}
/**
* Return the list of all possible cache storage methods.
*
* @return string[]
**/
public static function getAllCacheStorageMethods()
{
return self::$storageMethods;
}
/**
* Return the list of all available cache storage methods.
*
* @return string[]
**/
public static function getCacheStorageMethods()
{
$activeMethods = [];
foreach (self::$storageMethods as $storageMethod) {
$cacheStorageClass = '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\' . $storageMethod;
if (call_user_func([$cacheStorageClass, 'cacheMethodIsAvailable'])) {
$activeMethods[] = $storageMethod;
}
}
return $activeMethods;
}
/**
* Identify the cache storage method to use.
*
* @param string $method Name of the method to use for cell cacheing
* @param mixed[] $arguments Additional arguments to pass to the cell caching class
* when instantiating
*
* @return bool
**/
public static function initialize($method = self::CACHE_IN_MEMORY, $arguments = [])
{
if (!in_array($method, self::$storageMethods)) {
return false;
}
$cacheStorageClass = '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\' . $method;
if (!call_user_func([$cacheStorageClass, 'cacheMethodIsAvailable'])) {
return false;
}
self::$storageMethodParameters[$method] = self::$storageMethodDefaultParameters[$method];
foreach ($arguments as $argument => $value) {
if (isset(self::$storageMethodParameters[$method][$argument])) {
self::$storageMethodParameters[$method][$argument] = $value;
}
}
if (self::$cacheStorageMethod === null) {
self::$cacheStorageClass = '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\' . $method;
self::$cacheStorageMethod = $method;
}
return true;
}
/**
* Initialise the cache storage.
*
* @param Worksheet $parent Enable cell caching for this worksheet
*
* @return CachedObjectStorage\ICache
**/
public static function getInstance(Worksheet $parent)
{
$cacheMethodIsAvailable = true;
if (self::$cacheStorageMethod === null) {
$cacheMethodIsAvailable = self::initialize();
}
if ($cacheMethodIsAvailable) {
$instance = new self::$cacheStorageClass(
$parent,
self::$storageMethodParameters[self::$cacheStorageMethod]
);
if ($instance !== null) {
return $instance;
}
}
return false;
}
/**
* Clear the cache storage.
**/
public static function finalize()
{
self::$cacheStorageMethod = null;
self::$cacheStorageClass = null;
self::$storageMethodParameters = [];
}
}

View File

@ -3663,7 +3663,7 @@ class Calculation
$this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue)); $this->debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
} else { } else {
$this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet'); $this->debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
if ($pCellParent->isDataSet($cellRef)) { if ($pCellParent->has($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false); $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
$pCell->attach($pCellParent); $pCell->attach($pCellParent);
} else { } else {

View File

@ -2,6 +2,8 @@
namespace PhpOffice\PhpSpreadsheet; namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Collection\Cells;
/** /**
* Copyright (c) 2006 - 2016 PhpSpreadsheet. * Copyright (c) 2006 - 2016 PhpSpreadsheet.
* *
@ -67,9 +69,9 @@ class Cell
private $dataType; private $dataType;
/** /**
* Parent worksheet. * Collection of cells.
* *
* @var CachedObjectStorage\CacheBase * @var Cells
*/ */
private $parent; private $parent;
@ -86,11 +88,13 @@ class Cell
private $formulaAttributes; private $formulaAttributes;
/** /**
* Send notification to the cache controller. * Update the cell into the cell collection.
**/ *
public function notifyCacheController() * @return self
*/
public function updateInCollection()
{ {
$this->parent->updateCacheData($this); $this->parent->update($this);
return $this; return $this;
} }
@ -100,7 +104,7 @@ class Cell
$this->parent = null; $this->parent = null;
} }
public function attach(CachedObjectStorage\CacheBase $parent) public function attach(Cells $parent)
{ {
$this->parent = $parent; $this->parent = $parent;
} }
@ -120,7 +124,7 @@ class Cell
$this->value = $pValue; $this->value = $pValue;
// Set worksheet cache // Set worksheet cache
$this->parent = $pSheet->getCellCacheController(); $this->parent = $pSheet->getCellCollection();
// Set datatype? // Set datatype?
if ($pDataType !== null) { if ($pDataType !== null) {
@ -160,7 +164,7 @@ class Cell
*/ */
public function getCoordinate() public function getCoordinate()
{ {
return $this->parent->getCurrentAddress(); return $this->parent->getCurrentCoordinate();
} }
/** /**
@ -253,7 +257,7 @@ class Cell
// set the datatype // set the datatype
$this->dataType = $pDataType; $this->dataType = $pDataType;
return $this->notifyCacheController(); return $this->updateInCollection();
} }
/** /**
@ -313,7 +317,7 @@ class Cell
$this->calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue; $this->calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue;
} }
return $this->notifyCacheController(); return $this->updateInCollection();
} }
/** /**
@ -355,7 +359,7 @@ class Cell
} }
$this->dataType = $pDataType; $this->dataType = $pDataType;
return $this->notifyCacheController(); return $this->updateInCollection();
} }
/** /**
@ -417,7 +421,7 @@ class Cell
$this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation); $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation);
return $this->notifyCacheController(); return $this->updateInCollection();
} }
/** /**
@ -469,13 +473,13 @@ class Cell
$this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink); $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink);
return $this->notifyCacheController(); return $this->updateInCollection();
} }
/** /**
* Get parent worksheet. * Get cell collection.
* *
* @return CachedObjectStorage\CacheBase * @return Cells
*/ */
public function getParent() public function getParent()
{ {
@ -555,9 +559,9 @@ class Cell
*/ */
public function rebindParent(Worksheet $parent) public function rebindParent(Worksheet $parent)
{ {
$this->parent = $parent->getCellCacheController(); $this->parent = $parent->getCellCollection();
return $this->notifyCacheController(); return $this->updateInCollection();
} }
/** /**
@ -1031,7 +1035,7 @@ class Cell
{ {
$this->xfIndex = $pValue; $this->xfIndex = $pValue;
return $this->notifyCacheController(); return $this->updateInCollection();
} }
/** /**

View File

@ -0,0 +1,507 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Collection;
use PhpOffice\PhpSpreadsheet\Cell;
use PhpOffice\PhpSpreadsheet\Worksheet;
use Psr\SimpleCache\CacheInterface;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
class Cells
{
/**
* @var \Psr\SimpleCache\CacheInterface
*/
private $cache;
/**
* Parent worksheet.
*
* @var Worksheet
*/
private $parent;
/**
* The currently active Cell.
*
* @var Cell
*/
private $currentCell = null;
/**
* Coordinate of the currently active Cell.
*
* @var string
*/
private $currentCoordinate = null;
/**
* Flag indicating whether the currently active Cell requires saving.
*
* @var bool
*/
private $currentCellIsDirty = false;
/**
* An index of existing cells. Booleans indexed by their coordinate.
*
* @var bool[]
*/
private $index = [];
/**
* Prefix used to uniquely identify cache data for this worksheet.
*
* @var string
*/
private $cachePrefix = null;
/**
* 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.
*
* @return Worksheet
*/
public function getParent()
{
return $this->parent;
}
/**
* Whether the collection holds a cell for the given coordinate.
*
* @param string $pCoord Coordinate of the cell to check
*
* @return bool
*/
public function has($pCoord)
{
if ($pCoord === $this->currentCoordinate) {
return true;
}
// Check if the requested entry exists in the index
return isset($this->index[$pCoord]);
}
/**
* Add or update a cell in the collection.
*
* @param Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return Cell
*/
public function update(Cell $cell)
{
return $this->add($cell->getCoordinate(), $cell);
}
/**
* Delete a cell in cache identified by coordinate.
*
* @param string $pCoord Coordinate of the cell to delete
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function delete($pCoord)
{
if ($pCoord === $this->currentCoordinate && !is_null($this->currentCell)) {
$this->currentCell->detach();
$this->currentCoordinate = null;
$this->currentCell = null;
$this->currentCellIsDirty = false;
}
unset($this->index[$pCoord]);
// Delete the entry from cache
$this->cache->delete($this->cachePrefix . $pCoord);
}
/**
* Get a list of all cell coordinates currently held in the collection.
*
* @return string[]
*/
public function getCoordinates()
{
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()
{
$sortKeys = [];
foreach ($this->getCoordinates() as $coord) {
sscanf($coord, '%[A-Z]%d', $column, $row);
$sortKeys[sprintf('%09d%3s', $row, $column)] = $coord;
}
ksort($sortKeys);
return array_values($sortKeys);
}
/**
* Get highest worksheet column and highest row that have cell records.
*
* @return array Highest column name and highest row number
*/
public function getHighestRowAndColumn()
{
// Lookup highest column and highest row
$col = ['A' => '1A'];
$row = [1];
foreach ($this->getCoordinates() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
$row[$r] = $r;
$col[$c] = strlen($c) . $c;
}
if (!empty($row)) {
// Determine highest column and row
$highestRow = max($row);
$highestColumn = substr(max($col), 1);
}
return [
'row' => $highestRow,
'column' => $highestColumn,
];
}
/**
* Return the cell coordinate of the currently active cell object.
*
* @return string
*/
public function getCurrentCoordinate()
{
return $this->currentCoordinate;
}
/**
* Return the column coordinate of the currently active cell object.
*
* @return string
*/
public function getCurrentColumn()
{
sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row);
return $column;
}
/**
* Return the row coordinate of the currently active cell object.
*
* @return int
*/
public function getCurrentRow()
{
sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row);
return (int) $row;
}
/**
* Get highest worksheet column.
*
* @param 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)
{
if ($row == null) {
$colRow = $this->getHighestRowAndColumn();
return $colRow['column'];
}
$columnList = [1];
foreach ($this->getCoordinates() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($r != $row) {
continue;
}
$columnList[] = Cell::columnIndexFromString($c);
}
return Cell::stringFromColumnIndex(max($columnList) - 1);
}
/**
* Get highest worksheet row.
*
* @param 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($column = null)
{
if ($column == null) {
$colRow = $this->getHighestRowAndColumn();
return $colRow['row'];
}
$rowList = [0];
foreach ($this->getCoordinates() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($c != $column) {
continue;
}
$rowList[] = $r;
}
return max($rowList);
}
/**
* Generate a unique ID for cache referencing.
*
* @return string Unique Reference
*/
private function getUniqueID()
{
return uniqid('phpspreadsheet-', true) . '-';
}
/**
* Clone the cell collection.
*
* @param Worksheet $parent The new worksheet that we're copying to
*
* @return self
*/
public function cloneCellCollection(Worksheet $parent)
{
$this->storeCurrentCell();
$newCollection = clone $this;
$newCollection->parent = $parent;
if (($newCollection->currentCell !== null) && (is_object($newCollection->currentCell))) {
$newCollection->currentCell->attach($this);
}
// Get old values
$oldKeys = $newCollection->getAllCacheKeys();
$oldValues = $newCollection->cache->getMultiple($oldKeys);
$newValues = [];
$oldCachePrefix = $newCollection->cachePrefix;
// Change prefix
$newCollection->cachePrefix = $newCollection->getUniqueID();
foreach ($oldValues as $oldKey => $value) {
$newValues[str_replace($oldCachePrefix, $newCollection->cachePrefix, $oldKey)] = clone $value;
}
// Store new values
$stored = $newCollection->cache->setMultiple($newValues);
if (!$stored) {
$newCollection->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception('Failed to copy cells in cache');
}
return $newCollection;
}
/**
* Remove a row, deleting all cells in that row.
*
* @param string $row Row number to remove
*/
public function removeRow($row)
{
foreach ($this->getCoordinates() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($r == $row) {
$this->delete($coord);
}
}
}
/**
* Remove a column, deleting all cells in that column.
*
* @param string $column Column ID to remove
*/
public function removeColumn($column)
{
foreach ($this->getCoordinates() as $coord) {
sscanf($coord, '%[A-Z]%d', $c, $r);
if ($c == $column) {
$this->delete($coord);
}
}
}
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function storeCurrentCell()
{
if ($this->currentCellIsDirty && !empty($this->currentCoordinate)) {
$this->currentCell->detach();
$stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell);
if (!$stored) {
$this->__destruct();
throw new \PhpOffice\PhpSpreadsheet\Exception("Failed to store cell {$this->currentCoordinate} in cache");
}
$this->currentCellIsDirty = false;
}
$this->currentCoordinate = null;
$this->currentCell = null;
}
/**
* Add or update a cell identified by its coordinate into the collection.
*
* @param string $pCoord Coordinate of the cell to update
* @param Cell $cell Cell to update
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return Cell
*/
public function add($pCoord, Cell $cell)
{
if ($pCoord !== $this->currentCoordinate) {
$this->storeCurrentCell();
}
$this->index[$pCoord] = true;
$this->currentCoordinate = $pCoord;
$this->currentCell = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $pCoord Coordinate of the cell
*
* @throws \PhpOffice\PhpSpreadsheet\Exception
*
* @return Cell Cell that was found, or null if not found
*/
public function get($pCoord)
{
if ($pCoord === $this->currentCoordinate) {
return $this->currentCell;
}
$this->storeCurrentCell();
// Return null if requested entry doesn't exist in collection
if (!$this->has($pCoord)) {
return null;
}
// Check if the entry that has been requested actually exists
$cell = $this->cache->get($this->cachePrefix . $pCoord);
if ($cell === null) {
throw new \PhpOffice\PhpSpreadsheet\Exception("Cell entry {$pCoord} 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 = $pCoord;
$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()
{
if (!is_null($this->currentCell)) {
$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());
}
/**
* Returns all known cache keys.
*
* @return string
*/
private function getAllCacheKeys()
{
$keys = [];
foreach ($this->getCoordinates() as $coordinate) {
$keys[] = $this->cachePrefix . $coordinate;
}
return $keys;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Collection;
use PhpOffice\PhpSpreadsheet\Settings;
use PhpOffice\PhpSpreadsheet\Worksheet;
/**
* Copyright (c) 2006 - 2016 PhpSpreadsheet.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category PhpSpreadsheet
*
* @copyright Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
abstract class CellsFactory
{
/**
* Initialise the cache storage.
*
* @param Worksheet $parent Enable cell caching for this worksheet
*
* @return Cells
* */
public static function getInstance(Worksheet $parent)
{
$instance = new Cells($parent, Settings::getCache());
return $instance;
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Collection;
/**
* This is the default implementation for in-memory cell collection.
*
* Alternatives implementation should leverage off-memory, non-volatile storage
* to reduce overall memory usage.
*/
class Memory implements \Psr\SimpleCache\CacheInterface
{
private $cache = [];
public function clear()
{
$this->cache = [];
return true;
}
public function delete($key)
{
unset($this->cache[$key]);
return true;
}
public function deleteMultiple($keys)
{
foreach ($keys as $key) {
$this->delete($key);
}
return true;
}
public function get($key, $default = null)
{
if ($this->has($key)) {
return $this->cache[$key];
}
return $default;
}
public function getMultiple($keys, $default = null)
{
$results = [];
foreach ($keys as $key) {
$results[$key] = $this->get($key, $default);
}
return $results;
}
public function has($key)
{
return array_key_exists($key, $this->cache);
}
public function set($key, $value, $ttl = null)
{
$this->cache[$key] = $value;
return true;
}
public function setMultiple($values, $ttl = null)
{
foreach ($values as $key => $value) {
$this->set($key, $value);
}
return true;
}
}

View File

@ -84,19 +84,7 @@ class Migrator
'PHPExcel_Writer_Excel2007_Workbook' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Workbook', 'PHPExcel_Writer_Excel2007_Workbook' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Workbook',
'PHPExcel_Writer_Excel2007_Worksheet' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Worksheet', 'PHPExcel_Writer_Excel2007_Worksheet' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\Worksheet',
'PHPExcel_Writer_Excel2007_WriterPart' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\WriterPart', 'PHPExcel_Writer_Excel2007_WriterPart' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx\\WriterPart',
'PHPExcel_CachedObjectStorage_APC' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\APC', 'PHPExcel_CachedObjectStorage_CacheBase' => '\\PhpOffice\\PhpSpreadsheet\\Collection\\Cells',
'PHPExcel_CachedObjectStorage_CacheBase' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\CacheBase',
'PHPExcel_CachedObjectStorage_DiscISAM' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\DiscISAM',
'PHPExcel_CachedObjectStorage_ICache' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\ICache',
'PHPExcel_CachedObjectStorage_Igbinary' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Igbinary',
'PHPExcel_CachedObjectStorage_Memcache' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Memcache',
'PHPExcel_CachedObjectStorage_Memory' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Memory',
'PHPExcel_CachedObjectStorage_MemoryGZip' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\MemoryGZip',
'PHPExcel_CachedObjectStorage_MemorySerialized' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\MemorySerialized',
'PHPExcel_CachedObjectStorage_PHPTemp' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\PHPTemp',
'PHPExcel_CachedObjectStorage_SQLite' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\SQLite3',
'PHPExcel_CachedObjectStorage_SQLite3' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\SQLite3',
'PHPExcel_CachedObjectStorage_Wincache' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorage\\Wincache',
'PHPExcel_CalcEngine_CyclicReferenceStack' => '\\PhpOffice\\PhpSpreadsheet\\CalcEngine\\CyclicReferenceStack', 'PHPExcel_CalcEngine_CyclicReferenceStack' => '\\PhpOffice\\PhpSpreadsheet\\CalcEngine\\CyclicReferenceStack',
'PHPExcel_CalcEngine_Logger' => '\\PhpOffice\\PhpSpreadsheet\\CalcEngine\\Logger', 'PHPExcel_CalcEngine_Logger' => '\\PhpOffice\\PhpSpreadsheet\\CalcEngine\\Logger',
'PHPExcel_Calculation_Functions' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions', 'PHPExcel_Calculation_Functions' => '\\PhpOffice\\PhpSpreadsheet\\Calculation\\Functions',
@ -201,7 +189,7 @@ class Migrator
'PHPExcel_Writer_PDF' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Pdf', 'PHPExcel_Writer_PDF' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Pdf',
'PHPExcel_Writer_Excel5' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xls', 'PHPExcel_Writer_Excel5' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xls',
'PHPExcel_Writer_Excel2007' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx', 'PHPExcel_Writer_Excel2007' => '\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx',
'PHPExcel_CachedObjectStorageFactory' => '\\PhpOffice\\PhpSpreadsheet\\CachedObjectStorageFactory', 'PHPExcel_CachedObjectStorageFactory' => '\\PhpOffice\\PhpSpreadsheet\\Collection\\CellsFactory',
'PHPExcel_Calculation' => '\\PhpOffice\\PhpSpreadsheet\\Calculation', 'PHPExcel_Calculation' => '\\PhpOffice\\PhpSpreadsheet\\Calculation',
'PHPExcel_Cell' => '\\PhpOffice\\PhpSpreadsheet\\Cell', 'PHPExcel_Cell' => '\\PhpOffice\\PhpSpreadsheet\\Cell',
'PHPExcel_Chart' => '\\PhpOffice\\PhpSpreadsheet\\Chart', 'PHPExcel_Chart' => '\\PhpOffice\\PhpSpreadsheet\\Chart',

View File

@ -387,9 +387,9 @@ class ReferenceHelper
public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, Worksheet $pSheet = null) public function insertNewBefore($pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, Worksheet $pSheet = null)
{ {
$remove = ($pNumCols < 0 || $pNumRows < 0); $remove = ($pNumCols < 0 || $pNumRows < 0);
$aCellCollection = $pSheet->getCellCollection(); $allCoordinates = $pSheet->getCoordinates();
// Get coordinates of $pBefore // Get coordinate of $pBefore
$beforeColumn = 'A'; $beforeColumn = 'A';
$beforeRow = 1; $beforeRow = 1;
list($beforeColumn, $beforeRow) = Cell::coordinateFromString($pBefore); list($beforeColumn, $beforeRow) = Cell::coordinateFromString($pBefore);
@ -427,39 +427,39 @@ class ReferenceHelper
} }
} }
// Loop through cells, bottom-up, and change cell coordinates // Loop through cells, bottom-up, and change cell coordinate
if ($remove) { if ($remove) {
// It's faster to reverse and pop than to use unshift, especially with large cell collections // It's faster to reverse and pop than to use unshift, especially with large cell collections
$aCellCollection = array_reverse($aCellCollection); $allCoordinates = array_reverse($allCoordinates);
} }
while ($cellID = array_pop($aCellCollection)) { while ($coordinate = array_pop($allCoordinates)) {
$cell = $pSheet->getCell($cellID); $cell = $pSheet->getCell($coordinate);
$cellIndex = Cell::columnIndexFromString($cell->getColumn()); $cellIndex = Cell::columnIndexFromString($cell->getColumn());
if ($cellIndex - 1 + $pNumCols < 0) { if ($cellIndex - 1 + $pNumCols < 0) {
continue; continue;
} }
// New coordinates // New coordinate
$newCoordinates = Cell::stringFromColumnIndex($cellIndex - 1 + $pNumCols) . ($cell->getRow() + $pNumRows); $newCoordinate = Cell::stringFromColumnIndex($cellIndex - 1 + $pNumCols) . ($cell->getRow() + $pNumRows);
// Should the cell be updated? Move value and cellXf index from one cell to another. // Should the cell be updated? Move value and cellXf index from one cell to another.
if (($cellIndex >= $beforeColumnIndex) && ($cell->getRow() >= $beforeRow)) { if (($cellIndex >= $beforeColumnIndex) && ($cell->getRow() >= $beforeRow)) {
// Update cell styles // Update cell styles
$pSheet->getCell($newCoordinates)->setXfIndex($cell->getXfIndex()); $pSheet->getCell($newCoordinate)->setXfIndex($cell->getXfIndex());
// Insert this cell at its new location // Insert this cell at its new location
if ($cell->getDataType() == Cell\DataType::TYPE_FORMULA) { if ($cell->getDataType() == Cell\DataType::TYPE_FORMULA) {
// Formula should be adjusted // Formula should be adjusted
$pSheet->getCell($newCoordinates) $pSheet->getCell($newCoordinate)
->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle())); ->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
} else { } else {
// Formula should not be adjusted // Formula should not be adjusted
$pSheet->getCell($newCoordinates)->setValue($cell->getValue()); $pSheet->getCell($newCoordinate)->setValue($cell->getValue());
} }
// Clear the original cell // Clear the original cell
$pSheet->getCellCacheController()->deleteCacheData($cellID); $pSheet->getCellCollection()->delete($coordinate);
} else { } else {
/* We don't need to update styles for rows/columns before our insertion position, /* We don't need to update styles for rows/columns before our insertion position,
but we do still need to adjust any formulae in those cells */ but we do still need to adjust any formulae in those cells */
@ -818,8 +818,8 @@ class ReferenceHelper
} }
foreach ($spreadsheet->getWorksheetIterator() as $sheet) { foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
foreach ($sheet->getCellCollection(false) as $cellID) { foreach ($sheet->getCoordinates(false) as $coordinate) {
$cell = $sheet->getCell($cellID); $cell = $sheet->getCell($coordinate);
if (($cell !== null) && ($cell->getDataType() == Cell\DataType::TYPE_FORMULA)) { if (($cell !== null) && ($cell->getDataType() == Cell\DataType::TYPE_FORMULA)) {
$formula = $cell->getValue(); $formula = $cell->getValue();
if (strpos($formula, $oldName) !== false) { if (strpos($formula, $oldName) !== false) {
@ -886,10 +886,10 @@ class ReferenceHelper
private function updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0) private function updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
{ {
if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) { if (strpos($pCellReference, ':') === false && strpos($pCellReference, ',') === false) {
// Get coordinates of $pBefore // Get coordinate of $pBefore
list($beforeColumn, $beforeRow) = Cell::coordinateFromString($pBefore); list($beforeColumn, $beforeRow) = Cell::coordinateFromString($pBefore);
// Get coordinates of $pCellReference // Get coordinate of $pCellReference
list($newColumn, $newRow) = Cell::coordinateFromString($pCellReference); list($newColumn, $newRow) = Cell::coordinateFromString($pCellReference);
// Verify which parts should be updated // Verify which parts should be updated

View File

@ -2,6 +2,9 @@
namespace PhpOffice\PhpSpreadsheet; namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Collection\Memory;
use Psr\SimpleCache\CacheInterface;
/** /**
* Copyright (c) 2006 - 2016 PhpSpreadsheet. * Copyright (c) 2006 - 2016 PhpSpreadsheet.
* *
@ -37,7 +40,6 @@ class Settings
private static $chartRenderers = [ private static $chartRenderers = [
self::CHART_RENDERER_JPGRAPH, self::CHART_RENDERER_JPGRAPH,
]; ];
private static $pdfRenderers = [ private static $pdfRenderers = [
self::PDF_RENDERER_TCPDF, self::PDF_RENDERER_TCPDF,
self::PDF_RENDERER_DOMPDF, self::PDF_RENDERER_DOMPDF,
@ -77,37 +79,11 @@ class Settings
private static $libXmlLoaderOptions = null; private static $libXmlLoaderOptions = null;
/** /**
* Return the name of the method that is currently configured for cell cacheing. * The cache implementation to be used for cell collection.
* *
* @return string Name of the cacheing method * @var CacheInterface
*/ */
public static function getCacheStorageMethod() private static $cache;
{
return CachedObjectStorageFactory::getCacheStorageMethod();
}
/**
* Return the name of the class that is currently being used for cell cacheing.
*
* @return string Name of the class currently being used for cacheing
*/
public static function getCacheStorageClass()
{
return CachedObjectStorageFactory::getCacheStorageClass();
}
/**
* Set the method that should be used for cell caching.
*
* @param string $method Name of the caching method
* @param array $arguments Optional configuration arguments for the caching method
*
* @return bool Success or failure
*/
public static function setCacheStorageMethod($method = CachedObjectStorageFactory::CACHE_IN_MEMORY, $arguments = [])
{
return CachedObjectStorageFactory::initialize($method, $arguments);
}
/** /**
* Set the locale code to use for formula translations and any special formatting. * Set the locale code to use for formula translations and any special formatting.
@ -256,4 +232,28 @@ class Settings
return self::$libXmlLoaderOptions; return self::$libXmlLoaderOptions;
} }
/**
* Sets the implementation of cache that should be used for cell collection.
*
* @param CacheInterface $cache
*/
public static function setCache(CacheInterface $cache)
{
self::$cache = $cache;
}
/**
* Gets the implementation of cache that should be used for cell collection.
*
* @return CacheInterface
*/
public static function getCache()
{
if (!self::$cache) {
self::$cache = new Memory();
}
return self::$cache;
}
} }

View File

@ -786,8 +786,8 @@ class Spreadsheet
$pSheet->rebindParent($this); $pSheet->rebindParent($this);
// update the cellXfs // update the cellXfs
foreach ($pSheet->getCellCollection(false) as $cellID) { foreach ($pSheet->getCoordinates(false) as $coordinate) {
$cell = $pSheet->getCell($cellID); $cell = $pSheet->getCell($coordinate);
$cell->setXfIndex($cell->getXfIndex() + $countCellXfs); $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
} }
@ -1010,8 +1010,8 @@ class Spreadsheet
// then update cellXf indexes for cells // then update cellXf indexes for cells
foreach ($this->workSheetCollection as $worksheet) { foreach ($this->workSheetCollection as $worksheet) {
foreach ($worksheet->getCellCollection(false) as $cellID) { foreach ($worksheet->getCoordinates(false) as $coordinate) {
$cell = $worksheet->getCell($cellID); $cell = $worksheet->getCell($coordinate);
$xfIndex = $cell->getXfIndex(); $xfIndex = $cell->getXfIndex();
if ($xfIndex > $pIndex) { if ($xfIndex > $pIndex) {
// decrease xf index by 1 // decrease xf index by 1
@ -1114,8 +1114,8 @@ class Spreadsheet
foreach ($this->getWorksheetIterator() as $sheet) { foreach ($this->getWorksheetIterator() as $sheet) {
// from cells // from cells
foreach ($sheet->getCellCollection(false) as $cellID) { foreach ($sheet->getCoordinates(false) as $coordinate) {
$cell = $sheet->getCell($cellID); $cell = $sheet->getCell($coordinate);
++$countReferencesCellXf[$cell->getXfIndex()]; ++$countReferencesCellXf[$cell->getXfIndex()];
} }
@ -1158,8 +1158,8 @@ class Spreadsheet
// update the xfIndex for all cells, row dimensions, column dimensions // update the xfIndex for all cells, row dimensions, column dimensions
foreach ($this->getWorksheetIterator() as $sheet) { foreach ($this->getWorksheetIterator() as $sheet) {
// for all cells // for all cells
foreach ($sheet->getCellCollection(false) as $cellID) { foreach ($sheet->getCoordinates(false) as $coordinate) {
$cell = $sheet->getCell($cellID); $cell = $sheet->getCell($coordinate);
$cell->setXfIndex($map[$cell->getXfIndex()]); $cell->setXfIndex($map[$cell->getXfIndex()]);
} }

View File

@ -3,6 +3,8 @@
namespace PhpOffice\PhpSpreadsheet; namespace PhpOffice\PhpSpreadsheet;
use ArrayObject; use ArrayObject;
use PhpOffice\PhpSpreadsheet\Collection\Cells;
use PhpOffice\PhpSpreadsheet\Collection\CellsFactory;
/** /**
* Copyright (c) 2006 - 2016 PhpSpreadsheet. * Copyright (c) 2006 - 2016 PhpSpreadsheet.
@ -53,9 +55,9 @@ class Worksheet implements IComparable
private $parent; private $parent;
/** /**
* Cacheable collection of cells. * Collection of cells.
* *
* @var CachedObjectStorage_xxx * @var Cells
*/ */
private $cellCollection; private $cellCollection;
@ -340,7 +342,7 @@ class Worksheet implements IComparable
$this->setCodeName($this->getTitle()); $this->setCodeName($this->getTitle());
$this->setSheetState(self::SHEETSTATE_VISIBLE); $this->setSheetState(self::SHEETSTATE_VISIBLE);
$this->cellCollection = CachedObjectStorageFactory::getInstance($this); $this->cellCollection = CellsFactory::getInstance($this);
// Set page setup // Set page setup
$this->pageSetup = new Worksheet\PageSetup(); $this->pageSetup = new Worksheet\PageSetup();
// Set page margins // Set page margins
@ -387,11 +389,11 @@ class Worksheet implements IComparable
} }
/** /**
* Return the cache controller for the cell collection. * Return the cell collection.
* *
* @return CachedObjectStorage_xxx * @return Cells
*/ */
public function getCellCacheController() public function getCellCollection()
{ {
return $this->cellCollection; return $this->cellCollection;
} }
@ -461,37 +463,23 @@ class Worksheet implements IComparable
} }
/** /**
* Get collection of cells. * Get a sorted list of all cell coordinates currently held in the collection by row and column.
* *
* @param bool $pSorted Also sort the cell collection? * @param bool $sorted Also sort the cell collection?
* *
* @return Cell[] * @return string[]
*/ */
public function getCellCollection($pSorted = true) public function getCoordinates($sorted = true)
{ {
if ($pSorted) { if ($this->cellCollection == null) {
// Re-order cell collection return [];
return $this->sortCellCollection();
}
if ($this->cellCollection !== null) {
return $this->cellCollection->getCellList();
} }
return []; if ($sorted) {
} return $this->cellCollection->getSortedCoordinates();
/**
* Sort collection of cells.
*
* @return Worksheet
*/
public function sortCellCollection()
{
if ($this->cellCollection !== null) {
return $this->cellCollection->getSortedCellList();
} }
return []; return $this->cellCollection->getCoordinates();
} }
/** /**
@ -737,11 +725,11 @@ class Worksheet implements IComparable
} }
// loop through all cells in the worksheet // loop through all cells in the worksheet
foreach ($this->getCellCollection(false) as $cellID) { foreach ($this->getCoordinates(false) as $coordinate) {
$cell = $this->getCell($cellID, false); $cell = $this->getCell($coordinate, false);
if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) { if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
//Determine if cell is in merge range //Determine if cell is in merge range
$isMerged = isset($isMergeCell[$this->cellCollection->getCurrentAddress()]); $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentCoordinate()]);
//By default merged cells should be ignored //By default merged cells should be ignored
$isMergedButProceed = false; $isMergedButProceed = false;
@ -1201,8 +1189,8 @@ class Worksheet implements IComparable
public function getCell($pCoordinate = 'A1', $createIfNotExists = true) public function getCell($pCoordinate = 'A1', $createIfNotExists = true)
{ {
// Check cell collection // Check cell collection
if ($this->cellCollection->isDataSet(strtoupper($pCoordinate))) { if ($this->cellCollection->has(strtoupper($pCoordinate))) {
return $this->cellCollection->getCacheData($pCoordinate); return $this->cellCollection->get($pCoordinate);
} }
// Worksheet reference? // Worksheet reference?
@ -1251,8 +1239,8 @@ class Worksheet implements IComparable
$columnLetter = Cell::stringFromColumnIndex($pColumn); $columnLetter = Cell::stringFromColumnIndex($pColumn);
$coordinate = $columnLetter . $pRow; $coordinate = $columnLetter . $pRow;
if ($this->cellCollection->isDataSet($coordinate)) { if ($this->cellCollection->has($coordinate)) {
return $this->cellCollection->getCacheData($coordinate); return $this->cellCollection->get($coordinate);
} }
// Create new cell object, if required // Create new cell object, if required
@ -1268,10 +1256,8 @@ class Worksheet implements IComparable
*/ */
private function createNewCell($pCoordinate) private function createNewCell($pCoordinate)
{ {
$cell = $this->cellCollection->addCacheData( $cell = new Cell(null, Cell\DataType::TYPE_NULL, $this);
$pCoordinate, $this->cellCollection->add($pCoordinate, $cell);
new Cell(null, Cell\DataType::TYPE_NULL, $this)
);
$this->cellCollectionIsSorted = false; $this->cellCollectionIsSorted = false;
// Coordinates // Coordinates
@ -1344,7 +1330,7 @@ class Worksheet implements IComparable
$aCoordinates = Cell::coordinateFromString($pCoordinate); $aCoordinates = Cell::coordinateFromString($pCoordinate);
// Cell exists? // Cell exists?
return $this->cellCollection->isDataSet($pCoordinate); return $this->cellCollection->has($pCoordinate);
} }
/** /**
@ -2129,7 +2115,7 @@ class Worksheet implements IComparable
$objReferenceHelper = ReferenceHelper::getInstance(); $objReferenceHelper = ReferenceHelper::getInstance();
$objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this); $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
for ($r = 0; $r < $pNumRows; ++$r) { for ($r = 0; $r < $pNumRows; ++$r) {
$this->getCellCacheController()->removeRow($highestRow); $this->getCellCollection()->removeRow($highestRow);
--$highestRow; --$highestRow;
} }
} else { } else {
@ -2157,7 +2143,7 @@ class Worksheet implements IComparable
$objReferenceHelper = ReferenceHelper::getInstance(); $objReferenceHelper = ReferenceHelper::getInstance();
$objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this); $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
for ($c = 0; $c < $pNumCols; ++$c) { for ($c = 0; $c < $pNumCols; ++$c) {
$this->getCellCacheController()->removeColumn($highestColumn); $this->getCellCollection()->removeColumn($highestColumn);
$highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2); $highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2);
} }
} else { } else {
@ -2568,9 +2554,9 @@ class Worksheet implements IComparable
$cRef = ($returnCellRef) ? $col : ++$c; $cRef = ($returnCellRef) ? $col : ++$c;
// Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen // Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
// so we test and retrieve directly against cellCollection // so we test and retrieve directly against cellCollection
if ($this->cellCollection->isDataSet($col . $row)) { if ($this->cellCollection->has($col . $row)) {
// Cell exists // Cell exists
$cell = $this->cellCollection->getCacheData($col . $row); $cell = $this->cellCollection->get($col . $row);
if ($cell->getValue() !== null) { if ($cell->getValue() !== null) {
if ($cell->getValue() instanceof RichText) { if ($cell->getValue() instanceof RichText) {
$returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText(); $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
@ -2688,7 +2674,7 @@ class Worksheet implements IComparable
public function garbageCollect() public function garbageCollect()
{ {
// Flush cache // Flush cache
$this->cellCollection->getCacheData('A1'); $this->cellCollection->get('A1');
// Lookup highest column and highest row if cells are cleaned // Lookup highest column and highest row if cells are cleaned
$colRow = $this->cellCollection->getHighestRowAndColumn(); $colRow = $this->cellCollection->getHighestRowAndColumn();
@ -2973,8 +2959,7 @@ class Worksheet implements IComparable
if (is_object($val) || (is_array($val))) { if (is_object($val) || (is_array($val))) {
if ($key == 'cellCollection') { if ($key == 'cellCollection') {
$newCollection = clone $this->cellCollection; $newCollection = $this->cellCollection->cloneCellCollection($this);
$newCollection->copyCellCollection($this);
$this->cellCollection = $newCollection; $this->cellCollection = $newCollection;
} elseif ($key == 'drawingCollection') { } elseif ($key == 'drawingCollection') {
$newCollection = new ArrayObject(); $newCollection = new ArrayObject();

View File

@ -148,8 +148,8 @@ class Xls extends BaseWriter implements IWriter
// add fonts from rich text eleemnts // add fonts from rich text eleemnts
for ($i = 0; $i < $countSheets; ++$i) { for ($i = 0; $i < $countSheets; ++$i) {
foreach ($this->writerWorksheets[$i]->phpSheet->getCellCollection() as $cellID) { foreach ($this->writerWorksheets[$i]->phpSheet->getCoordinates() as $coordinate) {
$cell = $this->writerWorksheets[$i]->phpSheet->getCell($cellID); $cell = $this->writerWorksheets[$i]->phpSheet->getCell($coordinate);
$cVal = $cell->getValue(); $cVal = $cell->getValue();
if ($cVal instanceof \PhpOffice\PhpSpreadsheet\RichText) { if ($cVal instanceof \PhpOffice\PhpSpreadsheet\RichText) {
$elements = $cVal->getRichTextElements(); $elements = $cVal->getRichTextElements();

View File

@ -400,13 +400,12 @@ class Worksheet extends BIFFwriter
} }
// Write Cells // Write Cells
foreach ($phpSheet->getCellCollection() as $cellID) { foreach ($phpSheet->getCoordinates() as $coordinate) {
$cell = $phpSheet->getCell($cellID); $cell = $phpSheet->getCell($coordinate);
$row = $cell->getRow() - 1; $row = $cell->getRow() - 1;
$column = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($cell->getColumn()) - 1; $column = \PhpOffice\PhpSpreadsheet\Cell::columnIndexFromString($cell->getColumn()) - 1;
// Don't break Excel! // Don't break Excel!
// if ($row + 1 > 65536 or $column + 1 > 256) {
if ($row > 65535 || $column > 255) { if ($row > 65535 || $column > 255) {
break; break;
} }

View File

@ -53,8 +53,8 @@ class StringTable extends WriterPart
$aFlippedStringTable = $this->flipStringTable($aStringTable); $aFlippedStringTable = $this->flipStringTable($aStringTable);
// Loop through cells // Loop through cells
foreach ($pSheet->getCellCollection() as $cellID) { foreach ($pSheet->getCoordinates() as $coordinate) {
$cell = $pSheet->getCell($cellID); $cell = $pSheet->getCell($coordinate);
$cellValue = $cell->getValue(); $cellValue = $cell->getValue();
if (!is_object($cellValue) && if (!is_object($cellValue) &&
($cellValue !== null) && ($cellValue !== null) &&

View File

@ -995,9 +995,9 @@ class Worksheet extends WriterPart
// Loop through cells // Loop through cells
$cellsByRow = []; $cellsByRow = [];
foreach ($pSheet->getCellCollection() as $cellID) { foreach ($pSheet->getCoordinates() as $coordinate) {
$cellAddress = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($cellID); $cellAddress = \PhpOffice\PhpSpreadsheet\Cell::coordinateFromString($coordinate);
$cellsByRow[$cellAddress[1]][] = $cellID; $cellsByRow[$cellAddress[1]][] = $coordinate;
} }
$currentRow = 0; $currentRow = 0;

View File

@ -2,10 +2,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Cell; namespace PhpOffice\PhpSpreadsheetTests\Cell;
use PhpOffice\PhpSpreadsheet\CachedObjectStorage\Memory;
use PhpOffice\PhpSpreadsheet\Cell; use PhpOffice\PhpSpreadsheet\Cell;
use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder; use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Collection\Cells;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet;
@ -42,12 +42,12 @@ class AdvancedValueBinderTest extends \PHPUnit_Framework_TestCase
public function testCurrency($value, $valueBinded, $format, $thousandsSeparator, $decimalSeparator, $currencyCode) public function testCurrency($value, $valueBinded, $format, $thousandsSeparator, $decimalSeparator, $currencyCode)
{ {
$sheet = $this->getMockBuilder(Worksheet::class) $sheet = $this->getMockBuilder(Worksheet::class)
->setMethods(['getStyle', 'getNumberFormat', 'setFormatCode', 'getCellCacheController']) ->setMethods(['getStyle', 'getNumberFormat', 'setFormatCode', 'getCellCollection'])
->getMock(); ->getMock();
$cache = $this->getMockBuilder(Memory::class) $cellCollection = $this->getMockBuilder(Cells::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$cache->expects($this->any()) $cellCollection->expects($this->any())
->method('getParent') ->method('getParent')
->will($this->returnValue($sheet)); ->will($this->returnValue($sheet));
@ -62,8 +62,8 @@ class AdvancedValueBinderTest extends \PHPUnit_Framework_TestCase
->with($format) ->with($format)
->will($this->returnSelf()); ->will($this->returnSelf());
$sheet->expects($this->any()) $sheet->expects($this->any())
->method('getCellCacheController') ->method('getCellCollection')
->will($this->returnValue($cache)); ->will($this->returnValue($cellCollection));
StringHelper::setCurrencyCode($currencyCode); StringHelper::setCurrencyCode($currencyCode);
StringHelper::setDecimalSeparator($decimalSeparator); StringHelper::setDecimalSeparator($decimalSeparator);

View File

@ -0,0 +1,118 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Collection;
use PhpOffice\PhpSpreadsheet\Cell;
use PhpOffice\PhpSpreadsheet\Collection\Cells;
use PhpOffice\PhpSpreadsheet\Collection\Memory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet;
class CellsTest extends \PHPUnit_Framework_TestCase
{
public function testCollectionCell()
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$collection = $sheet->getCellCollection();
// Assert empty state
$this->assertEquals([], $collection->getCoordinates(), 'cell list should be empty');
$this->assertEquals([], $collection->getSortedCoordinates(), 'sorted cell list should be empty');
$this->assertNull($collection->get('B2'), 'getting non-existing cell must return null');
$this->assertFalse($collection->has('B2'), 'non-existing cell should be non-existent');
// Add one cell
$cell1 = $sheet->getCell('B2');
$this->assertSame($cell1, $collection->add('B2', $cell1), 'adding a cell should return the cell');
// Assert cell presence
$this->assertEquals(['B2'], $collection->getCoordinates(), 'cell list should contains the cell');
$this->assertEquals(['B2'], $collection->getSortedCoordinates(), 'sorted cell list contains the cell');
$this->assertSame($cell1, $collection->get('B2'), 'should get exact same object');
$this->assertTrue($collection->has('B2'), 'cell should exists');
// Add a second cell
$cell2 = $sheet->getCell('A1');
$this->assertSame($cell2, $collection->add('A1', $cell2), 'adding a second cell should return the cell');
$this->assertEquals(['B2', 'A1'], $collection->getCoordinates(), 'cell list should contains the cell');
$this->assertEquals(['A1', 'B2'], $collection->getSortedCoordinates(), 'sorted cell list contains the cell');
// Assert collection copy
$sheet2 = $spreadsheet->createSheet();
$collection2 = $collection->cloneCellCollection($sheet2);
$this->assertTrue($collection2->has('A1'));
$copiedCell2 = $collection2->get('A1');
$this->assertNotSame($cell2, $copiedCell2, 'copied cell should not be the same object any more');
$this->assertSame($collection2, $copiedCell2->getParent(), 'copied cell should be owned by the copied collection');
$this->assertSame('A1', $copiedCell2->getCoordinate(), 'copied cell should keep attributes');
// Assert deletion
$collection->delete('B2');
$this->assertFalse($collection->has('B2'), 'cell should have been deleted');
$this->assertEquals(['A1'], $collection->getCoordinates(), 'cell list should contains the cell');
// Assert update
$cell2 = $sheet->getCell('A1');
$this->assertSame($sheet->getCellCollection(), $collection);
$this->assertSame($cell2, $collection->update($cell2), 'should update existing cell');
$cell3 = $sheet->getCell('C3');
$this->assertSame($cell3, $collection->update($cell3), 'should silently add non-existing cell');
$this->assertEquals(['A1', 'C3'], $collection->getCoordinates(), 'cell list should contains the cell');
}
public function testCacheLastCell()
{
$workbook = new Spreadsheet();
$cells = ['A1', 'A2'];
$sheet = $workbook->getActiveSheet();
$sheet->setCellValue('A1', 1);
$sheet->setCellValue('A2', 2);
$this->assertEquals($cells, $sheet->getCoordinates(), 'list should include last added cell');
}
public function testCanGetCellAfterAnotherIsDeleted()
{
$workbook = new Spreadsheet();
$sheet = $workbook->getActiveSheet();
$collection = $sheet->getCellCollection();
$sheet->setCellValue('A1', 1);
$sheet->setCellValue('A2', 1);
$collection->delete('A1');
$sheet->setCellValue('A3', 1);
$this->assertNotNull($collection->get('A2'), 'should be able to get back the cell even when another cell was deleted while this one was the current one');
}
/**
* @expectedException \PhpOffice\PhpSpreadsheet\Exception
*/
public function testThrowsWhenCellCannotBeRetrievedFromCache()
{
$collection = $this->getMockBuilder(Cells::class)
->setConstructorArgs([new Worksheet(), new Memory()])
->setMethods(['has'])
->getMock();
$collection->method('has')
->willReturn(true);
$collection->get('A2');
}
/**
* @expectedException \PhpOffice\PhpSpreadsheet\Exception
*/
public function testThrowsWhenCellCannotBeStoredInCache()
{
$cache = $this->createMock(Memory::class);
$cell = $this->createMock(Cell::class);
$cache->method('set')
->willReturn(false);
$collection = new Cells(new Worksheet(), $cache);
$collection->add('A1', $cell);
$collection->add('A2', $cell);
}
}

View File

@ -205,7 +205,7 @@ class OdsTest extends \PHPUnit_Framework_TestCase
public function testReadBoldItalicUnderline() public function testReadBoldItalicUnderline()
{ {
$this->markTestSkipped('Features not implemented yet'); $this->markTestIncomplete('Features not implemented yet');
$spreadsheet = $this->loadOOCalcTestFile(); $spreadsheet = $this->loadOOCalcTestFile();
$firstSheet = $spreadsheet->getSheet(0); $firstSheet = $spreadsheet->getSheet(0);

View File

@ -2,7 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Worksheet; namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\CachedObjectStorage\Memory; use PhpOffice\PhpSpreadsheet\Collection\Cells;
use PhpOffice\PhpSpreadsheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
@ -12,19 +12,19 @@ class AutoFilterTest extends \PHPUnit_Framework_TestCase
private $testInitialRange = 'H2:O256'; private $testInitialRange = 'H2:O256';
private $testAutoFilterObject; private $testAutoFilterObject;
private $mockWorksheetObject; private $mockWorksheetObject;
private $mockCacheController; private $cellCollection;
public function setUp() public function setUp()
{ {
$this->mockWorksheetObject = $this->getMockBuilder(Worksheet::class) $this->mockWorksheetObject = $this->getMockBuilder(Worksheet::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$this->mockCacheController = $this->getMockBuilder(Memory::class) $this->cellCollection = $this->getMockBuilder(Cells::class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$this->mockWorksheetObject->expects($this->any()) $this->mockWorksheetObject->expects($this->any())
->method('getCellCacheController') ->method('getCellCollection')
->will($this->returnValue($this->mockCacheController)); ->will($this->returnValue($this->cellCollection));
$this->testAutoFilterObject = new AutoFilter($this->testInitialRange, $this->mockWorksheetObject); $this->testAutoFilterObject = new AutoFilter($this->testInitialRange, $this->mockWorksheetObject);
} }

View File

@ -1,24 +0,0 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\CachedObjectStorageFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
class CellCollectionTest extends \PHPUnit_Framework_TestCase
{
public function testCacheLastCell()
{
$methods = CachedObjectStorageFactory::getCacheStorageMethods();
foreach ($methods as $method) {
CachedObjectStorageFactory::initialize($method);
$workbook = new Spreadsheet();
$cells = ['A1', 'A2'];
$worksheet = $workbook->getActiveSheet();
$worksheet->setCellValue('A1', 1);
$worksheet->setCellValue('A2', 2);
$this->assertEquals($cells, $worksheet->getCellCollection(), "Cache method \"$method\".");
CachedObjectStorageFactory::finalize();
}
}
}