1: <?php
2: /**
3: * PHPExcel
4: *
5: * Copyright (c) 2006 - 2014 PHPExcel
6: *
7: * This library is free software; you can redistribute it and/or
8: * modify it under the terms of the GNU Lesser General Public
9: * License as published by the Free Software Foundation; either
10: * version 2.1 of the License, or (at your option) any later version.
11: *
12: * This library is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: * Lesser General Public License for more details.
16: *
17: * You should have received a copy of the GNU Lesser General Public
18: * License along with this library; if not, write to the Free Software
19: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20: *
21: * @category PHPExcel
22: * @package PHPExcel_CachedObjectStorage
23: * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
24: * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
25: * @version 1.8.0, 2014-03-02
26: */
27:
28:
29: /**
30: * PHPExcel_CachedObjectStorage_Memcache
31: *
32: * @category PHPExcel
33: * @package PHPExcel_CachedObjectStorage
34: * @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
35: */
36: class PHPExcel_CachedObjectStorage_Memcache extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache {
37:
38: /**
39: * Prefix used to uniquely identify cache data for this worksheet
40: *
41: * @var string
42: */
43: private $_cachePrefix = null;
44:
45: /**
46: * Cache timeout
47: *
48: * @var integer
49: */
50: private $_cacheTime = 600;
51:
52: /**
53: * Memcache interface
54: *
55: * @var resource
56: */
57: private $_memcache = null;
58:
59:
60: /**
61: * Store cell data in cache for the current cell object if it's "dirty",
62: * and the 'nullify' the current cell object
63: *
64: * @return void
65: * @throws PHPExcel_Exception
66: */
67: protected function _storeData() {
68: if ($this->_currentCellIsDirty && !empty($this->_currentObjectID)) {
69: $this->_currentObject->detach();
70:
71: $obj = serialize($this->_currentObject);
72: if (!$this->_memcache->replace($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) {
73: if (!$this->_memcache->add($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) {
74: $this->__destruct();
75: throw new PHPExcel_Exception('Failed to store cell '.$this->_currentObjectID.' in MemCache');
76: }
77: }
78: $this->_currentCellIsDirty = false;
79: }
80: $this->_currentObjectID = $this->_currentObject = null;
81: } // function _storeData()
82:
83:
84: /**
85: * Add or Update a cell in cache identified by coordinate address
86: *
87: * @param string $pCoord Coordinate address of the cell to update
88: * @param PHPExcel_Cell $cell Cell to update
89: * @return void
90: * @throws PHPExcel_Exception
91: */
92: public function addCacheData($pCoord, PHPExcel_Cell $cell) {
93: if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) {
94: $this->_storeData();
95: }
96: $this->_cellCache[$pCoord] = true;
97:
98: $this->_currentObjectID = $pCoord;
99: $this->_currentObject = $cell;
100: $this->_currentCellIsDirty = true;
101:
102: return $cell;
103: } // function addCacheData()
104:
105:
106: /**
107: * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell?
108: *
109: * @param string $pCoord Coordinate address of the cell to check
110: * @return void
111: * @return boolean
112: */
113: public function isDataSet($pCoord) {
114: // Check if the requested entry is the current object, or exists in the cache
115: if (parent::isDataSet($pCoord)) {
116: if ($this->_currentObjectID == $pCoord) {
117: return true;
118: }
119: // Check if the requested entry still exists in Memcache
120: $success = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache');
121: if ($success === false) {
122: // Entry no longer exists in Memcache, so clear it from the cache array
123: parent::deleteCacheData($pCoord);
124: throw new PHPExcel_Exception('Cell entry '.$pCoord.' no longer exists in MemCache');
125: }
126: return true;
127: }
128: return false;
129: } // function isDataSet()
130:
131:
132: /**
133: * Get cell at a specific coordinate
134: *
135: * @param string $pCoord Coordinate of the cell
136: * @throws PHPExcel_Exception
137: * @return PHPExcel_Cell Cell that was found, or null if not found
138: */
139: public function getCacheData($pCoord) {
140: if ($pCoord === $this->_currentObjectID) {
141: return $this->_currentObject;
142: }
143: $this->_storeData();
144:
145: // Check if the entry that has been requested actually exists
146: if (parent::isDataSet($pCoord)) {
147: $obj = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache');
148: if ($obj === false) {
149: // Entry no longer exists in Memcache, so clear it from the cache array
150: parent::deleteCacheData($pCoord);
151: throw new PHPExcel_Exception('Cell entry '.$pCoord.' no longer exists in MemCache');
152: }
153: } else {
154: // Return null if requested entry doesn't exist in cache
155: return null;
156: }
157:
158: // Set current entry to the requested entry
159: $this->_currentObjectID = $pCoord;
160: $this->_currentObject = unserialize($obj);
161: // Re-attach this as the cell's parent
162: $this->_currentObject->attach($this);
163:
164: // Return requested entry
165: return $this->_currentObject;
166: } // function getCacheData()
167:
168:
169: /**
170: * Get a list of all cell addresses currently held in cache
171: *
172: * @return array of string
173: */
174: public function getCellList() {
175: if ($this->_currentObjectID !== null) {
176: $this->_storeData();
177: }
178:
179: return parent::getCellList();
180: }
181:
182:
183: /**
184: * Delete a cell in cache identified by coordinate address
185: *
186: * @param string $pCoord Coordinate address of the cell to delete
187: * @throws PHPExcel_Exception
188: */
189: public function deleteCacheData($pCoord) {
190: // Delete the entry from Memcache
191: $this->_memcache->delete($this->_cachePrefix.$pCoord.'.cache');
192:
193: // Delete the entry from our cell address array
194: parent::deleteCacheData($pCoord);
195: } // function deleteCacheData()
196:
197:
198: /**
199: * Clone the cell collection
200: *
201: * @param PHPExcel_Worksheet $parent The new worksheet
202: * @return void
203: */
204: public function copyCellCollection(PHPExcel_Worksheet $parent) {
205: parent::copyCellCollection($parent);
206: // Get a new id for the new file name
207: $baseUnique = $this->_getUniqueID();
208: $newCachePrefix = substr(md5($baseUnique),0,8).'.';
209: $cacheList = $this->getCellList();
210: foreach($cacheList as $cellID) {
211: if ($cellID != $this->_currentObjectID) {
212: $obj = $this->_memcache->get($this->_cachePrefix.$cellID.'.cache');
213: if ($obj === false) {
214: // Entry no longer exists in Memcache, so clear it from the cache array
215: parent::deleteCacheData($cellID);
216: throw new PHPExcel_Exception('Cell entry '.$cellID.' no longer exists in MemCache');
217: }
218: if (!$this->_memcache->add($newCachePrefix.$cellID.'.cache',$obj,NULL,$this->_cacheTime)) {
219: $this->__destruct();
220: throw new PHPExcel_Exception('Failed to store cell '.$cellID.' in MemCache');
221: }
222: }
223: }
224: $this->_cachePrefix = $newCachePrefix;
225: } // function copyCellCollection()
226:
227:
228: /**
229: * Clear the cell collection and disconnect from our parent
230: *
231: * @return void
232: */
233: public function unsetWorksheetCells() {
234: if(!is_null($this->_currentObject)) {
235: $this->_currentObject->detach();
236: $this->_currentObject = $this->_currentObjectID = null;
237: }
238:
239: // Flush the Memcache cache
240: $this->__destruct();
241:
242: $this->_cellCache = array();
243:
244: // detach ourself from the worksheet, so that it can then delete this object successfully
245: $this->_parent = null;
246: } // function unsetWorksheetCells()
247:
248:
249: /**
250: * Initialise this new cell collection
251: *
252: * @param PHPExcel_Worksheet $parent The worksheet for this cell collection
253: * @param array of mixed $arguments Additional initialisation arguments
254: */
255: public function __construct(PHPExcel_Worksheet $parent, $arguments) {
256: $memcacheServer = (isset($arguments['memcacheServer'])) ? $arguments['memcacheServer'] : 'localhost';
257: $memcachePort = (isset($arguments['memcachePort'])) ? $arguments['memcachePort'] : 11211;
258: $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600;
259:
260: if (is_null($this->_cachePrefix)) {
261: $baseUnique = $this->_getUniqueID();
262: $this->_cachePrefix = substr(md5($baseUnique),0,8).'.';
263:
264: // Set a new Memcache object and connect to the Memcache server
265: $this->_memcache = new Memcache();
266: if (!$this->_memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, array($this, 'failureCallback'))) {
267: throw new PHPExcel_Exception('Could not connect to MemCache server at '.$memcacheServer.':'.$memcachePort);
268: }
269: $this->_cacheTime = $cacheTime;
270:
271: parent::__construct($parent);
272: }
273: } // function __construct()
274:
275:
276: /**
277: * Memcache error handler
278: *
279: * @param string $host Memcache server
280: * @param integer $port Memcache port
281: * @throws PHPExcel_Exception
282: */
283: public function failureCallback($host, $port) {
284: throw new PHPExcel_Exception('memcache '.$host.':'.$port.' failed');
285: }
286:
287:
288: /**
289: * Destroy this cell collection
290: */
291: public function __destruct() {
292: $cacheList = $this->getCellList();
293: foreach($cacheList as $cellID) {
294: $this->_memcache->delete($this->_cachePrefix.$cellID.'.cache');
295: }
296: } // function __destruct()
297:
298: /**
299: * Identify whether the caching method is currently available
300: * Some methods are dependent on the availability of certain extensions being enabled in the PHP build
301: *
302: * @return boolean
303: */
304: public static function cacheMethodIsAvailable() {
305: if (!function_exists('memcache_add')) {
306: return false;
307: }
308:
309: return true;
310: }
311:
312: }
313: