## Apache Zeta Components Manual :: File Source for stack.php

## Source for file stack.php

Documentation is available at stack.php

1. <?php
2. /**
3. * File containing the ezcCacheStack class.
4. *
5. * Licensed to the Apache Software Foundation (ASF) under one
6. * or more contributor license agreements. See the NOTICE file
7. * distributed with this work for additional information
8. * regarding copyright ownership. The ASF licenses this file
9. * to you under the Apache License, Version 2.0 (the
10. * "License"); you may not use this file except in compliance
11. * with the License. You may obtain a copy of the License at
12. *
13. * http://www.apache.org/licenses/LICENSE-2.0
14. *
15. * Unless required by applicable law or agreed to in writing,
16. * software distributed under the License is distributed on an
17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18. * KIND, either express or implied. See the License for the
19. * specific language governing permissions and limitations
20. * under the License.
21. *
22. * @package Cache
23. * @version //autogentag//
24. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
25. * @filesource
26. */
28. /**
29. * Hierarchical caching class using multiple storages.
30. *
31. * An instance of this class can be used to achieve so called "hierarchical
32. * caching". A cache stack consists of an arbitrary number of cache storages,
33. * being sorted from top to bottom. Usually this order reflects the speed of
34. * access for the caches: The fastest cache is at the top, the slowest at the
35. * bottom. Whenever data is stored in the stack, it is stored in all contained
36. * storages. When data is to be restored, the stack will restore it from the
37. * highest storage it is found in. Data is removed from a storage, whenever the
38. * storage reached a configured number of items, using a {@link }
39. * ezcCacheStackReplacementStrategy}.
40. *
41. * To create a cache stack, multiple storages are necessary. The following
42. * examples assume that $storage1, $storage2 and $storage3 contain objects that
43. * implement the {@link ezcCacheStackableStorage} interface.
44. *
45. * The slowest cache should be at the very bottom of the stack, so that items
46. * don't need to be restored from it that frequently.
47. * <code>
48. * <?php
49. * $stack = new ezcCacheStack( 'stack_id' );
50. * $stack->pushStorage(
51. * new ezcCacheStackStorageConfiguration(
52. * 'filesystem_cache',
53. * $storage1,
54. * 1000000,
55. * .5
56. * )
57. * );
58. * ?>
59. * </code>
60. * This operations create a new cache stack and add $storage1 to its very
61. * bottom. The first parameter for {@linke ezcCacheStackStorageConfiguration}
62. * are a unique ID for the storage inside the stack, which should never change.
63. * The second parameter is the storage object itself. Parameter 3 is the
64. * maximum number of items the storage might contain. As soon as this limit ist
65. * reached, 500000 items will be purged from the cache. The latter number is
66. * defined through the fourth parameter, indicating the fraction of the stored
67. * item number, that is to be freed. For freeing, first already outdated items
68. * will be purged. If this does not last, more items will be freed using the
69. * {@link ezcCacheStackReplacementStrategy} used by the stack.
70. *
71. * The following code adds the other 2 storages to the stack, where $storage2
72. * is a memory storage {@link ezcCacheStackMemoryStorage} and $storage3 is a
73. * custom storage implementation, that stores objects in the current requests
74. * memory.
75. * <code>
76. * <?php
77. * $stack->pushStorage(
78. * new ezcCacheStackStorageConfiguration(
79. * 'apc_storage',
80. * $storage2,
81. * 10000,
82. * .8
83. * )
84. * );
85. * $stack->pushStorage(
86. * new ezcCacheStackStorageConfiguration(
87. * 'custom_storage',
88. * $storage3,
89. * 50,
90. * .3
91. * )
92. * );
93. * ?>
94. * </code>
95. * The second level of the cache build by $storage2. This storage will contain
96. * 10000 items maximum and when it runs full, 8000 items will be deleted from
97. * it. The top level of the cache is the custom cache storage, that will only
98. * contain 50 objects in the currently processed request. If this storage ever
99. * runs full, which should not happen within a request, 15 items will be
100. * removed from it.
101. *
102. * Since the top most storage in this example does not implement {@link }
103. * ezcCacheStackMetaDataStorage}, another storage must be defined to be used
104. * for storing meta data about the stack:
105. * <code>
106. * <?php
107. * $stack->options->metaStorage = $storage2;
108. * $stack->options->replacementStrategy = 'ezcCacheStackLfuReplacementStrategy';
109. * ?>
110. * </code>
111. * $storage2 is defined to store the meta information for the stack. In
112. * addition, a different replacement strategy than the default {@link }
113. * ezcCacheStackLruReplacementStrategy} replacement strategy is defined. LFU
114. * removes least frequently used items, while LRU deletes least recently used
115. * items from a storage, if it runs full.
116. *
117. * Using the $bubbleUpOnRestore option you can determine, that data which is
118. * restored from a lower storage will be automatically stored in all higher
119. * storages again. The problem with this is, that only the attributes that are
120. * used for restoring will be assigned to the item in higher storages. In
121. * addition, the items will be stored with a fresh TTL. Therefore, this
122. * behavior is not recommended.
123. *
124. * If you want to use a cache stack in combination with {@ezcCacheManager}, you
125. * will most likely want to use {@link ezcCacheStackConfigurator}. This allows
126. * you to let the stack be configured on the fly, when it is used for the first
127. * time in a request.
128. *
129. * Beware, that whenever you change the structure of your stack or the
130. * replacement strategy between 2 requests, you should always perform a
131. * complete {@link ezcCacheStack::reset()} on it. Otherwise, your cache data
132. * might be seriously broken which will result in undefined behaviour of the
133. * stack. Changing the replacement strategy will most possibly result in an
134. * {@link ezcCacheInvalidMetaDataException}.
135. *
136. * @property ezcCacheStackOptions $options
137. * Options for the cache stack.
138. * @property string $location
139. * Location of this stack. Unused.
140. * @mainclass
141. * @package Cache
142. * @version //autogentag//
143. */
144. class ezcCacheStack extends ezcCacheStorage
145. {
146. /**
147. * Stack of storages.
148. *
149. * @var array(int=>ezcCacheStackableStorage)
150. */
151. private $storageStack = array();
153. /**
154. * Mapping if IDs to storages.
155. *
156. * Mainly used to validate an ID is not taken twice and a storage is not
157. * added twice.
158. *
159. * @var array(string=>ezcCacheStackableStorage)
160. */
161. private $storageIdMap = array();
163. /**
164. * Creates a new cache stack.
165. *
166. * Usually you will want to use the {@link ezcCacheManager} to take care of
167. * your caches. The $options ({@link ezcCacheStackOptions}) stored in the
168. * manager and given here can contain a class reference to an
169. * implementation of {@link ezcCacheStackConfigurator} that will be used to
170. * initially configure the stack instance directly after construction.
171. *
172. * To perform manual configuration of the stack the {@link }
173. * ezcCacheStack::pushStorage()} and {@link ezcCacheStack::popStorage()}
174. * methods can be used.
175. *
176. * The location can be a free form string that identifies the stack
177. * uniquely in the {@link ezcCacheManager}. It is currently not used
178. * internally in the stack.
179. *
180. * @param string $location
181. * @param ezcCacheStackOptions $options
182. */
183. public function __construct ( $location, ezcCacheStackOptions $options = null )
184. {
185. if ( $options === null )
186. {
187. $options = new ezcCacheStackOptions ();
188. }
190. $this-> properties ['location'] = $location;
191. $this-> properties ['options'] = $options;
193. if ( $options->configurator !== null )
194. {
195. // Configure this instance
196. call_user_func (
197. array( $options->configurator, 'configure' ),
198. $this
199. );
200. }
201. }
203. /**
204. * Stores data in the cache stack.
205. *
206. * This method will store the given data across the complete stack. It can
207. * afterwards be restored using the {@link ezcCacheStack::restore()} method
208. * and be deleted through the {@link ezcCacheStack::delete()} method.
209. *
210. * The data that can be stored in the cache depends on the configured
211. * storages. Usually it is save to store arrays and scalars. However, some
212. * caches support objects or do not even support arrays. You need to make
213. * sure that the inner caches of the stack *all* support the data you want
214. * to store!
215. *
216. * The $attributes array is optional and can be used to describe the stack
217. * further.
218. *
219. * @param string $id
220. * @param mixed $data
221. * @param array $attributes
222. */
223. public function store ( $id, $data, $attributes = array() )
224. {
225. $metaStorage = $this->getMetaDataStorage();
226. $metaStorage->lock();
228. $metaData = $this->getMetaData( $metaStorage );
230. foreach( $this->storageStack as $storageConf )
231. {
232. call_user_func (
233. array(
234. $this-> properties ['options']->replacementStrategy,
235. 'store'
236. ),
237. $storageConf,
238. $metaData,
239. $id,
240. $data,
241. $attributes
242. );
243. }
245. $metaStorage->storeMetaData( $metaData );
246. $metaStorage->unlock();
247. }
249. /**
250. * Returns the meta data to use.
251. *
252. * Returns the meta data to use with the configured {@link }
253. * ezcCacheStackStackReplacementStrategy}.
254. *
255. * @param ezcCacheStackMetaDataStorage $metaStorage
256. * @return ezcCacheMetaData
257. */
258. private function getMetaData( ezcCacheStackMetaDataStorage $metaStorage )
259. {
260. $metaData = $metaStorage->restoreMetaData();
262. if ( $metaData === null )
263. {
264. $metaData = call_user_func (
265. array(
266. $this-> properties ['options']->replacementStrategy,
267. 'createMetaData'
268. )
269. );
270. }
272. return $metaData;
273. }
275. /**
276. * Restores an item from the stack.
277. *
278. * This method tries to restore an item from the cache stack and returns
279. * the found data, if any. If no data is found, boolean false is returned.
280. * Given the ID of an object will restore exactly the desired object. If
281. * additional $attributes are given, this may speed up the restore process
282. * for some caches. If only attributes are given, the $search parameter
283. * makes sense, since it will instruct the inner cach storages to search
284. * their content for the given attribute combination and will restore the
285. * first item found. However, this process is much slower then restoring
286. * with an ID and therefore is not recommended.
287. *
288. * @param string $id
289. * @param array $attributes
290. * @param bool $search
291. * @return mixed The restored data or false on failure.
292. */
293. public function restore ( $id, $attributes = array(), $search = false )
294. {
295. $metaStorage = $this->getMetaDataStorage();
296. $metaStorage->lock();
298. $metaData = $this->getMetaData( $metaStorage );
300. $item = false;
301. foreach ( $this->storageStack as $storageConf )
302. {
303. $item = call_user_func (
304. array(
305. $this-> properties ['options']->replacementStrategy,
306. 'restore'
307. ),
308. $storageConf,
309. $metaData,
310. $id,
311. $attributes,
312. $search
313. );
314. if ( $item !== false )
315. {
316. if ( $this-> properties ['options']->bubbleUpOnRestore )
317. {
318. $this->bubbleUp( $id, $attributes, $item, $storageConf, $metaData );
319. }
320. // Found, so end.
321. break;
322. }
323. }
325. $metaStorage->storeMetaData( $metaData );
326. $metaStorage->unlock();
328. return $item;
329. }
331. /**
332. * Bubbles a restored $item up to all storages above $foundStorageConf.
333. *
334. * @param string $id
335. * @param array $attributes
336. * @param mixed $item
337. * @param ezcCacheStackStorageConfiguration $foundStorageConf
338. * @param ezcCacheStackMetaData $metaData
339. */
340. private function bubbleUp( $id, array $attributes, $item, ezcCacheStackStorageConfiguration $foundStorageConf, ezcCacheStackMetaData $metaData )
341. {
342. foreach( $this->storageStack as $storageConf )
343. {
344. if ( $storageConf === $foundStorageConf )
345. {
346. // This was the storage where we restored
347. break;
348. }
349. call_user_func (
350. array(
351. $this-> properties ['options']->replacementStrategy,
352. 'store'
353. ),
354. $storageConf,
355. $metaData,
356. $id,
357. $item,
358. $attributes
359. );
360. }
361. }
363. /**
364. * Deletes an item from the stack.
365. *
366. * This method deletes an item from the cache stack. The item will
367. * afterwards no more be stored in any of the inner cache storages. Giving
368. * the ID of the cache item will delete exactly 1 desired item. Giving an
369. * attribute array, describing the desired item in more detail, can speed
370. * up the deletion in some caches.
371. *
372. * Giving null as the ID and just an attribibute array, with the $search
373. * attribute in addition, will delete *all* items that comply to the
374. * attributes from all storages. This might be much slower than just
375. * deleting a single item.
376. *
377. * Deleting items from a cache stack is not recommended at all. Instead,
378. * items should expire on their own or be overwritten with more actual
379. * data.
380. *
381. * The method returns an array containing all deleted item IDs.
382. *
383. * @param string $id
384. * @param array $attributes
385. * @param bool $search
386. * @return array(string)
387. */
388. public function delete ( $id = null, $attributes = array(), $search = false )
389. {
390. $metaStorage = $this->getMetaDataStorage();
391. $metaStorage->lock();
393. $metaData = $metaStorage->restoreMetaData();
395. $deletedIds = array();
396. foreach ( $this->storageStack as $storageConf )
397. {
398. $deletedIds = array_merge (
399. $deletedIds,
400. call_user_func (
401. array(
402. $this-> properties ['options']->replacementStrategy,
403. 'delete'
404. ),
405. $storageConf,
406. $metaData,
407. $id,
408. $attributes,
409. $search
410. )
411. );
412. }
414. $metaStorage->storeMetaData( $metaData );
415. $metaStorage->unlock();
417. return array_unique ( $deletedIds );
418. }
420. /**
421. * Returns the meta data storage to be used.
422. *
423. * Determines the meta data storage to be used by the stack and returns it.
424. *
425. * @return ezcCacheStackMetaData
426. */
427. private function getMetaDataStorage()
428. {
429. $metaStorage = $this->options->metaStorage;
430. if ( $metaStorage === null )
431. {
432. $metaStorage = reset ( $this->storageStack )->storage;
433. if ( !( $metaStorage instanceof ezcCacheStackMetaDataStorage ) )
434. {
435. throw new ezcBaseValueException (
436. 'metaStorage',
437. $metaStorage,
438. 'ezcCacheStackMetaDataStorage',
439. 'top of storage stack'
440. );
441. }
442. }
443. return $metaStorage;
444. }
446. /**
447. * Counts how many items are stored, fulfilling certain criteria.
448. *
449. * This method counts how many data items fulfilling the given criteria are
450. * stored overall. Note: The items of all contained storages are counted
451. * independantly and summarized.
452. *
453. * @param string $id
454. * @param array $attributes
455. * @return int
456. */
457. public function countDataItems ( $id = null, $attributes = array() )
458. {
459. $sum = 0;
460. foreach( $this->storageStack as $storageConf )
461. {
462. $sum += $storageConf->storage-> countDataItems ( $id, $attributes );
463. }
464. return $sum;
465. }
467. /**
468. * Returns the remaining lifetime for the given item ID.
469. *
470. * This method returns the lifetime in seconds for the item identified by $item
471. * and optionally described by $attributes. Definining the $attributes
472. * might lead to faster results with some caches.
473. *
474. * The first internal storage that is found for the data item is chosen to
475. * detemine the lifetime. If no storage contains the item or the item is
476. * outdated in all found caches, 0 is returned.
477. *
478. * @param string $id
479. * @param array $attributes
480. * @return int
481. */
482. public function getRemainingLifetime ( $id, $attributes = array() )
483. {
484. foreach ( $this->storageStack as $storageConf )
485. {
486. $lifetime = $storageConf->storage-> getRemainingLifetime (
487. $id,
488. $attributes
489. );
490. if ( $lifetime > 0 )
491. {
492. return $lifetime;
493. }
494. }
495. return 0;
496. }
498. /**
499. * Add a storage to the top of the stack.
500. *
501. * This method is used to add a new storage to the top of the cache. The
502. * $storageConf of type {@link ezcCacheStackStorageConfiguration} consists
503. * of the actual {@link ezcCacheStackableStorage} and other information.
504. *
505. * Most importantly, the configuration object contains an ID, which must be
506. * unique within the whole storage. The itemLimit setting determines how
507. * many items might be stored in the storage at all. freeRate determines
508. * which fraction of itemLimit will be freed by the {@link }
509. * ezcCacheStackStackReplacementStrategy} of the stack, when the storage
510. * runs full.
511. *
512. * @param ezcCacheStackStorageConfiguration $storageConf
513. */
514. public function pushStorage ( ezcCacheStackStorageConfiguration $storageConf )
515. {
516. if ( isset( $this->storageIdMap[$storageConf->id] ) )
517. {
518. throw new ezcCacheStackIdAlreadyUsedException (
519. $storageConf->id
520. );
521. }
522. if ( in_array ( $storageConf->storage, $this->storageIdMap, true ) )
523. {
524. throw new ezcCacheStackStorageUsedTwiceException (
525. $storageConf->storage
526. );
527. }
528. array_unshift ( $this->storageStack, $storageConf );
529. $this->storageIdMap[$storageConf->id] = $storageConf->storage;
530. }
532. /**
533. * Removes a storage from the top of the stack.
534. *
535. * This method can be used to remove the top most {@link }
536. * ezcCacheStackableStorage} from the stack. This is commonly done to
537. * remove caches or to insert new ones into lower positions. In both cases,
538. * it is recommended to {@link ezcCacheStack::reset()} the whole cache
539. * afterwards to avoid any kind of inconsistency.
540. *
541. * @return ezcCacheStackStorageConfiguration
542. *
543. * @throws ezcCacheStackUnderflowException
544. * if called on an empty stack.
545. */
546. public function popStorage ()
547. {
548. if ( count ( $this->storageStack ) === 0 )
549. {
550. throw new ezcCacheStackUnderflowException ();
551. }
552. $storageConf = array_shift ( $this->storageStack );
553. unset( $this->storageIdMap[$storageConf->id] );
554. return $storageConf;
555. }
557. /**
558. * Returns the number of storages on the stack.
559. *
560. * Returns the number of storages currently on the stack.
561. *
562. * @return int
563. */
564. public function countStorages ()
565. {
566. return count ( $this->storageStack );
567. }
569. /**
570. * Returns all stacked storages.
571. *
572. * This method returns the whole stack of {@link ezcCacheStackableStorage}
573. * as an array. This maybe useful to adjust options of the storages after
574. * they have been added to the stack. However, it is not recommended to
575. * perform any drastical changes to the configurations. Performing manual
576. * stores, restores and deletes on the storages is *highly discouraged*,
577. * since it may lead to irrepairable inconsistencies of the stack.
578. *
579. * @return array(ezcCacheStackableStorage)
580. */
581. public function getStorages ()
582. {
583. return $this->storageStack;
584. }
586. /**
587. * Resets the complete stack.
588. *
589. * This method is used to reset the complete stack of storages. It will
590. * reset all storages, using {@link ezcCacheStackableStorage::reset()}, and
591. * therefore purge the complete content of the stack. In addition, it will
592. * kill the complete meta data.
593. *
594. * The stack is in a consistent, but empty, state afterwards.
595. *
596. * @return void
597. */
598. public function reset ()
599. {
600. foreach ( $this->storageStack as $storageConf )
601. {
602. $storageConf->storage-> reset ();
603. }
604. }
606. /**
607. * Validates the $location parameter of the constructor.
608. *
609. * Returns true, since $location is not necessary for this storage.
610. *
611. * @return bool
612. */
613. protected function validateLocation ()
614. {
615. // Does not utilize $location
616. return true;
617. }
619. /**
620. * Sets the options for this stack instance.
621. *
622. * Overwrites the parent implementation to only allow instances of {@link }
623. * ezcCacheStackOptions}.
624. *
625. * @param ezcCacheStackOptions $options
626. *
627. * @apichange Use $stack->options instead.
628. */
629. public function setOptions ( $options )
630. {
631. // Overloading
632. $this->options = $options;
633. }
635. /**
636. * Property write access.
637. *
638. * @param string $propertyName Name of the property.
639. * @param mixed $propertyValue The value for the property.
640. *
641. * @throws ezcBaseValueException
642. * If the value for the property options is not an instance of
643. * ezcCacheStackOptions.
644. * @ignore
645. */
646. public function __set( $propertyName, $propertyValue )
647. {
648. switch ( $propertyName )
649. {
650. case 'options':
651. if ( !( $propertyValue instanceof ezcCacheStackOptions ) )
652. {
653. throw new ezcBaseValueException ( $propertyName, $propertyValue, 'ezcCacheStackOptions' );
654. }
655. break;
656. default:
657. parent::__set( $propertyName, $propertyValue );
658. return;
659. }
660. $this-> properties [$propertyName] = $propertyValue;
661. }
662. }
664. ?>

Documentation generated by phpDocumentor 1.4.3