1<?php
2/**
3 * Zend Framework
4 *
5 * LICENSE
6 *
7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * http://framework.zend.com/license/new-bsd
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@zend.com so we can send you a copy immediately.
14 *
15 * @category   Zend
16 * @package    Zend_Cache
17 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
18 * @license    http://framework.zend.com/license/new-bsd     New BSD License
19 * @version    $Id$
20 */
21
22
23/**
24 * @package    Zend_Cache
25 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
26 * @license    http://framework.zend.com/license/new-bsd     New BSD License
27 */
28abstract class Zend_Cache
29{
30
31    /**
32     * Standard frontends
33     *
34     * @var array
35     */
36    public static $standardFrontends = array('Core', 'Output', 'Class', 'File', 'Function', 'Page');
37
38    /**
39     * Standard backends
40     *
41     * @var array
42     */
43    public static $standardBackends = array('File', 'Sqlite', 'Memcached', 'Libmemcached', 'Apc', 'ZendPlatform',
44                                            'Xcache', 'TwoLevels', 'WinCache', 'ZendServer_Disk', 'ZendServer_ShMem');
45
46    /**
47     * Standard backends which implement the ExtendedInterface
48     *
49     * @var array
50     */
51    public static $standardExtendedBackends = array('File', 'Apc', 'TwoLevels', 'Memcached', 'Libmemcached', 'Sqlite', 'WinCache');
52
53    /**
54     * Only for backward compatibility (may be removed in next major release)
55     *
56     * @var array
57     * @deprecated
58     */
59    public static $availableFrontends = array('Core', 'Output', 'Class', 'File', 'Function', 'Page');
60
61    /**
62     * Only for backward compatibility (may be removed in next major release)
63     *
64     * @var array
65     * @deprecated
66     */
67    public static $availableBackends = array('File', 'Sqlite', 'Memcached', 'Libmemcached', 'Apc', 'ZendPlatform', 'Xcache', 'WinCache', 'TwoLevels');
68
69    /**
70     * Consts for clean() method
71     */
72    const CLEANING_MODE_ALL              = 'all';
73    const CLEANING_MODE_OLD              = 'old';
74    const CLEANING_MODE_MATCHING_TAG     = 'matchingTag';
75    const CLEANING_MODE_NOT_MATCHING_TAG = 'notMatchingTag';
76    const CLEANING_MODE_MATCHING_ANY_TAG = 'matchingAnyTag';
77
78    /**
79     * Factory
80     *
81     * @param mixed  $frontend        frontend name (string) or Zend_Cache_Frontend_ object
82     * @param mixed  $backend         backend name (string) or Zend_Cache_Backend_ object
83     * @param array  $frontendOptions associative array of options for the corresponding frontend constructor
84     * @param array  $backendOptions  associative array of options for the corresponding backend constructor
85     * @param boolean $customFrontendNaming if true, the frontend argument is used as a complete class name ; if false, the frontend argument is used as the end of "Zend_Cache_Frontend_[...]" class name
86     * @param boolean $customBackendNaming if true, the backend argument is used as a complete class name ; if false, the backend argument is used as the end of "Zend_Cache_Backend_[...]" class name
87     * @param boolean $autoload if true, there will no require_once for backend and frontend (useful only for custom backends/frontends)
88     * @throws Zend_Cache_Exception
89     * @return Zend_Cache_Core|Zend_Cache_Frontend
90     */
91    public static function factory($frontend, $backend, $frontendOptions = array(), $backendOptions = array(), $customFrontendNaming = false, $customBackendNaming = false, $autoload = false)
92    {
93        if (is_string($backend)) {
94            $backendObject = self::_makeBackend($backend, $backendOptions, $customBackendNaming, $autoload);
95        } else {
96            if ((is_object($backend)) && (in_array('Zend_Cache_Backend_Interface', class_implements($backend)))) {
97                $backendObject = $backend;
98            } else {
99                self::throwException('backend must be a backend name (string) or an object which implements Zend_Cache_Backend_Interface');
100            }
101        }
102        if (is_string($frontend)) {
103            $frontendObject = self::_makeFrontend($frontend, $frontendOptions, $customFrontendNaming, $autoload);
104        } else {
105            if (is_object($frontend)) {
106                $frontendObject = $frontend;
107            } else {
108                self::throwException('frontend must be a frontend name (string) or an object');
109            }
110        }
111        $frontendObject->setBackend($backendObject);
112        return $frontendObject;
113    }
114
115    /**
116     * Backend Constructor
117     *
118     * @param string  $backend
119     * @param array   $backendOptions
120     * @param boolean $customBackendNaming
121     * @param boolean $autoload
122     * @return Zend_Cache_Backend
123     */
124    public static function _makeBackend($backend, $backendOptions, $customBackendNaming = false, $autoload = false)
125    {
126        if (!$customBackendNaming) {
127            $backend  = self::_normalizeName($backend);
128        }
129        if (in_array($backend, Zend_Cache::$standardBackends)) {
130            // we use a standard backend
131            $backendClass = 'Zend_Cache_Backend_' . $backend;
132            // security controls are explicit
133        } else {
134            // we use a custom backend
135            if (!preg_match('~^[\w\\\\]+$~D', $backend)) {
136                Zend_Cache::throwException("Invalid backend name [$backend]");
137            }
138            if (!$customBackendNaming) {
139                // we use this boolean to avoid an API break
140                $backendClass = 'Zend_Cache_Backend_' . $backend;
141            } else {
142                $backendClass = $backend;
143            }
144            if (!$autoload) {
145                $file = str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php';
146                if (!(self::_isReadable($file))) {
147                    self::throwException("file $file not found in include_path");
148                }
149            }
150        }
151        return new $backendClass($backendOptions);
152    }
153
154    /**
155     * Frontend Constructor
156     *
157     * @param string  $frontend
158     * @param array   $frontendOptions
159     * @param boolean $customFrontendNaming
160     * @param boolean $autoload
161     * @return Zend_Cache_Core|Zend_Cache_Frontend
162     */
163    public static function _makeFrontend($frontend, $frontendOptions = array(), $customFrontendNaming = false, $autoload = false)
164    {
165        if (!$customFrontendNaming) {
166            $frontend = self::_normalizeName($frontend);
167        }
168        if (in_array($frontend, self::$standardFrontends)) {
169            // we use a standard frontend
170            // For perfs reasons, with frontend == 'Core', we can interact with the Core itself
171            $frontendClass = 'Zend_Cache_' . ($frontend != 'Core' ? 'Frontend_' : '') . $frontend;
172            // security controls are explicit
173        } else {
174            // we use a custom frontend
175            if (!preg_match('~^[\w\\\\]+$~D', $frontend)) {
176                Zend_Cache::throwException("Invalid frontend name [$frontend]");
177            }
178            if (!$customFrontendNaming) {
179                // we use this boolean to avoid an API break
180                $frontendClass = 'Zend_Cache_Frontend_' . $frontend;
181            } else {
182                $frontendClass = $frontend;
183            }
184            if (!$autoload) {
185                $file = str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php';
186                if (!(self::_isReadable($file))) {
187                    self::throwException("file $file not found in include_path");
188                }
189            }
190        }
191        return new $frontendClass($frontendOptions);
192    }
193
194    /**
195     * Throw an exception
196     *
197     * Note : for perf reasons, the "load" of Zend/Cache/Exception is dynamic
198     * @param  string $msg  Message for the exception
199     * @throws Zend_Cache_Exception
200     */
201    public static function throwException($msg, Exception $e = null)
202    {
203        // For perfs reasons, we use this dynamic inclusion
204        throw new Zend_Cache_Exception($msg, 0, $e);
205    }
206
207    /**
208     * Normalize frontend and backend names to allow multiple words TitleCased
209     *
210     * @param  string $name  Name to normalize
211     * @return string
212     */
213    protected static function _normalizeName($name)
214    {
215        $name = ucfirst(strtolower($name));
216        $name = str_replace(array('-', '_', '.'), ' ', $name);
217        $name = ucwords($name);
218        $name = str_replace(' ', '', $name);
219        if (stripos($name, 'ZendServer') === 0) {
220            $name = 'ZendServer_' . substr($name, strlen('ZendServer'));
221        }
222
223        return $name;
224    }
225
226    /**
227     * Returns TRUE if the $filename is readable, or FALSE otherwise.
228     * This function uses the PHP include_path, where PHP's is_readable()
229     * does not.
230     *
231     * Note : this method comes from Zend_Loader (see #ZF-2891 for details)
232     *
233     * @param string   $filename
234     * @return boolean
235     */
236    private static function _isReadable($filename)
237    {
238        if (!$fh = @fopen($filename, 'r', true)) {
239            return false;
240        }
241        @fclose($fh);
242        return true;
243    }
244
245}
246