View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFPROP:Proposal Zone Template}

{zone-data:component-name}
Zend\Cache
{zone-data}

{zone-data:proposer-list}
[Marc Bennewitz|http://framework.zend.com/wiki/display/~mabe]
{zone-data}

{zone-data:liaison}
TBD
{zone-data}

{zone-data:revision}
1.0 - 7 March 2010: Initial Draft.
2.0 - 17 Jun 2010: finish proposal
{zone-data}

{zone-data:overview}
The current Zend_Cache component is very inflexible with it's Frontend/Backend structure. For example it's not possible to merge different frontends together (e.g. MasterFile and Page cache). Furthermore, few advantages of some cache storage systems are not derived by Zend_Cache. E.g. some storage systems can handle different data-types but Zend_Cache only permit strings.

{tip:title=Code available}
Current working implementation is hosted on a branch of my Github account: [Zend\Cache|http://github.com/marc-mabe/zf2/tree/cache/library/Zend/Cache].
{tip}

{info:title=Backwards compatibility}
Because the old Zend_Cache object has a complete different structure, keeping backwards compatibility is impossible.
{info}

The most of the open issues are related to this structure and should be solved with ZF 2.0 by this proposal.
{jiraissues:columns=key;type;priority;summary;created;updated|url=http://framework.zend.com/issues/sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml?jqlQuery=project+%3D+ZF+AND+fixVersion+%3D+%22Next+Major+Release%22+AND+component+%3D+Zend_Cache+AND+status+in+%28Open%2C+%22In+Progress%22%2C+Reopened%2C+Postponed%29&tempMax=1000}
{zone-data}

{zone-data:references}
* [Zend Framework 2.0|http://framework.zend.com/wiki/display/ZFDEV2]
* [Zend_Cache|http://framework.zend.com/manual/en/zend.cache.html]
* [Zend_Cache Refactoring|http://framework.zend.com/wiki/display/ZFPROP/Zend_Cache+refactoring+-+Marc+Bennewitz]
* [Zend_Cache_Frontend_Page Etag support|http://framework.zend.com/wiki/display/ZFPROP/Zend_Cache_Frontend_Page+Etag+support+-+Kevin+Schroeder]
{zone-data}

{zone-data:requirements}
* This component *will* break bc 100% !!!!!
* This components *will* read & write data from cache storage engines using only one adapter interface.
* Adapters *will not* implement special functionalities directly to the storage engines (like serialization) to keep performance
but implements a plugin structure to dynamically add/configure/re-order such functionalities.
* Plugins *will not* store any data directly to a storage engine - a real storage adapter have to be used.
* This component will add some special cache patterns using a different API (like PageCache, CaptureCache).
{zone-data}

{zone-data:dependencies}
*Global:*
* Zend\Loader\PluginLoader
* Zend\Exception

*Zend\Serializer*
* Storage\Plugin\Serialize

*Zend\Filter*
* Storage\Plugin\Filter
* Storage\Plugin\KeyFilter
{zone-data}

{zone-data:operation}
*Storage Adapter:*
The storage adapters are classes of wrappers of the existing storage engines usable for caching (Memcached, Database, Filesystem ...).
They all implement an interface to get a consistent API but only reflect supported facilities of the used storage engine.

*Storage Plugins:*
Precisely because the storage adapters supports different facilities you can add facilities belated with the storage plugins.
(e.g. tag support, automatic serialize). The storage plugins have the same API as the storage adapters and therefor implement
an interface extending the adapters interface. Where ever a storage adapter is required you can set an instance of a storage plugin and you can merge all storage plugins as you like. The storage plugins self don't store any data - they need a real storage adapter as storage engine.

A special storage plugin is the base storage class (Zend\Cache\Storage). It implements additional methods for simpler handling lists of
plugins within it's own instance.

*Special Cache Patterns:*
Some special cache patterns shouldn't use the standard cache API. They are: Output, Page, Function, Callback, Function, Class and Capture.
These caches only implement a very simple pattern interface which only defines how options have to be set.

In case of the capture pattern it no longer needs the "Static" storage adapter (or old named backend) because the storage is very specific and only usable with the capture pattern and vise versa. Therefore the capture pattern will implement reading/writing files within it's self.
{zone-data}

{zone-data:milestones}
* Milestone 1: \[DONE\] Finish proposal
* Milestone 2: \[DONE\] Working prototype
* Milestone 3: Unit tests exist finished and component is working
* Milestone 4: Initial documentation exists
* Milestone 5: Changed related components
* Milestone 6: Moved to core.
{zone-data}

{zone-data:class-list}
*Storage Adapter:*
* namespace *Zend\Cache\Storage\Adapter*
* interface *\Zend\Cache\Storage\Adapter*
* abstract *AbstractAdapter* implements Storage\Adapter
* class *Memory* extends AbstractAdapter
* class *Memcached* extends AbstractAdapter
* class *Apc* extends AbstractAdapter
* class *WinCache* extends AbstractAdapter
* class *Xcache* extends AbstractAdapter
* class *ZendPlatform* extends AbstractAdapter
* class *Database* extends AbstractAdapter
* class *Filesystem* extends AbstractAdapter
* class *SystemVShm* extends AbstractZendServer
* abstract *AbstractZendServer* extends AbstractAdapter
* class *ZendServerShm* extends AbstractZendServer
* class *ZendServerDisk* extends AbstractZendServer

*Storage Plugins:*
* namespace *Zend\Cache\Storage\Plugin*
* interface *\Zend\Cache\Storgae\Plugin* extends \Zend\Cache\Storgae\Adapter
* abstract *AbstractPlugin* implements \Zend\Cache\Storgae\Plugin
* class *ClearByFactor* extends AbstractPlugin
* class *ExceptionHandler* extends AbstractPlugin (Callback on exception + logging)
* class *IgnoreUserAbort* extends AbstractPlugin
* class *KeyFilter* extends AbstractPlugin
* class *Levels* extends AbstractPlugin
* class *MasterFile* extends AbstractPlugin
* class *OptimizeByFactor* extends AbstractPlugin
* class *Profiler* extends AbstractPlugin
* class *Reluctant* extends AbstractPlugin
* class *Serialize* extends AbstractPlugin
* class *Tagging* extends AbstractPlugin
* class *Timer* extends AbstractPlugin
* class *ValueFilter* extends AbstractPlugin
* class *WriteBuffer* extends AbstractPlugin
* class *WriteControl* extends AbstractPlugin

* namespace *\Zend\Cache*
* class *Storage* extends Storage\Plugin\AbstractPlugin
* class *StorageFactory*

*Special Cache Patterns:*
* namesapce *Zend\Cache\Pattern*
* abstract *AbstractPattern* implements \Zend\Cache\Pattern
* class *CallbackCache* extends AbstractPattern (Function and Callback calls)
* class *ClassCache* extends AbstractPattern (only static method calls)
* class *ObjectCache* extends AbstractPattern (only object method calls)
* class *OutputCache* extends AbstractPattern
* class *PageCache* extends AbstractPattern
* class *CaptureCache* extends AbstractPattern

* namespace *\Zend\Cache*
* interface *Pattern*
* class *PatternFactory*

*Manager:*
* namesapce *Zend\Cache*
* class *Manager*

*Profiler*
* namesapce *Zend\Cache\Profiler*
* class *Profiler*
* class *Profile*

*Exceptions:*
* namesapce *Zend\Cache\Exception*

* class *RuntimeException* extends \RuntimeException
* class *LogicException* extends \LogicException
* class *InvalidArgumentException* extends \InvalidArgumentException
* class *UnexpectedValueException* extends \UnexpectedValueException
* class *ExtensionNotLoadedException* extends \RuntimeException
* class *BadMethodCallException* extends \BadMethodCallException
* class *ItemNotFoundException* extends RuntimeException
* class *MissingKeyException* extends RuntimeException
* class *MissingDependencyException* extends RuntimeException
{zone-data}

{zone-data:use-cases}
{composition-setup}
{deck:id=Create a storage}

{card:label=Instantiate the base storage}
1. Using the base storage class
{code}
$cache = new Zend\Cache\Storage(array(
'storage' => array(
'adapter' => 'Filesystem',
'options' => array(/* adapter options */),
),
'plugins' => array('WriteControl', 'IgnoreUserAbort'),
'options' => array(
'removeOnFailure' => true,
'exitOnAbort' => true,
'ttl' => 100
)
));
// Now $cache has the structure: Storage -> WriteControl -> IgnoreUserAbort -> Filesystem
{code}
{card}

{card:label=using StorageFactory::factory}
{code}
$cache = \Zend\Cache\StorageFactory::factory(array(
'storage' => array(
'adapter' => 'Filesystem',
'options' => array(/* adapter options */),
),
'plugins' => array('WriteControl', 'IgnoreUserAbort'),
'options' => array(
'removeOnFailure' => true,
'exitOnAbort' => true,
'ttl' => 100
)
));
// Now $cache has the structure: WriteControl -> IgnoreUserAbort -> Filesystem
{code}
{card}

{card:label=using StorageFactory::adapterFactory}
{code}
$cache = \Zend\Cache\StorageFactory::adapterFactory(
'Filesystem',
array(
'ttl' => 100
)
);
// Now $cache is directly an instance of \Zend\Cache\Storage\Adapter\Filesystem
{code}
{card}

{card:label=using StorageFactory::pluginFactory}
{code}
$cache = \Zend\Cache\StorageFactory::pluginFactory(
'IgnoreUserAbort',
array(
// you need an adapter to instantiate a plugin
'adapter' => \Zend\Cache\StorageFactory::adapterFactory('Filesystem'),
'exitOnAbort' => true,
)
);
// Now $cache has the structure: IgnoreUserAbort -> Filesystem
{code}
{card}

{deck}

{deck:id=Basic usage}

{card:label=Read/Write}
{code}
// set an item (replace or add)
$cache->setItem($value, $key);

// replace an existing item
$cache->replaceItem($value, $key);

// add a new item
$cache->addItem($value, $key);

// get item value
$cache->getItem($key);

// check existence
$cache->hasItem($key);

// get additional item information (like mtime, atime, ctime, ...)
$cache->getMetadata($key);

// remove an item
$cache->removeItem($key);
{code}
{card}

{card:label=Increment/Decrement}
{code}
$cache->setItem(10, 'counter');

// increment
$cache->incrementItem(5, 'counter');
$cache->getItem('counter'); // -> 15

// decrement
$cache->decrementItem(9, 'counter');
$cache->getItem('counter'); // -> 6
{code}
{card}

{card:label=CAS (Check And Set)}
{code}
// get value and CAS-token
$token = null;
$cache->getItem('key', array('token' => &$token)); // !! set $token as reference !!
// set a new value using CAS
$cache->checkAndSetItem($token, 'newValue', 'key');
{code}
{card}

{card:label=Multiple API}
{code}
// the most simple methods have a additional multiple brother
$cache->setItems(array('key1' => 'value1', 'key2' => 'value2'));
$cache->replaceItems(array('key1' => 'value1', 'key2' => 'value2'));
$cache->addItems(array('key1' => 'value1', 'key2' => 'value2'));

$cache->incrementItems(array('key1' => 'value1', 'key2' => 'value2'));
$cache->decrementItems(array('key1' => 'value1', 'key2' => 'value2'));

$cache->getItems(array('key1', 'key2'));
$cache->hasItems(array('key1', 'key2'));
$cache->getMetadatas(array('key1', 'key2'));

$cache->removeItems(array('key1', 'key2'));
{code}
{card}

{card:label=Non-Blocking API}
{code}
// some adapters (like memcached) allows to load cached items in background
// this can save some time on reading cached items
// (If it's not supported it will be emulated)

$keys = array('key1', 'key2', 'key3', 'key4');

// get key and value pairs
$opts = array('select' => array('key', 'value'));
$cache->getDelayed($keys, $opts);
// do something
while (($item = $cache->fetch()) !== false) {
echo $item['key'] . ' : ' . $item['value'] . PHP_EOL;
}

// get key and mtime pairs
$opts = array('select' => array('key', 'mtime'));
$cache->getDelayed($keys, $opts);
// do something
while (($item = $cache->fetch()) !== false) {
echo $item['key'] . ': ' . $item['mtime'] . PHP_EOL;
}
{code}
{card}

{card:label=Clear storage}
{code}
use Zend\Cache\Storage as CacheStorage;

// clear all
$cache->clear(CacheStorage::MATCH_ALL);

// clear expired or active items
$cache->clear(CacheStorage::MATCH_EXPIRED);
$cache->clear(CacheStorage::MATCH_ACTIVE);

// clear *active* items where *one tag* matches (DEFAULT)
$tags = array('tag1', 'tag2', 'tag3');
$cache->clear(CacheStorage::MATCH_ACTIVE | CacheStorage::MATCH_TAGS_OR, array('tags' => $tags));

// clear *all* items where *all tags* matches
$tags = array('tag1', 'tag2', 'tag3');
$cache->clear(CacheStorage::MATCH_ALL | CacheStorage::MATCH_TAGS_AND, array('tags' => $tags));

// clear *all* items where min. *one tag doesn't* match
$tags = array('tag1', 'tag2', 'tag3');
$cache->clear(CacheStorage::MATCH_ALL | CacheStorage::MATCH_TAGS_OR_NOT, array('tags' => $tags));

// NOTE: if no tags are present tags comparing will be completely ignored
{code}
{card}

{card:label=Capacity information}
{code}
// you can get capacity information by the following simple call:
$capacity = $cache->getCapacity(); // array('total' => <float>, 'free' => <float>)
{code}
{card}

{card:label=Optimize storage}
{code}
// Some storage adapters needs an optimization one a while
$sqliteCache->optimize(); // -> sends query "VACUUM"
$memoryCache->optimize(); // -> removes empty namespaces within data array
$filesystemCache->optimize(); // -> removes empty sub-directories
{code}
{card}

{deck}

{deck:id=Pattern}

{card:label=PageCache + ETag}
{code}
<?php

require_once '/path/to/Zend/Loader/StandardAutoloader.php';
$autoloader = new \Zend\Loader\StandardAutoloader();
$autoloader->register();

$pageCache = \Zend\Cache\PatternFactory::factory('page', array(
// set data cache storage
'storage' => array(
'adapter' => 'Filesystem',
'plugins' => array('Serialize')
),

// enable caching of http headers
'http_headers' => true,

// enable ETag handling (only if http_headers is enabled)
'http_etag' => true,

// autogenerate ETag with:
'http_etag_size' => true, // size of content
'http_etag_mtime' => true, // last modification time
'http_etag_hash' => true, // hash of content
'http_etag_hash_algo' => 'crc32', // use crc32 hashing
));

// $pageCache->getStorage()->clear(\Zend\Cache\Storage::MATCH_ALL);

$pageCache->start();


// If you would like to change HTTP-Response-Code
// you have to tell it
// @see http://bugs.php.net/bug.php?id=52555
header('Status: 200', true, 200);
// or
$pageCache->setHttpResponseCode(200);


// set own http headers
header('Content-Type: text/plain');

// create an own ETag (disables autogenerating ETag by Zend Cache)
header('ETag: "myOwnETag"');

// display page
echo 'page created at ' . date('r');
{code}

{panel:title=First reqest}
(Request-Line) GET /index.php HTTP/1.1
Host 192.168.178.30
User-Agent Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Connection keep-alive
Cookie ZDEDebuggerPresent=php,phtml,php3

(Status-Line) HTTP/1.1 200 OK
Date Fri, 10 Dec 2010 06:20:04 GMT
Server Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/1.0.0
Status 200
Etag "myOwnETag"
Content-Length 47
Keep-Alive timeout=5, max=100
Connection Keep-Alive
Content-Type text/plain

page created at Fri, 10 Dec 2010 07:20:04 +0100
{panel}

{panel:title=On second request server sends 304 without body}
(Request-Line) GET /index.php HTTP/1.1
Host 192.168.178.30
User-Agent Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Connection keep-alive
Cookie ZDEDebuggerPresent=php,phtml,php3
If-None-Match "myOwnETag"

(Status-Line) HTTP/1.1 304 Not Modified
Date Fri, 10 Dec 2010 06:20:44 GMT
Server Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/1.0.0
Connection Keep-Alive
Keep-Alive timeout=5, max=100
{panel}

{card}

{deck}

{zone-data}

{zone-data:skeletons}
{composition-setup}
please look at [http://github.com/marc-mabe/zf2/tree/cache/library/Zend/Cache]
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>