1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Cache
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Public cache handler
14 *
15 * @since 11.1
16 * @note As of 4.0 this class will be abstract
17 */
18 class JCacheController
19 {
20 /**
21 * JCache object
22 *
23 * @var JCache
24 * @since 11.1
25 */
26 public $cache;
27
28 /**
29 * Array of options
30 *
31 * @var array
32 * @since 11.1
33 */
34 public $options;
35
36 /**
37 * Constructor
38 *
39 * @param array $options Array of options
40 *
41 * @since 11.1
42 */
43 public function __construct($options)
44 {
45 $this->cache = new JCache($options);
46 $this->options = & $this->cache->_options;
47
48 // Overwrite default options with given options
49 foreach ($options as $option => $value)
50 {
51 if (isset($options[$option]))
52 {
53 $this->options[$option] = $options[$option];
54 }
55 }
56 }
57
58 /**
59 * Magic method to proxy JCacheController method calls to JCache
60 *
61 * @param string $name Name of the function
62 * @param array $arguments Array of arguments for the function
63 *
64 * @return mixed
65 *
66 * @since 11.1
67 */
68 public function __call($name, $arguments)
69 {
70 return call_user_func_array(array($this->cache, $name), $arguments);
71 }
72
73 /**
74 * Returns a reference to a cache adapter object, always creating it
75 *
76 * @param string $type The cache object type to instantiate; default is output.
77 * @param array $options Array of options
78 *
79 * @return JCacheController
80 *
81 * @since 11.1
82 * @throws RuntimeException
83 */
84 public static function getInstance($type = 'output', $options = array())
85 {
86 self::addIncludePath(JPATH_PLATFORM . '/joomla/cache/controller');
87
88 $type = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $type));
89
90 $class = 'JCacheController' . ucfirst($type);
91
92 if (!class_exists($class))
93 {
94 // Search for the class file in the JCache include paths.
95 jimport('joomla.filesystem.path');
96
97 $path = JPath::find(self::addIncludePath(), strtolower($type) . '.php');
98
99 if ($path !== false)
100 {
101 JLoader::register($class, $path);
102 }
103
104 // The class should now be loaded
105 if (!class_exists($class))
106 {
107 throw new RuntimeException('Unable to load Cache Controller: ' . $type, 500);
108 }
109 }
110
111 return new $class($options);
112 }
113
114 /**
115 * Add a directory where JCache should search for controllers. You may either pass a string or an array of directories.
116 *
117 * @param array|string $path A path to search.
118 *
119 * @return array An array with directory elements
120 *
121 * @since 11.1
122 */
123 public static function addIncludePath($path = '')
124 {
125 static $paths;
126
127 if (!isset($paths))
128 {
129 $paths = array();
130 }
131
132 if (!empty($path) && !in_array($path, $paths))
133 {
134 jimport('joomla.filesystem.path');
135 array_unshift($paths, JPath::clean($path));
136 }
137
138 return $paths;
139 }
140
141 /**
142 * Get stored cached data by ID and group
143 *
144 * @param string $id The cache data ID
145 * @param string $group The cache data group
146 *
147 * @return mixed Boolean false on no result, cached object otherwise
148 *
149 * @since 11.1
150 * @deprecated 4.0 Implement own method in subclass
151 */
152 public function get($id, $group = null)
153 {
154 $data = $this->cache->get($id, $group);
155
156 if ($data === false)
157 {
158 $locktest = $this->cache->lock($id, $group);
159
160 // If locklooped is true try to get the cached data again; it could exist now.
161 if ($locktest->locked === true && $locktest->locklooped === true)
162 {
163 $data = $this->cache->get($id, $group);
164 }
165
166 if ($locktest->locked === true)
167 {
168 $this->cache->unlock($id, $group);
169 }
170 }
171
172 // Check again because we might get it from second attempt
173 if ($data !== false)
174 {
175 // Trim to fix unserialize errors
176 $data = unserialize(trim($data));
177 }
178
179 return $data;
180 }
181
182 /**
183 * Store data to cache by ID and group
184 *
185 * @param mixed $data The data to store
186 * @param string $id The cache data ID
187 * @param string $group The cache data group
188 * @param boolean $wrkarounds True to use wrkarounds
189 *
190 * @return boolean True if cache stored
191 *
192 * @since 11.1
193 * @deprecated 4.0 Implement own method in subclass
194 */
195 public function store($data, $id, $group = null, $wrkarounds = true)
196 {
197 $locktest = $this->cache->lock($id, $group);
198
199 if ($locktest->locked === false && $locktest->locklooped === true)
200 {
201 // We can not store data because another process is in the middle of saving
202 return false;
203 }
204
205 $result = $this->cache->store(serialize($data), $id, $group);
206
207 if ($locktest->locked === true)
208 {
209 $this->cache->unlock($id, $group);
210 }
211
212 return $result;
213 }
214 }
215