3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Cache\Storage\Adapter
;
14 use Zend\Cache\Exception
;
15 use Zend\Cache\Storage\AvailableSpaceCapableInterface
;
16 use Zend\Cache\Storage\Capabilities
;
17 use Zend\Cache\Storage\ClearByNamespaceInterface
;
18 use Zend\Cache\Storage\ClearByPrefixInterface
;
19 use Zend\Cache\Storage\FlushableInterface
;
20 use Zend\Cache\Storage\IterableInterface
;
21 use Zend\Cache\Storage\TotalSpaceCapableInterface
;
23 class XCache
extends AbstractAdapter
implements
24 AvailableSpaceCapableInterface
,
25 ClearByNamespaceInterface
,
26 ClearByPrefixInterface
,
29 TotalSpaceCapableInterface
32 * Backup HTTP authentication properties of $_SERVER array
36 protected $backupAuth = array();
39 * Total space in bytes
43 protected $totalSpace;
48 * @param null|array|Traversable|XCacheOptions $options
49 * @throws Exception\ExceptionInterface
51 public function __construct($options = null)
53 if (!extension_loaded('xcache')) {
54 throw new Exception\
ExtensionNotLoadedException('Missing ext/xcache');
57 if (PHP_SAPI
== 'cli') {
58 throw new Exception\
ExtensionNotLoadedException(
59 "ext/xcache isn't available on SAPI 'cli'"
63 if (ini_get('xcache.var_size') <= 0) {
64 throw new Exception\
ExtensionNotLoadedException(
65 "ext/xcache is disabled - see 'xcache.var_size'"
69 parent
::__construct($options);
77 * @param array|Traversable|XCacheOptions $options
81 public function setOptions($options)
83 if (!$options instanceof XCacheOptions
) {
84 $options = new XCacheOptions($options);
87 return parent
::setOptions($options);
93 * @return XCacheOptions
96 public function getOptions()
98 if (!$this->options
) {
99 $this->setOptions(new XCacheOptions());
101 return $this->options
;
104 /* TotalSpaceCapableInterface */
107 * Get total space in bytes
111 public function getTotalSpace()
113 if ($this->totalSpace
=== null) {
114 $this->totalSpace
= 0;
116 $this->initAdminAuth();
117 $cnt = xcache_count(XC_TYPE_VAR
);
118 for ($i=0; $i < $cnt; $i++
) {
119 $info = xcache_info(XC_TYPE_VAR
, $i);
120 $this->totalSpace+
= $info['size'];
122 $this->resetAdminAuth();
125 return $this->totalSpace
;
128 /* AvailableSpaceCapableInterface */
131 * Get available space in bytes
135 public function getAvailableSpace()
139 $this->initAdminAuth();
140 $cnt = xcache_count(XC_TYPE_VAR
);
141 for ($i = 0; $i < $cnt; $i++
) {
142 $info = xcache_info(XC_TYPE_VAR
, $i);
143 $availableSpace+
= $info['avail'];
145 $this->resetAdminAuth();
147 return $availableSpace;
150 /* ClearByNamespaceInterface */
153 * Remove items by given namespace
155 * @param string $namespace
158 public function clearByNamespace($namespace)
160 $namespace = (string) $namespace;
161 if ($namespace === '') {
162 throw new Exception\
InvalidArgumentException('No namespace given');
165 $options = $this->getOptions();
166 $prefix = $namespace . $options->getNamespaceSeparator();
168 xcache_unset_by_prefix($prefix);
172 /* ClearByPrefixInterface */
175 * Remove items matching given prefix
177 * @param string $prefix
180 public function clearByPrefix($prefix)
182 $prefix = (string) $prefix;
183 if ($prefix === '') {
184 throw new Exception\
InvalidArgumentException('No prefix given');
187 $options = $this->getOptions();
188 $namespace = $options->getNamespace();
189 $prefix = ($namespace === '') ?
'' : $namespace . $options->getNamespaceSeparator() . $prefix;
191 xcache_unset_by_prefix($prefix);
195 /* FlushableInterface */
198 * Flush the whole storage
202 public function flush()
204 $this->initAdminAuth();
205 $cnt = xcache_count(XC_TYPE_VAR
);
206 for ($i = 0; $i < $cnt; $i++
) {
207 xcache_clear_cache(XC_TYPE_VAR
, $i);
209 $this->resetAdminAuth();
214 /* IterableInterface */
217 * Get the storage iterator
219 * @return KeyListIterator
221 public function getIterator()
223 $options = $this->getOptions();
224 $namespace = $options->getNamespace();
227 $this->initAdminAuth();
229 if ($namespace === '') {
230 $cnt = xcache_count(XC_TYPE_VAR
);
231 for ($i=0; $i < $cnt; $i++
) {
232 $list = xcache_list(XC_TYPE_VAR
, $i);
233 foreach ($list['cache_list'] as & $item) {
234 $keys[] = $item['name'];
238 $prefix = $namespace . $options->getNamespaceSeparator();
239 $prefixL = strlen($prefix);
241 $cnt = xcache_count(XC_TYPE_VAR
);
242 for ($i=0; $i < $cnt; $i++
) {
243 $list = xcache_list(XC_TYPE_VAR
, $i);
244 foreach ($list['cache_list'] as & $item) {
245 $keys[] = substr($item['name'], $prefixL);
250 $this->resetAdminAuth();
252 return new KeyListIterator($this, $keys);
258 * Internal method to get an item.
260 * @param string $normalizedKey
261 * @param bool $success
262 * @param mixed $casToken
263 * @return mixed Data on success, null on failure
264 * @throws Exception\ExceptionInterface
266 protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
268 $options = $this->getOptions();
269 $namespace = $options->getNamespace();
270 $prefix = ($namespace === '') ?
'' : $namespace . $options->getNamespaceSeparator();
271 $internalKey = $prefix . $normalizedKey;
273 $result = xcache_get($internalKey);
274 $success = ($result !== null);
284 * Internal method to test if an item exists.
286 * @param string $normalizedKey
288 * @throws Exception\ExceptionInterface
290 protected function internalHasItem(& $normalizedKey)
292 $options = $this->getOptions();
293 $namespace = $options->getNamespace();
294 $prefix = ($namespace === '') ?
'' : $namespace . $options->getNamespaceSeparator();
295 return xcache_isset($prefix . $normalizedKey);
299 * Get metadata of an item.
301 * @param string $normalizedKey
302 * @return array|bool Metadata on success, false on failure
303 * @throws Exception\ExceptionInterface
305 protected function internalGetMetadata(& $normalizedKey)
307 $options = $this->getOptions();
308 $namespace = $options->getNamespace();
309 $prefix = ($namespace === '') ?
'' : $namespace . $options->getNamespaceSeparator();
310 $internalKey = $prefix . $normalizedKey;
312 if (xcache_isset($internalKey)) {
313 $this->initAdminAuth();
314 $cnt = xcache_count(XC_TYPE_VAR
);
315 for ($i=0; $i < $cnt; $i++
) {
316 $list = xcache_list(XC_TYPE_VAR
, $i);
317 foreach ($list['cache_list'] as & $metadata) {
318 if ($metadata['name'] === $internalKey) {
319 $this->normalizeMetadata($metadata);
324 $this->resetAdminAuth();
333 * Internal method to store an item.
335 * @param string $normalizedKey
336 * @param mixed $value
338 * @throws Exception\ExceptionInterface
340 protected function internalSetItem(& $normalizedKey, & $value)
342 $options = $this->getOptions();
343 $namespace = $options->getNamespace();
344 $prefix = ($options === '') ?
'' : $namespace . $options->getNamespaceSeparator();
345 $internalKey = $prefix . $normalizedKey;
346 $ttl = $options->getTtl();
348 if (!xcache_set($internalKey, $value, $ttl)) {
349 $type = is_object($value) ?
get_class($value) : gettype($value);
350 throw new Exception\
RuntimeException(
351 "xcache_set('{$internalKey}', <{$type}>, {$ttl}) failed"
359 * Internal method to remove an item.
361 * @param string $normalizedKey
363 * @throws Exception\ExceptionInterface
365 protected function internalRemoveItem(& $normalizedKey)
367 $options = $this->getOptions();
368 $namespace = $options->getNamespace();
369 $prefix = ($namespace === '') ?
'' : $namespace . $options->getNamespaceSeparator();
370 $internalKey = $prefix . $normalizedKey;
372 return xcache_unset($internalKey);
376 * Internal method to increment an item.
378 * @param string $normalizedKey
380 * @return int|bool The new value on success, false on failure
381 * @throws Exception\ExceptionInterface
383 protected function internalIncrementItem(& $normalizedKey, & $value)
385 $options = $this->getOptions();
386 $namespace = $options->getNamespace();
387 $prefix = ($namespace === '') ?
'' : $namespace . $options->getNamespaceSeparator();
388 $internalKey = $prefix . $normalizedKey;
389 $ttl = $options->getTtl();
390 $value = (int) $value;
392 return xcache_inc($internalKey, $value, $ttl);
396 * Internal method to decrement an item.
398 * @param string $normalizedKey
400 * @return int|bool The new value on success, false on failure
401 * @throws Exception\ExceptionInterface
403 protected function internalDecrementItem(& $normalizedKey, & $value)
405 $options = $this->getOptions();
406 $namespace = $options->getNamespace();
407 $prefix = ($namespace === '') ?
'' : $namespace . $options->getNamespaceSeparator();
408 $internalKey = $prefix . $normalizedKey;
409 $ttl = $options->getTtl();
410 $value = (int) $value;
412 return xcache_dec($internalKey, $value, $ttl);
418 * Internal method to get capabilities of this adapter
420 * @return Capabilities
422 protected function internalGetCapabilities()
424 if ($this->capabilities
=== null) {
425 $marker = new stdClass();
426 $capabilities = new Capabilities(
430 'supportedDatatypes' => array(
437 'object' => 'object',
440 'supportedMetadata' => array(
442 'size', 'refcount', 'hits',
443 'ctime', 'atime', 'hvalue',
446 'maxTtl' => (int)ini_get('xcache.var_maxttl'),
449 'useRequestTime' => true,
450 'expiredRead' => false,
451 'maxKeyLength' => 5182,
452 'namespaceIsPrefix' => true,
453 'namespaceSeparator' => $this->getOptions()->getNamespaceSeparator(),
457 // update namespace separator on change option
458 $this->getEventManager()->attach('option', function ($event) use ($capabilities, $marker) {
459 $params = $event->getParams();
461 if (isset($params['namespace_separator'])) {
462 $capabilities->setNamespaceSeparator($marker, $params['namespace_separator']);
466 $this->capabilities
= $capabilities;
467 $this->capabilityMarker
= $marker;
470 return $this->capabilities
;
476 * Init authentication before calling admin functions
480 protected function initAdminAuth()
482 $options = $this->getOptions();
484 if ($options->getAdminAuth()) {
485 $adminUser = $options->getAdminUser();
486 $adminPass = $options->getAdminPass();
488 // backup HTTP authentication properties
489 if (isset($_SERVER['PHP_AUTH_USER'])) {
490 $this->backupAuth
['PHP_AUTH_USER'] = $_SERVER['PHP_AUTH_USER'];
492 if (isset($_SERVER['PHP_AUTH_PW'])) {
493 $this->backupAuth
['PHP_AUTH_PW'] = $_SERVER['PHP_AUTH_PW'];
496 // set authentication
497 $_SERVER['PHP_AUTH_USER'] = $adminUser;
498 $_SERVER['PHP_AUTH_PW'] = $adminPass;
503 * Reset authentication after calling admin functions
507 protected function resetAdminAuth()
509 unset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
510 $_SERVER = $this->backupAuth +
$_SERVER;
511 $this->backupAuth
= array();
515 * Normalize metadata to work with XCache
517 * @param array $metadata
519 protected function normalizeMetadata(array & $metadata)
521 $metadata['internal_key'] = &$metadata['name'];
522 unset($metadata['name']);