2
0
Просмотр исходного кода

[REVIEW] Promoted Zend_Cache_Manager and Zend_Cache_Backend_Static to trunk

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@19830 44c647ce-9c0f-0410-b52a-842ac1e357ba
matthew 16 лет назад
Родитель
Сommit
ca1d319e9e

+ 250 - 0
library/Zend/Cache/Backend/BlackHole.php

@@ -0,0 +1,250 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cache
+ * @subpackage Zend_Cache_Backend
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: BlackHole.php 17867 2009-08-28 09:42:11Z yoshida@zend.co.jp $
+ */
+
+/**
+ * @see Zend_Cache_Backend_Interface
+ */
+require_once 'Zend/Cache/Backend/ExtendedInterface.php';
+
+/**
+ * @see Zend_Cache_Backend
+ */
+require_once 'Zend/Cache/Backend.php';
+
+/**
+ * @package    Zend_Cache
+ * @subpackage Zend_Cache_Backend
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cache_Backend_BlackHole 
+    extends Zend_Cache_Backend 
+    implements Zend_Cache_Backend_ExtendedInterface
+{
+    /**
+     * Test if a cache is available for the given id and (if yes) return it (false else)
+     *
+     * @param  string $id cache id
+     * @param  boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
+     * @return string|false cached datas
+     */
+    public function load($id, $doNotTestCacheValidity = false)
+    {
+        return false;
+    }
+
+    /**
+     * Test if a cache is available or not (for the given id)
+     *
+     * @param  string $id cache id
+     * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+     */
+    public function test($id)
+    {
+        return false;
+    }
+
+    /**
+     * Save some string datas into a cache record
+     *
+     * Note : $data is always "string" (serialization is done by the
+     * core not by the backend)
+     *
+     * @param  string $data             Datas to cache
+     * @param  string $id               Cache id
+     * @param  array  $tags             Array of strings, the cache record will be tagged by each string entry
+     * @param  int    $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+     * @return boolean true if no problem
+     */
+    public function save($data, $id, $tags = array(), $specificLifetime = false)
+    {
+        return true;
+    }
+
+    /**
+     * Remove a cache record
+     *
+     * @param  string $id cache id
+     * @return boolean true if no problem
+     */
+    public function remove($id)
+    {
+        return true;
+    }
+
+    /**
+     * Clean some cache records
+     *
+     * Available modes are :
+     * 'all' (default)  => remove all cache entries ($tags is not used)
+     * 'old'            => remove too old cache entries ($tags is not used)
+     * 'matchingTag'    => remove cache entries matching all given tags
+     *                     ($tags can be an array of strings or a single string)
+     * 'notMatchingTag' => remove cache entries not matching one of the given tags
+     *                     ($tags can be an array of strings or a single string)
+     * 'matchingAnyTag' => remove cache entries matching any given tags
+     *                     ($tags can be an array of strings or a single string)
+     *
+     * @param  string $mode clean mode
+     * @param  tags array $tags array of tags
+     * @return boolean true if no problem
+     */
+    public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+    {
+        return true;
+    }
+
+    /**
+     * Return an array of stored cache ids
+     *
+     * @return array array of stored cache ids (string)
+     */
+    public function getIds()
+    {
+        return array();
+    }
+
+    /**
+     * Return an array of stored tags
+     *
+     * @return array array of stored tags (string)
+     */
+    public function getTags()
+    {
+        return array();
+    }
+
+    /**
+     * Return an array of stored cache ids which match given tags
+     *
+     * In case of multiple tags, a logical AND is made between tags
+     *
+     * @param array $tags array of tags
+     * @return array array of matching cache ids (string)
+     */
+    public function getIdsMatchingTags($tags = array())
+    {
+        return array();
+    }
+
+    /**
+     * Return an array of stored cache ids which don't match given tags
+     *
+     * In case of multiple tags, a logical OR is made between tags
+     *
+     * @param array $tags array of tags
+     * @return array array of not matching cache ids (string)
+     */
+    public function getIdsNotMatchingTags($tags = array())
+    {
+        return array();
+    }
+
+    /**
+     * Return an array of stored cache ids which match any given tags
+     *
+     * In case of multiple tags, a logical AND is made between tags
+     *
+     * @param  array $tags array of tags
+     * @return array array of any matching cache ids (string)
+     */
+    public function getIdsMatchingAnyTags($tags = array())
+    {
+        return array();
+    }
+
+    /**
+     * Return the filling percentage of the backend storage
+     *
+     * @return int integer between 0 and 100
+     * @throws Zend_Cache_Exception
+     */
+    public function getFillingPercentage()
+    {
+        return 0;
+    }
+
+    /**
+     * Return an array of metadatas for the given cache id
+     *
+     * The array must include these keys :
+     * - expire : the expire timestamp
+     * - tags : a string array of tags
+     * - mtime : timestamp of last modification time
+     *
+     * @param  string $id cache id
+     * @return array array of metadatas (false if the cache id is not found)
+     */
+    public function getMetadatas($id)
+    {
+        return false;
+    }
+
+    /**
+     * Give (if possible) an extra lifetime to the given cache id
+     *
+     * @param  string $id cache id
+     * @param  int $extraLifetime
+     * @return boolean true if ok
+     */
+    public function touch($id, $extraLifetime)
+    {
+        return false;
+    }
+
+    /**
+     * Return an associative array of capabilities (booleans) of the backend
+     *
+     * The array must include these keys :
+     * - automatic_cleaning (is automating cleaning necessary)
+     * - tags (are tags supported)
+     * - expired_read (is it possible to read expired cache records
+     *                 (for doNotTestCacheValidity option for example))
+     * - priority does the backend deal with priority when saving
+     * - infinite_lifetime (is infinite lifetime can work with this backend)
+     * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+     *
+     * @return array associative of with capabilities
+     */
+    public function getCapabilities()
+    {
+        return array(
+            'automatic_cleaning' => true,
+            'tags'               => true,
+            'expired_read'       => true,
+            'priority'           => true,
+            'infinite_lifetime'  => true,
+            'get_list'           => true,
+        );
+    }
+
+    /**
+     * PUBLIC METHOD FOR UNIT TESTING ONLY !
+     *
+     * Force a cache record to expire
+     *
+     * @param string $id cache id
+     */
+    public function ___expire($id)
+    {
+    }
+}

+ 459 - 0
library/Zend/Cache/Backend/Static.php

@@ -0,0 +1,459 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cache
+ * @subpackage Zend_Cache_Backend
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: BlackHole.php 17867 2009-08-28 09:42:11Z yoshida@zend.co.jp $
+ */
+
+/**
+ * @see Zend_Cache_Backend_Interface
+ */
+require_once 'Zend/Cache/Backend/Interface.php';
+
+/**
+ * @see Zend_Cache_Backend
+ */
+require_once 'Zend/Cache/Backend.php';
+
+/**
+ * @package    Zend_Cache
+ * @subpackage Zend_Cache_Backend
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cache_Backend_Static 
+    extends Zend_Cache_Backend 
+    implements Zend_Cache_Backend_Interface
+{
+    const INNER_CACHE_NAME = 'zend_cache_backend_static_tagcache';
+
+    /**
+     * Static backend options
+     * @var array
+     */
+    protected $_options = array(
+        'public_dir'       => null,
+        'sub_dir'          => 'html',
+        'file_extension'   => '.html',
+        'index_filename'   => 'index',
+        'file_locking'     => true,
+        'cache_file_umask' => 0600,
+        'debug_header'     => false,
+        'tag_cache'        => null,
+    );
+
+    /**
+     * Cache for handling tags
+     * @var Zend_Cache_Core
+     */
+    protected $_tagCache = null;
+
+    /**
+     * Tagged items
+     * @var array
+     */
+    protected $_tagged = null;
+
+    /**
+     * Interceptor child method to handle the case where an Inner
+     * Cache object is being set since it's not supported by the
+     * standard backend interface
+     *
+     * @param  string $name
+     * @param  mixed $value
+     * @return Zend_Cache_Backend_Static
+     */
+    public function setOption($name, $value)
+    {
+        if ($name == 'tag_cache') {
+            $this->setInnerCache($value);
+        } else {
+            parent::setOption($name, $value);
+        }
+        return $this;
+    }
+
+    /**
+     * Retrieve any option via interception of the parent's statically held
+     * options including the local option for a tag cache.
+     *
+     * @param  string $name
+     * @return mixed
+     */
+    public function getOption($name)
+    {
+        if ($name == 'tag_cache') {
+            return $this->getInnerCache();
+        } else {
+            return parent::getOption($name);
+        }
+    }
+
+    /**
+     * Test if a cache is available for the given id and (if yes) return it (false else)
+     *
+     * Note : return value is always "string" (unserialization is done by the core not by the backend)
+     *
+     * @param  string  $id                     Cache id
+     * @param  boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+     * @return string|false cached datas
+     */
+    public function load($id, $doNotTestCacheValidity = false)
+    {
+        if (empty($id)) {
+            $id = $this->_detectId();
+        }
+        if (!$this->_verifyPath($id)) {
+            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+        }
+        if ($doNotTestCacheValidity) {
+            $this->_log("Zend_Cache_Backend_Static::load() : \$doNotTestCacheValidity=true is unsupported by the Static backend");
+        }
+
+        $fileName = basename($id);
+        if (empty($fileName)) {
+            $fileName = $this->_options['index_filename'];
+        }
+        $pathName = $this->_options['public_dir'] . dirname($id);
+        $file     = $pathName . '/' . $fileName . $this->_options['file_extension'];
+        if (file_exists($file)) {
+            $content = file_get_contents($file);
+            return $content;
+        }
+
+        return false;
+    }
+
+    /**
+     * Test if a cache is available or not (for the given id)
+     *
+     * @param  string $id cache id
+     * @return bool 
+     */
+    public function test($id)
+    {
+        if (!$this->_verifyPath($id)) {
+            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+        }
+
+        $fileName = basename($id);
+        if (empty($fileName)) {
+            $fileName = $this->_options['index_filename'];
+        }
+        $pathName = $this->_options['public_dir'] . dirname($id);
+        $file     = $pathName . '/' . $fileName . $this->_options['file_extension'];
+        if (file_exists($file)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Save some string datas into a cache record
+     *
+     * Note : $data is always "string" (serialization is done by the
+     * core not by the backend)
+     *
+     * @param  string $data            Datas to cache
+     * @param  string $id              Cache id
+     * @param  array $tags             Array of strings, the cache record will be tagged by each string entry
+     * @param  int   $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+     * @return boolean true if no problem
+     */
+    public function save($data, $id, $tags = array(), $specificLifetime = false)
+    {
+        clearstatcache();
+        if (is_null($id) || strlen($id) == 0) {
+            $id = $this->_detectId();
+        }
+
+        $fileName = basename($id);
+        if (empty($fileName)) {
+            $fileName = $this->_options['index_filename'];
+        }
+
+        $pathName = $this->_options['public_dir'] . dirname($id);
+        if (!file_exists($pathName)) {
+            mkdir($pathName, $this->_options['cache_file_umask'], true);
+        }
+
+        if (is_null($id) || strlen($id) == 0) {
+            $dataUnserialized = unserialize($data);
+            $data = $dataUnserialized['data'];
+        }
+
+        $file = $pathName . '/' . $fileName . $this->_options['file_extension'];
+        if ($this->_options['file_locking']) {
+            $result = file_put_contents($file, $data, LOCK_EX);
+        } else {
+            $result = file_put_contents($file, $data);
+        }
+        @chmod($file, $this->_options['cache_file_umask']);
+
+        if (count($tags) > 0) {
+            if (is_null($this->_tagged) && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
+                $this->_tagged = $tagged;
+            } elseif (is_null($this->_tagged)) {
+                $this->_tagged = array();
+            }
+            if (!isset($this->_tagged[$id])) {
+                $this->_tagged[$id] = array();
+            }
+            $this->_tagged[$id] = array_unique(array_merge($this->_tagged[$id], $tags));
+            $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+        }
+        return (bool) $result;
+    }
+
+    /**
+     * Remove a cache record
+     *
+     * @param  string $id Cache id
+     * @return boolean True if no problem
+     */
+    public function remove($id)
+    {
+        if (!$this->_verifyPath($id)) {
+            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+        }
+        $fileName = basename($id);
+        if (empty($fileName)) {
+            $fileName = $this->_options['index_filename'];
+        }
+        $pathName = $this->_options['public_dir'] . dirname($id);
+        $file     = $pathName . '/' . $fileName . $this->_options['file_extension'];
+        if (!file_exists($file)) {
+            return false;
+        }
+        return unlink($file);
+    }
+
+    /**
+     * Remove a cache record recursively for the given directory matching a
+     * REQUEST_URI based relative path (deletes the actual file matching this
+     * in addition to the matching directory)
+     *
+     * @param  string $id Cache id
+     * @return boolean True if no problem
+     */
+    public function removeRecursively($id)
+    {
+        if (!$this->_verifyPath($id)) {
+            Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+        }
+        $fileName = basename($id);
+        if (empty($fileName)) {
+            $fileName = $this->_options['index_filename'];
+        }
+        $pathName  = $this->_options['public_dir'] . dirname($id);
+        $file      = $pathName . '/' . $fileName . $this->_options['file_extension'];
+        $directory = $pathName . '/' . $fileName;
+        if (file_exists($directory)) {
+            if (!is_writable($directory)) {
+                return false;
+            }
+            foreach (new DirectoryIterator($directory) as $file) {
+                if (true === $file->isFile()) {
+                    if (false === unlink($file->getPathName())) {
+                        return false;
+                    }
+                }
+            }
+            rmdir(dirname($path));
+        }
+        if (file_exists($file)) {
+            if (!is_writable($file)) {
+                return false;
+            }
+            return unlink($file);
+        }
+        return true;
+    }
+
+    /**
+     * Clean some cache records
+     *
+     * Available modes are :
+     * Zend_Cache::CLEANING_MODE_ALL (default)    => remove all cache entries ($tags is not used)
+     * Zend_Cache::CLEANING_MODE_OLD              => remove too old cache entries ($tags is not used)
+     * Zend_Cache::CLEANING_MODE_MATCHING_TAG     => remove cache entries matching all given tags
+     *                                               ($tags can be an array of strings or a single string)
+     * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+     *                                               ($tags can be an array of strings or a single string)
+     * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+     *                                               ($tags can be an array of strings or a single string)
+     *
+     * @param  string $mode Clean mode
+     * @param  array  $tags Array of tags
+     * @return boolean true if no problem
+     */
+    public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+    {
+        $result = false;
+        switch ($mode) {
+            case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+            case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+                if (empty($tags)) {
+                    throw new Zend_Exception('Cannot use tag matching modes as no tags were defined');
+                }
+                if (is_null($this->_tagged) && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
+                    $this->_tagged = $tagged;
+                } elseif (!$this->_tagged) {
+                    return true;
+                }
+                foreach ($tags as $tag) {
+                    $urls = array_keys($this->_tagged);
+                    foreach ($urls as $url) {
+                        if (in_array($tag, $this->_tagged[$url])) {
+                            $this->remove($url);
+                            unset($this->_tagged[$url]);
+                        }
+                    }
+                }
+                $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+                $result = true;
+                break;
+            case Zend_Cache::CLEANING_MODE_ALL:
+                if (is_null($this->_tagged)) {
+                    $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME);
+                    $this->_tagged = $tagged;
+                }
+                if (is_null($this->_tagged) || empty($this->_tagged)) {
+                    return true;
+                }
+                $urls = array_keys($this->_tagged);
+                foreach ($urls as $url) {
+                    $this->remove($url);
+                    unset($this->_tagged[$url]);
+                }
+                $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+                $result = true;
+                break;
+            case Zend_Cache::CLEANING_MODE_OLD:
+                $this->_log("Zend_Cache_Backend_Static : Selected Cleaning Mode Currently Unsupported By This Backend");
+                break;
+            case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+                if (empty($tags)) {
+                    throw new Zend_Exception('Cannot use tag matching modes as no tags were defined');
+                }
+                if (is_null($this->_tagged)) {
+                    $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME);
+                    $this->_tagged = $tagged;
+                }
+                if (is_null($this->_tagged) || empty($this->_tagged)) {
+                    return true;
+                }
+                $urls = array_keys($this->_tagged);
+                foreach ($urls as $url) {
+                    $difference = array_diff($tags, $this->_tagged[$url]);
+                    if (count($tags) == count($difference)) {
+                        $this->remove($url);
+                        unset($this->_tagged[$url]);
+                    }
+                }
+                $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+                $result = true;
+                break;
+            default:
+                Zend_Cache::throwException('Invalid mode for clean() method');
+                break;
+        }
+        return $result;
+    }
+
+    /**
+     * Set an Inner Cache, used here primarily to store Tags associated
+     * with caches created by this backend. Note: If Tags are lost, the cache
+     * should be completely cleaned as the mapping of tags to caches will
+     * have been irrevocably lost.
+     *
+     * @param  Zend_Cache_Core
+     * @return void
+     */
+    public function setInnerCache(Zend_Cache_Core $cache)
+    {
+        $this->_tagCache = $cache;
+        $this->_options['tag_cache'] = $cache;
+    }
+
+    /**
+     * Get the Inner Cache if set
+     *
+     * @return Zend_Cache_Core
+     */
+    public function getInnerCache()
+    {
+        if (is_null($this->_tagCache)) {
+            Zend_Cache::throwException('An Inner Cache has not been set; use setInnerCache()');
+        }
+        return $this->_tagCache;
+    }
+
+    /**
+     * Verify path exists and is non-empty
+     * 
+     * @param  string $path 
+     * @return bool
+     */
+    protected function _verifyPath($path)
+    {
+        $path = realpath($path);
+        $base = realpath($this->_options['public_dir']);
+        return strncmp($path, $base, strlen($base)) !== 0;
+    }
+
+    /**
+     * Determine the page to save from the request
+     * 
+     * @return string
+     */
+    protected function _detectId()
+    {
+        return $_SERVER['REQUEST_URI'];
+    }
+
+    /**
+     * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
+     *
+     * Throw an exception if a problem is found
+     *
+     * @param  string $string Cache id or tag
+     * @throws Zend_Cache_Exception
+     * @return void
+     */
+    protected static function _validateIdOrTag($string)
+    {
+        if (!is_string($string)) {
+            Zend_Cache::throwException('Invalid id or tag : must be a string');
+        }
+
+        // Internal only checked in Frontend - not here!
+        if (substr($string, 0, 9) == 'internal-') {
+            return;
+        }
+
+        // Validation assumes no query string, fragments or scheme included - only the path
+        if (!preg_match(
+                '/^(?:\/(?:(?:%[[:xdigit:]]{2}|[A-Za-z0-9-_.!~*\'()\[\]:@&=+$,;])*)?)+$/', 
+                $string
+            )
+        ) {
+            Zend_Cache::throwException("Invalid id or tag '$string' : must be a valid URL path");
+        }
+    }
+}

+ 79 - 0
library/Zend/Cache/Frontend/Capture.php

@@ -0,0 +1,79 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cache
+ * @subpackage Zend_Cache_Frontend
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+
+/**
+ * @see Zend_Cache_Core
+ */
+require_once 'Zend/Cache/Core.php';
+
+
+/**
+ * @package    Zend_Cache
+ * @subpackage Zend_Cache_Frontend
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cache_Frontend_Capture extends Zend_Cache_Core
+{
+    /**
+     * Page identifiers
+     * @var array
+     */
+    protected $_idStack = array();
+
+    /**
+     * Tags
+     * @var array
+     */
+    protected $_tags = array();
+
+    /**
+     * Start the cache
+     *
+     * @param  string  $id Cache id
+     * @return mixed True if the cache is hit (false else) with $echoData=true (default) ; string else (datas)
+     */
+    public function start($id, $tags)
+    {
+        ob_start(array($this, '_flush'));
+        ob_implicit_flush(false);
+        $this->_idStack[] = $id;
+        return false;
+    }
+
+    /**
+     * callback for output buffering
+     * (shouldn't really be called manually)
+     *
+     * @param  string $data Buffered output
+     * @return string Data to send to browser
+     */
+    public function _flush($data)
+    {
+        $id = array_pop($this->_idStack);
+        if (is_null($id)) {
+            Zend_Cache::throwException('use of end() without a start()');
+        }
+        $this->save($data, $id, $this->_tags);
+        return $data;
+    }
+}

+ 272 - 0
library/Zend/Cache/Manager.php

@@ -0,0 +1,272 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Cache
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** @see Zend_Cache_Exception */
+require_once 'Zend/Cache/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Cache
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cache_Manager
+{
+    /**
+     * Constant holding reserved name for default Page Cache
+     */
+    const PAGECACHE = 'page';
+
+    /**
+     * Array of caches stored by the Cache Manager instance
+     *
+     * @var array
+     */
+    protected $_caches = array();
+
+    /**
+     * Array of ready made configuration templates for lazy
+     * loading caches.
+     *
+     * @var array
+     */
+    protected $_optionTemplates = array(
+        // Null Cache (Enforce Null/Empty Values)
+        'skeleton' => array(
+            'frontend' => array(
+                'name'    => null,
+                'options' => array(),
+            ),
+            'backend' => array(
+                'name'    => null,
+                'options' => array(),
+            ),
+        ),
+        // Simple Common Default
+        'default' => array(
+            'frontend' => array(
+                'name'    => 'Core',
+                'options' => array(
+                    'automatic_serialization' => true,
+                ),
+            ),
+            'backend' => array(
+                'name'    => 'File',
+                'options' => array(
+                    'cache_dir' => '../cache',
+                ),
+            ),
+        ),
+        // Static Page HTML Cache
+        'page' => array(
+            'frontend' => array(
+                'name'    => 'Output',
+                'options' => array(
+                    'ignore_user_abort' => true,
+                ),
+            ),
+            'backend' => array(
+                'name'    => 'Static',
+                'options' => array(
+                    'public_dir' => '../public',
+                ),
+            ),
+        ),
+        // Tag Cache
+        'tagCache' => array(
+            'frontend' => array(
+                'name'    => 'Core',
+                'options' => array(
+                    'automatic_serialization' => true,
+                ),
+            ),
+            'backend' => array(
+                'name'    => 'File',
+                'options' => array(
+                    'cache_dir' => '../cache',
+                ),
+            ),
+        ),
+    );
+
+    /**
+     * Set a new cache for the Cache Manager to contain
+     *
+     * @param  string $name
+     * @param  Zend_Cache_Core $cache
+     * @return Zend_Cache_Manager
+     */
+    public function setCache($name, Zend_Cache_Core $cache)
+    {
+        $this->_caches[$name] = $cache;
+        return $this;
+    }
+
+    /**
+     * Check if the Cache Manager contains the named cache object, or a named
+     * configuration template to lazy load the cache object
+     *
+     * @param string $name
+     * @return bool
+     */
+    public function hasCache($name)
+    {
+        if (isset($this->_caches[$name])
+            || $this->hasCacheTemplate($name)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Fetch the named cache object, or instantiate and return a cache object
+     * using a named configuration template
+     *
+     * @param  string $name
+     * @return Zend_Cache_Core
+     */
+    public function getCache($name)
+    {
+        if (isset($this->_caches[$name])) {
+            return $this->_caches[$name];
+        }
+        if (isset($this->_optionTemplates[$name])) {
+            if ($name == self::PAGECACHE 
+                && (!isset($this->_optionTemplates[$name]['backend']['options']['tag_cache']) 
+                || !$this->_optionTemplates[$name]['backend']['options']['tag_cache'] instanceof Zend_Cache_Core)
+            ) {
+                $this->_optionTemplates[$name]['backend']['options']['tag_cache']
+                    = $this->getCache('tagCache');
+            }
+            $this->_caches[$name] = Zend_Cache::factory(
+                $this->_optionTemplates[$name]['frontend']['name'],
+                $this->_optionTemplates[$name]['backend']['name'],
+                $this->_optionTemplates[$name]['frontend']['options'],
+                $this->_optionTemplates[$name]['backend']['options']
+            );
+            return $this->_caches[$name];
+        }
+    }
+
+    /**
+     * Set a named configuration template from which a cache object can later
+     * be lazy loaded
+     *
+     * @param  string $name
+     * @param  array $options
+     * @return Zend_Cache_Manager
+     */
+    public function setCacheTemplate($name, $options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Cache/Exception.php';
+            throw new Zend_Cache_Exception('Options passed must be in'
+                . ' an associative array or instance of Zend_Config');
+        }
+        $this->_optionTemplates[$name] = $options;
+    }
+
+    /**
+     * Check if the named configuration template
+     *
+     * @param  string $name
+     * @return bool
+     */
+    public function hasCacheTemplate($name)
+    {
+        if (isset($this->_optionTemplates[$name])) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get the named configuration template
+     *
+     * @param  string $name
+     * @return array
+     */
+    public function getCacheTemplate($name)
+    {
+        if (isset($this->_optionTemplates[$name])) {
+            return $this->_optionTemplates[$name];
+        }
+    }
+
+    /**
+     * Pass an array containing changes to be applied to a named
+     * configuration
+     * template
+     *
+     * @param  string $name
+     * @param  array $options
+     * @return Zend_Cache_Manager
+     * @throws Zend_Cache_Exception for invalid options format or if option templates do not have $name
+     */
+    public function setTemplateOptions($name, $options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } elseif (!is_array($options)) {
+            require_once 'Zend/Cache/Exception.php';
+            throw new Zend_Cache_Exception('Options passed must be in'
+                . ' an associative array or instance of Zend_Config');
+        }
+        if (!isset($this->_optionTemplates[$name])) {
+            throw new Zend_Cache_Exception('A cache configuration template'
+                . 'does not exist with the name "' . $name . '"');
+        }
+        $this->_optionTemplates[$name]
+            = $this->_mergeOptions($this->_optionTemplates[$name], $options);
+        return $this;
+    }
+
+    /**
+     * Simple method to merge two configuration arrays
+     *
+     * @param  array $current
+     * @param  array $options
+     * @return array
+     */
+    protected function _mergeOptions(array $current, array $options)
+    {
+        if (isset($options['frontend']['name'])) {
+            $current['frontend']['name'] = $options['frontend']['name'];
+        }
+        if (isset($options['backend']['name'])) {
+            $current['backend']['name'] = $options['backend']['name'];
+        }
+        if (isset($options['frontend']['options'])) {
+            foreach ($options['frontend']['options'] as $key=>$value) {
+                $current['frontend']['options'][$key] = $value;
+            }
+        }
+        if (isset($options['backend']['options'])) {
+            foreach ($options['backend']['options'] as $key=>$value) {
+                $current['backend']['options'][$key] = $value;
+            }
+        }
+        return $current;
+    }
+}

+ 10 - 8
tests/Zend/Cache/AllTests.php

@@ -26,21 +26,23 @@ if (!defined('PHPUnit_MAIN_METHOD')) {
     define('PHPUnit_MAIN_METHOD', 'Zend_Cache_AllTests::main');
 }
 
-require_once 'Zend/Cache/FactoryTest.php';
+require_once 'Zend/Cache/ApcBackendTest.php';
+require_once 'Zend/Cache/ClassFrontendTest.php';
 require_once 'Zend/Cache/CoreTest.php';
+require_once 'Zend/Cache/FactoryTest.php';
 require_once 'Zend/Cache/FileBackendTest.php';
-require_once 'Zend/Cache/SqliteBackendTest.php';
-require_once 'Zend/Cache/OutputFrontendTest.php';
-require_once 'Zend/Cache/FunctionFrontendTest.php';
-require_once 'Zend/Cache/ClassFrontendTest.php';
 require_once 'Zend/Cache/FileFrontendTest.php';
-require_once 'Zend/Cache/ApcBackendTest.php';
-require_once 'Zend/Cache/XcacheBackendTest.php';
+require_once 'Zend/Cache/FunctionFrontendTest.php';
+require_once 'Zend/Cache/ManagerTest.php';
 require_once 'Zend/Cache/MemcachedBackendTest.php';
+require_once 'Zend/Cache/OutputFrontendTest.php';
 require_once 'Zend/Cache/PageFrontendTest.php';
-require_once 'Zend/Cache/ZendPlatformBackendTest.php';
 require_once 'Zend/Cache/SkipTests.php';
+require_once 'Zend/Cache/SqliteBackendTest.php';
+require_once 'Zend/Cache/StaticBackendTest.php';
 require_once 'Zend/Cache/TwoLevelsBackendTest.php';
+require_once 'Zend/Cache/XcacheBackendTest.php';
+require_once 'Zend/Cache/ZendPlatformBackendTest.php';
 require_once 'Zend/Cache/ZendServerDiskTest.php';
 require_once 'Zend/Cache/ZendServerShMemTest.php';
 

+ 1 - 2
tests/Zend/Cache/CommonBackendTest.php

@@ -174,8 +174,7 @@ class Zend_Cache_CommonBackendTest extends PHPUnit_Framework_TestCase {
     {
         $this->assertTrue($this->_instance->remove('bar'));
         $this->assertFalse($this->_instance->test('bar'));
-
-        $this->_instance->remove('barbar');
+        $this->assertFalse($this->_instance->remove('barbar'));
         $this->assertFalse($this->_instance->test('barbar'));
     }
 

+ 239 - 0
tests/Zend/Cache/ManagerTest.php

@@ -0,0 +1,239 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend_Cache
+ * @package    UnitTests
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+ 
+require_once 'Zend/Cache.php';
+require_once 'Zend/Cache/Manager.php';
+require_once 'Zend/Config.php';
+
+/**
+ * @category   Zend_Cache
+ * @package    UnitTests
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Cache_ManagerTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setUp()
+    {
+        $this->_cache_dir = $this->mkdir();
+        $this->_cache = Zend_Cache::factory(
+            'Core', 'File',
+            array('automatic_serialization'=>true),
+            array('cache_dir'=>$this->_cache_dir)
+        );
+    }
+
+    public function tearDown()
+    {
+        $this->rmdir();
+        $this->_cache = null;
+    }
+
+    public function testSetsCacheObject()
+    {
+        $manager = new Zend_Cache_Manager;
+        $manager->setCache('cache1', $this->_cache);
+        $this->assertTrue($manager->getCache('cache1') instanceof Zend_Cache_Core);
+    }
+
+    public function testLazyLoadsDefaultPageCache()
+    {
+        $manager = new Zend_Cache_Manager;
+        $manager->setTemplateOptions('tagCache',array(
+            'backend' => array(
+                'options' => array(
+                    'cache_dir' => $this->_cache_dir
+                )
+            )
+        ));
+        $this->assertTrue($manager->getCache('page') instanceof Zend_Cache_Frontend_Output);
+    }
+
+    public function testCanOverrideCacheFrontendNameConfiguration()
+    {
+        $manager = new Zend_Cache_Manager;
+        $manager->setTemplateOptions('tagCache',array(
+            'backend' => array(
+                'options' => array(
+                    'cache_dir' => $this->_cache_dir
+                )
+            )
+        ));
+        $manager->setTemplateOptions('page', array(
+            'frontend' => array(
+                'name'=> 'Page'
+            )
+        ));
+        $this->assertTrue($manager->getCache('page') instanceof Zend_Cache_Frontend_Page);
+    }
+
+    public function testCanMergeTemplateCacheOptionsFromZendConfig()
+    {
+        $manager = new Zend_Cache_Manager;
+        $config = new Zend_Config(array(
+            'backend' => array(
+                'options' => array(
+                    'cache_dir' => $this->_cache_dir
+                )
+            )
+        ));
+        $manager->setTemplateOptions('tagCache', $config);
+        $options = $manager->getCacheTemplate('tagCache');
+        $this->assertEquals($this->_cache_dir, $options['backend']['options']['cache_dir']);
+    }
+
+    public function testCanOverrideCacheBackendendNameConfiguration()
+    {
+        $manager = new Zend_Cache_Manager;
+        $manager->setTemplateOptions('tagCache',array(
+            'backend' => array(
+                'options' => array(
+                    'cache_dir' => $this->_cache_dir
+                )
+            )
+        ));
+        $manager->setTemplateOptions('page', array(
+            'backend' => array(
+                'name'=> 'File'
+            )
+        ));
+        $this->assertTrue($manager->getCache('page')->getBackend() instanceof Zend_Cache_Backend_File);
+    }
+
+    public function testCanOverrideCacheFrontendOptionsConfiguration()
+    {
+        $manager = new Zend_Cache_Manager;
+        $manager->setTemplateOptions('page', array(
+            'frontend' => array(
+                'options'=> array(
+                    'lifetime' => 9999
+                )
+            )
+        ));
+        $config = $manager->getCacheTemplate('page');
+        $this->assertEquals(9999, $config['frontend']['options']['lifetime']);
+    }
+
+    public function testCanOverrideCacheBackendOptionsConfiguration()
+    {
+        $manager = new Zend_Cache_Manager;
+        $manager->setTemplateOptions('page', array(
+            'backend' => array(
+                'options'=> array(
+                    'public_dir' => './cacheDir'
+                )
+            )
+        ));
+        $config = $manager->getCacheTemplate('page');
+        $this->assertEquals('./cacheDir', $config['backend']['options']['public_dir']);
+    }
+
+    public function testSetsConfigTemplate()
+    {
+        $manager = new Zend_Cache_Manager;
+        $config = array(
+            'frontend' => array(
+                'name' => 'Core',
+                'options' => array(
+                    'automatic_serialization' => true
+                )
+            ),
+            'backend' => array(
+                'name' => 'File',
+                'options' => array(
+                    'cache_dir' => '../cache',
+                )
+            )
+        );
+        $manager->setCacheTemplate('myCache', $config);
+        $this->assertSame($config, $manager->getCacheTemplate('myCache'));
+    }
+
+    public function testSetsOptionsTemplateUsingZendConfig()
+    {
+        $manager = new Zend_Cache_Manager;
+        $config = array(
+            'frontend' => array(
+                'name' => 'Core',
+                'options' => array(
+                    'automatic_serialization' => true
+                )
+            ),
+            'backend' => array(
+                'name' => 'File',
+                'options' => array(
+                    'cache_dir' => '../cache',
+                )
+            )
+        );
+        $manager->setCacheTemplate('myCache', new Zend_Config($config));
+        $this->assertSame($config, $manager->getCacheTemplate('myCache'));
+    }
+
+    public function testConfigTemplatesDetectedAsAvailableCaches()
+    {
+        $manager = new Zend_Cache_Manager;
+        $this->assertTrue($manager->hasCache('page'));
+    }
+
+    public function testGettingPageCacheAlsoCreatesTagCache()
+    {
+        $manager = new Zend_Cache_Manager;
+        $tagCacheConfig = $manager->getCacheTemplate('tagCache');
+        $tagCacheConfig['backend']['options']['cache_dir'] = $this->getTmpDir();
+        $manager->setCacheTemplate('tagCache', $tagCacheConfig);
+        $tagCache = $manager->getCache('page')->getBackend()->getOption('tag_cache');
+        $this->assertTrue($tagCache instanceof Zend_Cache_Core);
+    }
+
+    // Helper Methods
+
+    public function mkdir()
+    {
+        $tmp = $this->getTmpDir();
+        @mkdir($tmp);
+        return $tmp;
+    }
+
+    public function rmdir()
+    {
+        $tmpDir = $this->getTmpDir(false);
+        foreach (glob("$tmpDir*") as $dirname) {
+            @rmdir($dirname);
+        }
+    }
+
+    public function getTmpDir($date = true)
+    {
+        $suffix = '';
+        $tmp = sys_get_temp_dir();
+        if ($date) {
+            $suffix = date('mdyHis');
+        }
+        if (is_writeable($tmp)) {
+            return $tmp . DIRECTORY_SEPARATOR . 'zend_cache_tmp_dir_' . $suffix;
+        } else {
+            throw new Exception("no writable tmpdir found");
+        }
+    }
+
+}

+ 248 - 0
tests/Zend/Cache/StaticBackendTest.php

@@ -0,0 +1,248 @@
+<?php
+/**
+ * @package    Zend_Cache
+ * @subpackage UnitTests
+ */
+
+/**
+ * Zend_Cache
+ */
+require_once 'Zend/Cache.php';
+require_once 'Zend/Cache/Backend/Static.php';
+
+/**
+ * Zend_Log
+ */
+require_once 'Zend/Log.php';
+require_once 'Zend/Log/Writer/Null.php';
+
+/**
+ * Common tests for backends
+ */
+require_once 'CommonBackendTest.php';
+
+/**
+ * PHPUnit test case
+ */
+require_once 'PHPUnit/Framework/TestCase.php';
+
+/**
+ * @package    Zend_Cache
+ * @subpackage UnitTests
+ */
+class Zend_Cache_StaticBackendTest extends Zend_Cache_CommonBackendTest {
+
+    protected $_instance;
+    protected $_instance2;
+    protected $_cache_dir;
+    protected $_requestUriOld;
+    protected $_innerCache;
+
+    public function __construct($name = null, array $data = array(), $dataName = '')
+    {
+        parent::__construct('Zend_Cache_Backend_Static', $data, $dataName);
+    }
+
+    public function setUp($notag = false)
+    {
+        $this->mkdir();
+        $this->_cache_dir = $this->mkdir();
+        @mkdir($this->_cache_dir.'/tags');
+
+        $this->_innerCache = Zend_Cache::factory('Core','File',
+            array('automatic_serialization'=>true), array('cache_dir'=>$this->_cache_dir.'/tags')
+        );
+        $this->_instance = new Zend_Cache_Backend_Static(array(
+            'public_dir' => $this->_cache_dir,
+            'tag_cache' => $this->_innerCache
+        ));
+
+        $logger = new Zend_Log(new Zend_Log_Writer_Null());
+        $this->_instance->setDirectives(array('logger' => $logger));
+
+        $this->_requestUriOld =
+            isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null;
+        $_SERVER['REQUEST_URI'] = '/foo';
+
+        $this->_instance->setDirectives(array('logging' => true));
+
+        $this->_instance->save('bar : data to cache', '/bar', array('tag3', 'tag4'));
+        $this->_instance->save('bar2 : data to cache', '/bar2', array('tag3', 'tag1'));
+        $this->_instance->save('bar3 : data to cache', '/bar3', array('tag2', 'tag3'));
+    }
+
+    public function tearDown()
+    {
+        parent::tearDown();
+        unset($this->_instance);
+        $_SERVER['REQUEST_URI'] = $this->_requestUriOld;
+        $this->rmdir();
+    }
+
+    public function testConstructorCorrectCall()
+    {
+        $test = new Zend_Cache_Backend_Static(array());
+    }
+
+    public function testRemoveCorrectCall()
+    {
+        $this->assertTrue($this->_instance->remove('/bar'));
+        $this->assertFalse($this->_instance->test('/bar'));
+        $this->assertFalse($this->_instance->remove('/barbar'));
+        $this->assertFalse($this->_instance->test('/barbar'));
+    }
+
+    public function testOptionsSetTagCache()
+    {
+        $test = new Zend_Cache_Backend_Static(array('tag_cache'=>$this->_innerCache));
+        $this->assertTrue($test->getInnerCache() instanceof Zend_Cache_Core);
+    }
+
+    public function testSaveCorrectCall()
+    {
+        $res = $this->_instance->save('data to cache', '/foo', array('tag1', 'tag2'));
+        $this->assertTrue($res);
+    }
+
+    public function testSaveWithNullLifeTime()
+    {
+        $this->_instance->setDirectives(array('lifetime' => null));
+        $res = $this->_instance->save('data to cache', '/foo', array('tag1', 'tag2'));
+        $this->assertTrue($res);
+    }
+
+    public function testSaveWithSpecificLifeTime()
+    {
+        $this->_instance->setDirectives(array('lifetime' => 3600));
+        $res = $this->_instance->save('data to cache', '/foo', array('tag1', 'tag2'), 10);
+        $this->assertTrue($res);
+    }
+
+    public function testTestWithAnExistingCacheId()
+    {
+        $res = $this->_instance->test('/bar');
+        if (!$res) {
+            $this->fail('test() return false');
+        }
+        return;
+    }
+
+    public function testTestWithANonExistingCacheId()
+    {
+        $this->assertFalse($this->_instance->test('/barbar'));
+    }
+
+    public function testTestWithAnExistingCacheIdAndANullLifeTime()
+    {
+        $this->_instance->setDirectives(array('lifetime' => null));
+        $res = $this->_instance->test('/bar');
+        if (!$res) {
+            $this->fail('test() return false');
+        }
+        return;
+    }
+
+    public function testGetWithANonExistingCacheId()
+    {
+        $this->assertFalse($this->_instance->load('/barbar'));
+    }
+
+    public function testGetWithAnExistingCacheId()
+    {
+        $this->assertEquals('bar : data to cache', $this->_instance->load('/bar'));
+    }
+
+    public function testGetWithAnExistingCacheIdAndUTFCharacters()
+    {
+        $data = '"""""' . "'" . '\n' . 'ééééé';
+        $this->_instance->save($data, '/foo');
+        $this->assertEquals($data, $this->_instance->load('/foo'));
+    }
+
+    public function testCleanModeMatchingTags()
+    {
+        $this->assertTrue($this->_instance->clean('matchingTag', array('tag3')));
+        $this->assertFalse($this->_instance->test('/bar'));
+        $this->assertFalse($this->_instance->test('/bar2'));
+    }
+
+    public function testCleanModeMatchingTags2()
+    {
+        $this->assertTrue($this->_instance->clean('matchingTag', array('tag3', 'tag4')));
+        $this->assertFalse($this->_instance->test('/bar'));
+    }
+
+    public function testCleanModeNotMatchingTags()
+    {
+        $this->assertTrue($this->_instance->clean('notMatchingTag', array('tag3')));
+        $this->assertTrue($this->_instance->test('/bar'));
+        $this->assertTrue($this->_instance->test('/bar2'));
+    }
+
+    public function testCleanModeNotMatchingTags2()
+    {
+        $this->assertTrue($this->_instance->clean('notMatchingTag', array('tag4')));
+        $this->assertTrue($this->_instance->test('/bar'));
+        $this->assertFalse($this->_instance->test('/bar2'));
+    }
+
+    public function testCleanModeNotMatchingTags3()
+    {
+        $this->assertTrue($this->_instance->clean('notMatchingTag', array('tag4', 'tag1')));
+        $this->assertTrue($this->_instance->test('/bar'));
+        $this->assertTrue($this->_instance->test('/bar2'));
+        $this->assertFalse($this->_instance->test('/bar3'));
+    }
+
+    public function testCleanModeAll()
+    {
+        $this->assertTrue($this->_instance->clean('all'));
+        $this->assertFalse($this->_instance->test('bar'));
+        $this->assertFalse($this->_instance->test('bar2'));
+    }
+
+
+    // Irrelevant Tests (from common tests)
+
+    public function testGetWithAnExpiredCacheId()
+    {
+        $this->markTestSkipped('Irrelevant Test');
+    }
+
+    public function testCleanModeOld()
+    {
+        $this->markTestSkipped('Irrelevant Test');
+    }
+
+    // Helper Methods
+
+    public function mkdir()
+    {
+        $tmp = $this->getTmpDir();
+        @mkdir($tmp);
+        return $tmp;
+    }
+
+    public function rmdir()
+    {
+        $tmpDir = $this->getTmpDir(false);
+        foreach (glob("$tmpDir*") as $dirname) {
+            @rmdir($dirname);
+        }
+    }
+
+    public function getTmpDir($date = true)
+    {
+        $suffix = '';
+        $tmp = sys_get_temp_dir();
+        if ($date) {
+            $suffix = date('mdyHis');
+        }
+        if (is_writeable($tmp)) {
+            return $tmp . DIRECTORY_SEPARATOR . 'zend_cache_tmp_dir_' . $suffix;
+        } else {
+            throw new Exception("no writable tmpdir found");
+        }
+    }
+
+}