[COMPOSER] Add new php-ffmpeg package

This commit is contained in:
t3nma
2020-08-07 23:42:38 +01:00
parent 0a6bb5190f
commit c527ad0803
8874 changed files with 1090008 additions and 154 deletions

View File

@@ -0,0 +1,381 @@
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 2.8.3 - 2019-08-28
### Added
- Nothing.
### Changed
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#184](https://github.com/zendframework/zend-cache/pull/184) fixes
an issue with SimpleCacheDecorator where elements were deleted
after creation. Wrong TTL was set instead of using default value
from options.
- [#182](https://github.com/zendframework/zend-cache/pull/182) fixes
a typo in variable name within the `ExtMongoDbResourceManager::getResource`
method which prevented using custom db name when using that adapter.
## 2.8.2 - 2018-05-01
### Added
- Nothing.
### Changed
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#168](https://github.com/zendframework/zend-cache/pull/168) fixes a typo in a variable name within the `Filesystem::setTags()` method which
prevented clearing of tags when using that adapter.
## 2.8.1 - 2018-04-26
### Added
- Nothing.
### Changed
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#165](https://github.com/zendframework/zend-cache/issues/165) fixes an issue
with the memcached adapter ensuring that retrieval returns boolean false when
unable to retrieve the requested item.
## 2.8.0 - 2018-04-24
### Added
- [#148](https://github.com/zendframework/zend-cache/pull/148) adds support for PHP 7.1 and 7.2.
- [#46](https://github.com/zendframework/zend-cache/issues/46), [#155](https://github.com/zendframework/zend-cache/issues/155), and [#161](https://github.com/zendframework/zend-cache/issues/161) add support for [PSR-6](https://www.php-fig.org/psr/psr-6/) (Caching Interface).
They provides an implementation of `Psr\Cache\CacheItemPoolInterface` via
`Zend\Cache\Psr\CacheItemPool\CacheItemPoolDecorator`, which accepts a
`Zend\Cache\Storage\StorageInterface` instance to its constructor, and proxies
the various PSR-6 methods to it. It also provides a
`Psr\Cache\CacheItemInterface` implementation via `Zend\Cache\Psr\CacheItemPool\CacheItem`,
which provides a value object for both introspecting cache fetch results, as
well as providing values to cache.
- [#152](https://github.com/zendframework/zend-cache/pull/152), [#155](https://github.com/zendframework/zend-cache/pull/155), [#159](https://github.com/zendframework/zend-cache/pull/159), and [#161](https://github.com/zendframework/zend-cache/issues/161)
add an adapter providing [PSR-16](https://www.php-fig.org/psr/psr-16/) (Caching Library Interface) support.
The new class, `Zend\Cache\Psr\SimpleCache\SimpleCacheDecorator`, accepts a
`Zend\Cache\Storage\StorageInterface` instance to its constructor, and proxies
the various PSR-16 methods to it.
- [#154](https://github.com/zendframework/zend-cache/pull/154) adds an ext-mongodb adapter, `Zend\Cache\Storage\Adapter\ExtMongoDb`.
You may use the `StorageFactory` to create an instance using either the fully qualified class
name as the adapter name, or the strings `ext_mongo_db` or `ExtMongoDB` (or most variations
on case of the latter string). The options it accepts are the same as for the existing
`Zend\Cache\Storage\Adapter\MongoDb`, and it provides the same capabilities. The adapter
requires the mongodb/mongodb package to operate.
- [#120](https://github.com/zendframework/zend-cache/pull/120) adds the ability to configure alternate file suffixes for both
cache and tag cache files within the Filesystem adapter. Use the `suffix` and `tag_suffix`
options to set them; they will default to `dat` and `tag`, respectively.
- [#79](https://github.com/zendframework/zend-cache/issues/79)
Add capability for the "lock-on-expire" feature (úsed by Zend Data Cache)
### Changed
- [#116](https://github.com/zendframework/zend-cache/pull/116) adds docblock method chaining consistency.
### Deprecated
- Nothing.
### Removed
- [#101](https://github.com/zendframework/zend-cache/pull/101) removes support for PHP 5.5.
- [#148](https://github.com/zendframework/zend-cache/pull/148) removes support for HHVM.
### Fixed
- [#151](https://github.com/zendframework/zend-cache/pull/151) adds logic to normalize options before creating the underlying Redis
resource when using a Redis adapter, fixing issues when using an array with the server and port
to use for connecting to the server.
- [#151](https://github.com/zendframework/zend-cache/pull/151) adds logic to prevent changing the underlying resource within Redis adapter instances.
- [#150](https://github.com/zendframework/zend-cache/pull/150) fixes an issue with how CAS tokens are handled when using the memcached adapter.
- [#61](https://github.com/zendframework/zend-cache/pull/61) sets the Zend Data Cache minTtl value to 1.
- [#147](https://github.com/zendframework/zend-cache/pull/147) fixes the Redis extension by ensuring it casts the results of `exists()` to a
boolean when testing if the storage contains an item.
- [#146](https://github.com/zendframework/zend-cache/pull/146) fixes several methods to change `@return` annotations to `@throws` where applicable.
- [#134](https://github.com/zendframework/zend-cache/pull/134) adds a missing import statement for `Traversable` within the `AdapterOptions` class.
- [#128](https://github.com/zendframework/zend-cache/pull/128)
Fixed incorrect variable usage in MongoDbResourceManager
## 2.7.2 - 2016-12-16
### Added
- [#124](https://github.com/zendframework/zend-cache/pull/124)
New coding standard
### Deprecated
- [#123](https://github.com/zendframework/zend-cache/pull/123)
Deprecate capability "expiredRead".
It's basically providing the same information as staticTtl but from a wrong PoV
### Removed
- Nothing.
### Fixed
- [#122](https://github.com/zendframework/zend-cache/pull/122)
Fixed redis doc for lib_options (not lib_option)
- [#118](https://github.com/zendframework/zend-cache/pull/118)
fixed redis tests in case running with different server
- [#119](https://github.com/zendframework/zend-cache/pull/119)
Redis: Don't call method Redis::info() every time
- [#113](https://github.com/zendframework/zend-cache/pull/113)
Travis: Moved coverage reporting to latest env
- [#114](https://github.com/zendframework/zend-cache/pull/114)
Travis: removed fast_finish flag
- [#107](https://github.com/zendframework/zend-cache/issues/107)
fixed redis server version test in Redis::internalGetMetadata()
- [#111](https://github.com/zendframework/zend-cache/pull/111)
Fixed typo in storage adapter doc
- [#102](https://github.com/zendframework/zend-cache/pull/102)
filesystem: fixes a lot of possible race conditions
## 2.7.1 - 2016-05-12
### Added
- [#35](https://github.com/zendframework/zend-cache/pull/35)
Added benchmarks using PHPBench
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#76](https://github.com/zendframework/zend-cache/pull/76)
ZendServer: fixed return null on missing item
- [#88](https://github.com/zendframework/zend-cache/issues/88)
Redis: fixed segfault on storing NULL and fixed supported datatypes capabilities
- [#95](https://github.com/zendframework/zend-cache/issues/95)
don't try to unserialize missing items
- [#66](https://github.com/zendframework/zend-cache/issues/66)
fixed Memcached::internalSetItems in PHP-7 by reducing variables by reference
- [#57](https://github.com/zendframework/zend-cache/pull/57)
Memcached: HHVM compatibility and reduced duplicated code
- [#91](https://github.com/zendframework/zend-cache/pull/91)
fixed that order of adapter options may cause exception
- [#98](https://github.com/zendframework/zend-cache/pull/98) updates the plugin
manager alias list to ensure all adapter name permutations commonly used are
accepted.
## 2.7.0 - 2016-04-12
### Added
- [#59](https://github.com/zendframework/zend-cache/pull/59)
XCache >= 3.1.0 works in CLI mode
- [#23](https://github.com/zendframework/zend-cache/issues/23)
[#47](https://github.com/zendframework/zend-cache/issues/47)
Added an Apcu storage adapter as future replacement for Apc
- [#63](https://github.com/zendframework/zend-cache/pull/63)
Implemented ClearByNamespaceInterface in Stoage\Adapter\Redis
- [#94](https://github.com/zendframework/zend-cache/pull/94) adds factories for
each of the `PatternPluginManager`, `AdapterPluginManager`, and storage
`PluginManager`.
- [#94](https://github.com/zendframework/zend-cache/pull/94) exposes the package
as a standalone config-provider / ZF component, by adding:
- `Zend\Cache\ConfigProvider`, which enables the
`StorageCacheAbstractServiceFactory`, and maps factories for all plugin
managers.
- `Zend\Cache\Module`, which does the same, for zend-mvc contexts.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#44](https://github.com/zendframework/zend-cache/issues/44)
Filesystem: fixed race condition in method clearByTags
- [#59](https://github.com/zendframework/zend-cache/pull/59)
XCache: fixed broken internalSetItem() with empty namespace
- [#58](https://github.com/zendframework/zend-cache/issues/58)
XCache: Fatal error storing objects
- [#94](https://github.com/zendframework/zend-cache/pull/94) updates the
`PatternPluginManager` to accept `$options` to `get()` and `build()`, cast
them to a `PatternOptions` instance, and inject them into the generated plugin
instance. This change allows better standalone usage of the plugin manager.
- [#94](https://github.com/zendframework/zend-cache/pull/94) updates the
`StorageCacheFactory` and `StorageCacheAbstractServiceFactory` to seed the
`StorageFactory` with the storage plugin manager and/or adapter plugin manager
as pulled from the provided container, if present. This change enables re-use
of pre-configured plugin managers (e.g., those seeded with custom plugins
and/or adapters).
## 2.6.1 - 2016-02-12
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#73](https://github.com/zendframework/zend-cache/pull/73) fixes how the
`EventManager` instance is lazy-instantiated in
`Zend\Cache\Storage\Adapter\AbstractAdapter::getEventManager()`. In 2.6.0, it
was using the v3-specific syntax; it now uses syntax compatible with both v2
and v3.
## 2.6.0 - 2016-02-11
### Added
- [#70](https://github.com/zendframework/zend-cache/pull/70) adds, revises, and
publishes the documentation to https://zendframework.github.io/zend-cache/
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#22](https://github.com/zendframework/zend-cache/pull/22),
[#64](https://github.com/zendframework/zend-cache/pull/64),
[#68](https://github.com/zendframework/zend-cache/pull/68), and
[#69](https://github.com/zendframework/zend-cache/pull/69) update the
component to be forwards-compatible with zend-eventmanager,
zend-servicemanager, and zend-stdlib v3.
- [#31](https://github.com/zendframework/zend-cache/issues/31)
Check Documentation Code Blocks
- [#53](https://github.com/zendframework/zend-cache/pull/53)
fixed seg fault in redis adapter on PHP 7
- [#50](https://github.com/zendframework/zend-cache/issues/50)
fixed APC tests not running on travis-ci since apcu-5 was released
- [#36](https://github.com/zendframework/zend-cache/pull/36)
fixed AbstractAdapter::internalDecrementItems
- [#38](https://github.com/zendframework/zend-cache/pull/38)
better test coverage of AbstractAdapter
- [#45](https://github.com/zendframework/zend-cache/pull/45)
removed unused internal function Filesystem::readInfoFile
- [#25](https://github.com/zendframework/zend-cache/pull/25)
MongoDd: fixed expiration support and removed duplicated tests
- [#40](https://github.com/zendframework/zend-cache/pull/40)
Fixed TTL support of `Redis::addItem`
- [#18](https://github.com/zendframework/zend-cache/issues/18)
Fixed `Redis::getCapabilities` and `RedisResourceManager::getMajorVersion`
if resource wasn't initialized before
## 2.5.3 - 2015-09-15
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#15](https://github.com/zendframework/zend-cache/pull/15) fixes an issue
observed on HHVM when merging a list of memcached servers to add to the
storage resource.
- [#17](https://github.com/zendframework/zend-cache/pull/17) Composer: moved
`zendframework/zend-serializer` from `require` to `require-dev` as using the
serializer is optional.
- A fix was provided for [ZF2015-07](http://framework.zend.com/security/advisory/ZF2015-07),
ensuring that any directories or files created by the component use umask 0002
in order to prevent arbitrary local execution and/or local privilege
escalation.
## 2.5.2 - 2015-07-16
### Added
- [#10](https://github.com/zendframework/zend-cache/pull/10) adds TTL support
for the Redis adapter.
- [#6](https://github.com/zendframework/zend-cache/pull/6) adds more suggestions
to the `composer.json` for PHP extensions supported by storage adapters.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#9](https://github.com/zendframework/zend-cache/pull/9) fixes an issue when
connecting to a Redis instance with the `persistent_id` option.

View File

@@ -0,0 +1,27 @@
Copyright (c) 2005-2018, Zend Technologies USA, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name of Zend Technologies USA, Inc. nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,24 @@
# zend-cache
[![Build Status](https://secure.travis-ci.org/zendframework/zend-cache.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-cache)
[![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-cache/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-cache?branch=master)
`Zend\Cache` provides a general cache system for PHP. The `Zend\Cache` component
is able to cache different patterns (class, object, output, etc) using different
storage adapters (DB, File, Memcache, etc).
- File issues at https://github.com/zendframework/zend-cache/issues
- Documentation is at https://docs.zendframework.com/zend-cache/
## Benchmarks
We provide scripts for benchmarking zend-cache using the
[PHPBench](https://github.com/phpbench/phpbench) framework; these can be
found in the `benchmark/` directory.
To execute the benchmarks you can run the following command:
```bash
$ vendor/bin/phpbench run --report=aggregate
```

View File

@@ -0,0 +1,17 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
use Zend\Cache\PatternPluginManager;
use Zend\ServiceManager\ServiceManager;
call_user_func(function () {
$target = method_exists(ServiceManager::class, 'configure')
? PatternPluginManager\PatternPluginManagerV3Polyfill::class
: PatternPluginManager\PatternPluginManagerV2Polyfill::class;
class_alias($target, PatternPluginManager::class);
});

View File

@@ -0,0 +1,97 @@
{
"name": "zendframework/zend-cache",
"description": "Caching implementation with a variety of storage options, as well as codified caching strategies for callbacks, classes, and output",
"license": "BSD-3-Clause",
"keywords": [
"zendframework",
"zf",
"cache",
"psr-6",
"psr-16"
],
"support": {
"docs": "https://docs.zendframework.com/zend-cache/",
"issues": "https://github.com/zendframework/zend-cache/issues",
"source": "https://github.com/zendframework/zend-cache",
"rss": "https://github.com/zendframework/zend-cache/releases.atom",
"chat": "https://zendframework-slack.herokuapp.com",
"forum": "https://discourse.zendframework.com/c/questions/components"
},
"require": {
"php": "^5.6 || ^7.0",
"psr/cache": "^1.0",
"psr/simple-cache": "^1.0",
"zendframework/zend-eventmanager": "^2.6.3 || ^3.2",
"zendframework/zend-servicemanager": "^2.7.8 || ^3.3",
"zendframework/zend-stdlib": "^2.7.7 || ^3.1"
},
"require-dev": {
"cache/integration-tests": "^0.16",
"phpbench/phpbench": "^0.13",
"phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2",
"zendframework/zend-coding-standard": "~1.0.0",
"zendframework/zend-serializer": "^2.6",
"zendframework/zend-session": "^2.7.4"
},
"suggest": {
"zendframework/zend-serializer": "Zend\\Serializer component",
"zendframework/zend-session": "Zend\\Session component",
"ext-apc": "APC or compatible extension, to use the APC storage adapter",
"ext-apcu": "APCU >= 5.1.0, to use the APCu storage adapter",
"ext-dba": "DBA, to use the DBA storage adapter",
"ext-memcache": "Memcache >= 2.0.0 to use the Memcache storage adapter",
"ext-memcached": "Memcached >= 1.0.0 to use the Memcached storage adapter",
"ext-mongo": "Mongo, to use MongoDb storage adapter",
"ext-mongodb": "MongoDB, to use the ExtMongoDb storage adapter",
"ext-redis": "Redis, to use Redis storage adapter",
"ext-wincache": "WinCache, to use the WinCache storage adapter",
"ext-xcache": "XCache, to use the XCache storage adapter",
"mongodb/mongodb": "Required for use with the ext-mongodb adapter",
"mongofill/mongofill": "Alternative to ext-mongo - a pure PHP implementation designed as a drop in replacement"
},
"autoload": {
"files": [
"autoload/patternPluginManagerPolyfill.php"
],
"psr-4": {
"Zend\\Cache\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"ZendTest\\Cache\\": "test/",
"ZendBench\\Cache\\": "benchmark/"
},
"files": [
"test/autoload.php"
]
},
"config": {
"process-timeout": 600,
"sort-packages": true
},
"provide": {
"psr/cache-implementation": "1.0",
"psr/simple-cache-implementation": "1.0"
},
"extra": {
"branch-alias": {
"dev-master": "2.8.x-dev",
"dev-develop": "2.9.x-dev"
},
"zf": {
"component": "Zend\\Cache",
"config-provider": "Zend\\Cache\\ConfigProvider"
}
},
"scripts": {
"check": [
"@cs-check",
"@test"
],
"cs-check": "phpcs",
"cs-fix": "phpcbf",
"test": "phpunit --colors=always",
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* @link http://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache;
class ConfigProvider
{
/**
* Return default configuration for zend-cache.
*
* @return array
*/
public function __invoke()
{
return [
'dependencies' => $this->getDependencyConfig(),
];
}
/**
* Return default service mappings for zend-cache.
*
* @return array
*/
public function getDependencyConfig()
{
return [
'abstract_factories' => [
Service\StorageCacheAbstractServiceFactory::class,
],
'factories' => [
PatternPluginManager::class => Service\PatternPluginManagerFactory::class,
Storage\AdapterPluginManager::class => Service\StorageAdapterPluginManagerFactory::class,
Storage\PluginManager::class => Service\StoragePluginManagerFactory::class,
],
];
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class BadMethodCallException extends \BadMethodCallException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class ExtensionNotLoadedException extends RuntimeException
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class LogicException extends \LogicException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class MissingDependencyException extends RuntimeException
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class MissingKeyException extends RuntimeException
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class OutOfSpaceException extends \OverflowException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class UnexpectedValueException extends \UnexpectedValueException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Exception;
class UnsupportedMethodCallException extends \BadMethodCallException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* @link http://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache;
class Module
{
/**
* Return default zend-cache configuration for zend-mvc context.
*
* @return array
*/
public function getConfig()
{
$provider = new ConfigProvider();
return [
'service_manager' => $provider->getDependencyConfig(),
];
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
use Zend\Cache\Exception;
abstract class AbstractPattern implements PatternInterface
{
/**
* @var PatternOptions
*/
protected $options;
/**
* Set pattern options
*
* @param PatternOptions $options
* @return AbstractPattern Provides a fluent interface
* @throws Exception\InvalidArgumentException
*/
public function setOptions(PatternOptions $options)
{
if (! $options instanceof PatternOptions) {
$options = new PatternOptions($options);
}
$this->options = $options;
return $this;
}
/**
* Get all pattern options
*
* @return PatternOptions
*/
public function getOptions()
{
if (null === $this->options) {
$this->setOptions(new PatternOptions());
}
return $this->options;
}
}

View File

@@ -0,0 +1,202 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
use Zend\Cache\Exception;
use Zend\Stdlib\ErrorHandler;
class CallbackCache extends AbstractPattern
{
/**
* Set options
*
* @param PatternOptions $options
* @return CallbackCache Provides a fluent interface
* @throws Exception\InvalidArgumentException if missing storage option
*/
public function setOptions(PatternOptions $options)
{
parent::setOptions($options);
if (! $options->getStorage()) {
throw new Exception\InvalidArgumentException("Missing option 'storage'");
}
return $this;
}
/**
* Call the specified callback or get the result from cache
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
* @return mixed Result
* @throws Exception\RuntimeException if invalid cached data
* @throws \Exception
*/
public function call($callback, array $args = [])
{
$options = $this->getOptions();
$storage = $options->getStorage();
$success = null;
$key = $this->generateCallbackKey($callback, $args);
$result = $storage->getItem($key, $success);
if ($success) {
if (! array_key_exists(0, $result)) {
throw new Exception\RuntimeException("Invalid cached data for key '{$key}'");
}
echo isset($result[1]) ? $result[1] : '';
return $result[0];
}
$cacheOutput = $options->getCacheOutput();
if ($cacheOutput) {
ob_start();
ob_implicit_flush(0);
}
// TODO: do not cache on errors using [set|restore]_error_handler
try {
if ($args) {
$ret = call_user_func_array($callback, $args);
} else {
$ret = call_user_func($callback);
}
} catch (\Exception $e) {
if ($cacheOutput) {
ob_end_flush();
}
throw $e;
}
if ($cacheOutput) {
$data = [$ret, ob_get_flush()];
} else {
$data = [$ret];
}
$storage->setItem($key, $data);
return $ret;
}
/**
* function call handler
*
* @param string $function Function name to call
* @param array $args Function arguments
* @return mixed
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function __call($function, array $args)
{
return $this->call($function, $args);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
* @return string
* @throws Exception\RuntimeException
* @throws Exception\InvalidArgumentException
*/
public function generateKey($callback, array $args = [])
{
return $this->generateCallbackKey($callback, $args);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
* @throws Exception\RuntimeException if callback not serializable
* @throws Exception\InvalidArgumentException if invalid callback
* @return string
*/
protected function generateCallbackKey($callback, array $args)
{
if (! is_callable($callback, false, $callbackKey)) {
throw new Exception\InvalidArgumentException('Invalid callback');
}
// functions, methods and classnames are case-insensitive
$callbackKey = strtolower($callbackKey);
// generate a unique key of object callbacks
if (is_object($callback)) {
// Closures & __invoke
$object = $callback;
} elseif (isset($callback[0])) {
// array($object, 'method')
$object = $callback[0];
}
if (isset($object)) {
ErrorHandler::start();
try {
$serializedObject = serialize($object);
} catch (\Exception $e) {
ErrorHandler::stop();
throw new Exception\RuntimeException("Can't serialize callback: see previous exception", 0, $e);
}
$error = ErrorHandler::stop();
if (! $serializedObject) {
throw new Exception\RuntimeException(
sprintf('Cannot serialize callback%s', ($error ? ': ' . $error->getMessage() : '')),
0,
$error
);
}
$callbackKey .= $serializedObject;
}
return md5($callbackKey) . $this->generateArgumentsKey($args);
}
/**
* Generate a unique key of the argument part.
*
* @param array $args
* @throws Exception\RuntimeException
* @return string
*/
protected function generateArgumentsKey(array $args)
{
if (! $args) {
return '';
}
ErrorHandler::start();
try {
$serializedArgs = serialize(array_values($args));
} catch (\Exception $e) {
ErrorHandler::stop();
throw new Exception\RuntimeException("Can't serialize arguments: see previous exception", 0, $e);
}
$error = ErrorHandler::stop();
if (! $serializedArgs) {
throw new Exception\RuntimeException(
sprintf('Cannot serialize arguments%s', ($error ? ': ' . $error->getMessage() : '')),
0,
$error
);
}
return md5($serializedArgs);
}
}

View File

@@ -0,0 +1,387 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
use Zend\Cache\Exception;
use Zend\Stdlib\ErrorHandler;
class CaptureCache extends AbstractPattern
{
/**
* Start the cache
*
* @param string $pageId Page identifier
* @return void
*/
public function start($pageId = null)
{
if ($pageId === null) {
$pageId = $this->detectPageId();
}
ob_start(function ($content) use ($pageId) {
$this->set($content, $pageId);
// http://php.net/manual/function.ob-start.php
// -> If output_callback returns FALSE original input is sent to the browser.
return false;
});
ob_implicit_flush(0);
}
/**
* Write content to page identity
*
* @param string $content
* @param null|string $pageId
* @throws Exception\LogicException
*/
public function set($content, $pageId = null)
{
$publicDir = $this->getOptions()->getPublicDir();
if ($publicDir === null) {
throw new Exception\LogicException("Option 'public_dir' no set");
}
if ($pageId === null) {
$pageId = $this->detectPageId();
}
$path = $this->pageId2Path($pageId);
$file = $path . DIRECTORY_SEPARATOR . $this->pageId2Filename($pageId);
$this->createDirectoryStructure($publicDir . DIRECTORY_SEPARATOR . $path);
$this->putFileContent($publicDir . DIRECTORY_SEPARATOR . $file, $content);
}
/**
* Get from cache
*
* @param null|string $pageId
* @return string|null
* @throws Exception\LogicException
* @throws Exception\RuntimeException
*/
public function get($pageId = null)
{
$publicDir = $this->getOptions()->getPublicDir();
if ($publicDir === null) {
throw new Exception\LogicException("Option 'public_dir' no set");
}
if ($pageId === null) {
$pageId = $this->detectPageId();
}
$file = $publicDir
. DIRECTORY_SEPARATOR . $this->pageId2Path($pageId)
. DIRECTORY_SEPARATOR . $this->pageId2Filename($pageId);
if (file_exists($file)) {
ErrorHandler::start();
$content = file_get_contents($file);
$error = ErrorHandler::stop();
if ($content === false) {
throw new Exception\RuntimeException("Failed to read cached pageId '{$pageId}'", 0, $error);
}
return $content;
}
}
/**
* Checks if a cache with given id exists
*
* @param null|string $pageId
* @throws Exception\LogicException
* @return bool
*/
public function has($pageId = null)
{
$publicDir = $this->getOptions()->getPublicDir();
if ($publicDir === null) {
throw new Exception\LogicException("Option 'public_dir' no set");
}
if ($pageId === null) {
$pageId = $this->detectPageId();
}
$file = $publicDir
. DIRECTORY_SEPARATOR . $this->pageId2Path($pageId)
. DIRECTORY_SEPARATOR . $this->pageId2Filename($pageId);
return file_exists($file);
}
/**
* Remove from cache
*
* @param null|string $pageId
* @throws Exception\LogicException
* @throws Exception\RuntimeException
* @return bool
*/
public function remove($pageId = null)
{
$publicDir = $this->getOptions()->getPublicDir();
if ($publicDir === null) {
throw new Exception\LogicException("Option 'public_dir' no set");
}
if ($pageId === null) {
$pageId = $this->detectPageId();
}
$file = $publicDir
. DIRECTORY_SEPARATOR . $this->pageId2Path($pageId)
. DIRECTORY_SEPARATOR . $this->pageId2Filename($pageId);
if (file_exists($file)) {
ErrorHandler::start();
$res = unlink($file);
$err = ErrorHandler::stop();
if (! $res) {
throw new Exception\RuntimeException("Failed to remove cached pageId '{$pageId}'", 0, $err);
}
return true;
}
return false;
}
/**
* Clear cached pages matching glob pattern
*
* @param string $pattern
* @throws Exception\LogicException
*/
public function clearByGlob($pattern = '**')
{
$publicDir = $this->getOptions()->getPublicDir();
if ($publicDir === null) {
throw new Exception\LogicException("Option 'public_dir' no set");
}
$it = new \GlobIterator(
$publicDir . '/' . $pattern,
\GlobIterator::CURRENT_AS_SELF | \GlobIterator::SKIP_DOTS | \GlobIterator::UNIX_PATHS
);
foreach ($it as $pathname => $entry) {
if ($entry->isFile()) {
unlink($pathname);
}
}
}
/**
* Determine the page to save from the request
*
* @throws Exception\RuntimeException
* @return string
*/
protected function detectPageId()
{
if (! isset($_SERVER['REQUEST_URI'])) {
throw new Exception\RuntimeException("Can't auto-detect current page identity");
}
return $_SERVER['REQUEST_URI'];
}
/**
* Get filename for page id
*
* @param string $pageId
* @return string
*/
protected function pageId2Filename($pageId)
{
if (substr($pageId, -1) === '/') {
return $this->getOptions()->getIndexFilename();
}
return basename($pageId);
}
/**
* Get path for page id
*
* @param string $pageId
* @return string
*/
protected function pageId2Path($pageId)
{
if (substr($pageId, -1) == '/') {
$path = rtrim($pageId, '/');
} else {
$path = dirname($pageId);
}
// convert requested "/" to the valid local directory separator
if ('/' != DIRECTORY_SEPARATOR) {
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
}
return $path;
}
/**
* Write content to a file
*
* @param string $file File complete path
* @param string $data Data to write
* @return void
* @throws Exception\RuntimeException
*/
protected function putFileContent($file, $data)
{
$options = $this->getOptions();
$locking = $options->getFileLocking();
$perm = $options->getFilePermission();
$umask = $options->getUmask();
if ($umask !== false && $perm !== false) {
$perm = $perm & ~$umask;
}
ErrorHandler::start();
$umask = ($umask !== false) ? umask($umask) : false;
$rs = file_put_contents($file, $data, $locking ? LOCK_EX : 0);
if ($umask) {
umask($umask);
}
if ($rs === false) {
$err = ErrorHandler::stop();
throw new Exception\RuntimeException("Error writing file '{$file}'", 0, $err);
}
if ($perm !== false && ! chmod($file, $perm)) {
$oct = decoct($perm);
$err = ErrorHandler::stop();
throw new Exception\RuntimeException("chmod('{$file}', 0{$oct}) failed", 0, $err);
}
ErrorHandler::stop();
}
/**
* Creates directory if not already done.
*
* @param string $pathname
* @return void
* @throws Exception\RuntimeException
*/
protected function createDirectoryStructure($pathname)
{
// Directory structure already exists
if (file_exists($pathname)) {
return;
}
$options = $this->getOptions();
$perm = $options->getDirPermission();
$umask = $options->getUmask();
if ($umask !== false && $perm !== false) {
$perm = $perm & ~$umask;
}
ErrorHandler::start();
if ($perm === false) {
// built-in mkdir function is enough
$umask = ($umask !== false) ? umask($umask) : false;
$res = mkdir($pathname, ($perm !== false) ? $perm : 0775, true);
if ($umask !== false) {
umask($umask);
}
if (! $res) {
$oct = ($perm === false) ? '775' : decoct($perm);
$err = ErrorHandler::stop();
throw new Exception\RuntimeException("mkdir('{$pathname}', 0{$oct}, true) failed", 0, $err);
}
if ($perm !== false && ! chmod($pathname, $perm)) {
$oct = decoct($perm);
$err = ErrorHandler::stop();
throw new Exception\RuntimeException("chmod('{$pathname}', 0{$oct}) failed", 0, $err);
}
} else {
// built-in mkdir function sets permission together with current umask
// which doesn't work well on multo threaded webservers
// -> create directories one by one and set permissions
// find existing path and missing path parts
$parts = [];
$path = $pathname;
while (! file_exists($path)) {
array_unshift($parts, basename($path));
$nextPath = dirname($path);
if ($nextPath === $path) {
break;
}
$path = $nextPath;
}
// make all missing path parts
foreach ($parts as $part) {
$path .= DIRECTORY_SEPARATOR . $part;
// create a single directory, set and reset umask immediately
$umask = ($umask !== false) ? umask($umask) : false;
$res = mkdir($path, ($perm === false) ? 0775 : $perm, false);
if ($umask !== false) {
umask($umask);
}
if (! $res) {
$oct = ($perm === false) ? '775' : decoct($perm);
ErrorHandler::stop();
throw new Exception\RuntimeException(
"mkdir('{$path}', 0{$oct}, false) failed"
);
}
if ($perm !== false && ! chmod($path, $perm)) {
$oct = decoct($perm);
ErrorHandler::stop();
throw new Exception\RuntimeException(
"chmod('{$path}', 0{$oct}) failed"
);
}
}
}
ErrorHandler::stop();
}
/**
* Returns the generated file name.
*
* @param null|string $pageId
* @return string
*/
public function getFilename($pageId = null)
{
if ($pageId === null) {
$pageId = $this->detectPageId();
}
$publicDir = $this->getOptions()->getPublicDir();
$path = $this->pageId2Path($pageId);
$file = $path . DIRECTORY_SEPARATOR . $this->pageId2Filename($pageId);
return $publicDir . $file;
}
}

View File

@@ -0,0 +1,167 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
use Zend\Cache;
use Zend\Cache\Exception;
class ClassCache extends CallbackCache
{
/**
* Set options
*
* @param PatternOptions $options
* @return ClassCache Provides a fluent interface
* @throws Exception\InvalidArgumentException if missing 'class' or 'storage' options
*/
public function setOptions(PatternOptions $options)
{
parent::setOptions($options);
if (! $options->getClass()) {
throw new Exception\InvalidArgumentException("Missing option 'class'");
} elseif (! $options->getStorage()) {
throw new Exception\InvalidArgumentException("Missing option 'storage'");
}
return $this;
}
/**
* Call and cache a class method
*
* @param string $method Method name to call
* @param array $args Method arguments
* @return mixed
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function call($method, array $args = [])
{
$options = $this->getOptions();
$classname = $options->getClass();
$method = strtolower($method);
$callback = $classname . '::' . $method;
$cache = $options->getCacheByDefault();
if ($cache) {
$cache = ! in_array($method, $options->getClassNonCacheMethods());
} else {
$cache = in_array($method, $options->getClassCacheMethods());
}
if (! $cache) {
if ($args) {
return call_user_func_array($callback, $args);
} else {
return $classname::$method();
}
}
return parent::call($callback, $args);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param string $method The method
* @param array $args Callback arguments
* @return string
* @throws Exception\RuntimeException
*/
public function generateKey($method, array $args = [])
{
return $this->generateCallbackKey(
$this->getOptions()->getClass() . '::' . $method,
$args
);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
* @return string
* @throws Exception\RuntimeException
*/
protected function generateCallbackKey($callback, array $args)
{
$callbackKey = md5(strtolower($callback));
$argumentKey = $this->generateArgumentsKey($args);
return $callbackKey . $argumentKey;
}
/**
* Calling a method of the entity.
*
* @param string $method Method name to call
* @param array $args Method arguments
* @return mixed
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function __call($method, array $args)
{
return $this->call($method, $args);
}
/**
* Set a static property
*
* @param string $name
* @param mixed $value
* @return void
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*/
public function __set($name, $value)
{
$class = $this->getOptions()->getClass();
$class::$name = $value;
}
/**
* Get a static property
*
* @param string $name
* @return mixed
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*/
public function __get($name)
{
$class = $this->getOptions()->getClass();
return $class::$name;
}
/**
* Is a static property exists.
*
* @param string $name
* @return bool
*/
public function __isset($name)
{
$class = $this->getOptions()->getClass();
return isset($class::$name);
}
/**
* Unset a static property
*
* @param string $name
* @return void
*/
public function __unset($name)
{
$class = $this->getOptions()->getClass();
unset($class::$name);
}
}

View File

@@ -0,0 +1,284 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
use Zend\Cache\Exception;
class ObjectCache extends CallbackCache
{
/**
* Set options
*
* @param PatternOptions $options
* @return void
* @throws Exception\InvalidArgumentException
*/
public function setOptions(PatternOptions $options)
{
parent::setOptions($options);
if (! $options->getObject()) {
throw new Exception\InvalidArgumentException("Missing option 'object'");
} elseif (! $options->getStorage()) {
throw new Exception\InvalidArgumentException("Missing option 'storage'");
}
}
/**
* Call and cache a class method
*
* @param string $method Method name to call
* @param array $args Method arguments
* @return mixed
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function call($method, array $args = [])
{
$options = $this->getOptions();
$object = $options->getObject();
$method = strtolower($method);
// handle magic methods
switch ($method) {
case '__set':
$property = array_shift($args);
$value = array_shift($args);
$object->{$property} = $value;
if (! $options->getObjectCacheMagicProperties()
|| property_exists($object, $property)
) {
// no caching if property isn't magic
// or caching magic properties is disabled
return;
}
// remove cached __get and __isset
$removeKeys = null;
if (method_exists($object, '__get')) {
$removeKeys[] = $this->generateKey('__get', [$property]);
}
if (method_exists($object, '__isset')) {
$removeKeys[] = $this->generateKey('__isset', [$property]);
}
if ($removeKeys) {
$options->getStorage()->removeItems($removeKeys);
}
return;
case '__get':
$property = array_shift($args);
if (! $options->getObjectCacheMagicProperties()
|| property_exists($object, $property)
) {
// no caching if property isn't magic
// or caching magic properties is disabled
return $object->{$property};
}
array_unshift($args, $property);
return parent::call([$object, '__get'], $args);
case '__isset':
$property = array_shift($args);
if (! $options->getObjectCacheMagicProperties()
|| property_exists($object, $property)
) {
// no caching if property isn't magic
// or caching magic properties is disabled
return isset($object->{$property});
}
return parent::call([$object, '__isset'], [$property]);
case '__unset':
$property = array_shift($args);
unset($object->{$property});
if (! $options->getObjectCacheMagicProperties()
|| property_exists($object, $property)
) {
// no caching if property isn't magic
// or caching magic properties is disabled
return;
}
// remove previous cached __get and __isset calls
$removeKeys = null;
if (method_exists($object, '__get')) {
$removeKeys[] = $this->generateKey('__get', [$property]);
}
if (method_exists($object, '__isset')) {
$removeKeys[] = $this->generateKey('__isset', [$property]);
}
if ($removeKeys) {
$options->getStorage()->removeItems($removeKeys);
}
return;
}
$cache = $options->getCacheByDefault();
if ($cache) {
$cache = ! in_array($method, $options->getObjectNonCacheMethods());
} else {
$cache = in_array($method, $options->getObjectCacheMethods());
}
if (! $cache) {
if ($args) {
return call_user_func_array([$object, $method], $args);
}
return $object->{$method}();
}
return parent::call([$object, $method], $args);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param string $method The method
* @param array $args Callback arguments
* @return string
* @throws Exception\RuntimeException
*/
public function generateKey($method, array $args = [])
{
return $this->generateCallbackKey(
[$this->getOptions()->getObject(), $method],
$args
);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
* @return string
* @throws Exception\RuntimeException
*/
protected function generateCallbackKey($callback, array $args = [])
{
$callbackKey = md5($this->getOptions()->getObjectKey() . '::' . strtolower($callback[1]));
$argumentKey = $this->generateArgumentsKey($args);
return $callbackKey . $argumentKey;
}
/**
* Class method call handler
*
* @param string $method Method name to call
* @param array $args Method arguments
* @return mixed
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function __call($method, array $args)
{
return $this->call($method, $args);
}
/**
* Writing data to properties.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it calls __set
* and removes cached data of previous __get and __isset calls.
*
* @param string $name
* @param mixed $value
* @return void
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*/
public function __set($name, $value)
{
return $this->call('__set', [$name, $value]);
}
/**
* Reading data from properties.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it calls __get.
*
* @param string $name
* @return mixed
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*/
public function __get($name)
{
return $this->call('__get', [$name]);
}
/**
* Checking existing properties.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it calls __get.
*
* @param string $name
* @return bool
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*/
public function __isset($name)
{
return $this->call('__isset', [$name]);
}
/**
* Unseting a property.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it removes
* previous cached __isset and __get calls.
*
* @param string $name
* @return void
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*/
public function __unset($name)
{
return $this->call('__unset', [$name]);
}
/**
* Handle casting to string
*
* @return string
* @see http://php.net/manual/language.oop5.magic.php#language.oop5.magic.tostring
*/
public function __toString()
{
return $this->call('__toString');
}
/**
* Handle invoke calls
*
* @return mixed
* @see http://php.net/manual/language.oop5.magic.php#language.oop5.magic.invoke
*/
public function __invoke()
{
return $this->call('__invoke', func_get_args());
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
use Zend\Cache\Exception;
class OutputCache extends AbstractPattern
{
/**
* The key stack
*
* @var array
*/
protected $keyStack = [];
/**
* Set options
*
* @param PatternOptions $options
* @return OutputCache Provides a fluent interface
* @throws Exception\InvalidArgumentException
*/
public function setOptions(PatternOptions $options)
{
parent::setOptions($options);
if (! $options->getStorage()) {
throw new Exception\InvalidArgumentException("Missing option 'storage'");
}
return $this;
}
/**
* if there is a cached item with the given key display it's data and return true
* else start buffering output until end() is called or the script ends.
*
* @param string $key Key
* @throws Exception\MissingKeyException if key is missing
* @return bool
*/
public function start($key)
{
if (($key = (string) $key) === '') {
throw new Exception\MissingKeyException('Missing key to read/write output from cache');
}
$success = null;
$data = $this->getOptions()->getStorage()->getItem($key, $success);
if ($success) {
echo $data;
return true;
}
ob_start();
ob_implicit_flush(0);
$this->keyStack[] = $key;
return false;
}
/**
* Stops buffering output, write buffered data to cache using the given key on start()
* and displays the buffer.
*
* @throws Exception\RuntimeException if output cache not started or buffering not active
* @return bool TRUE on success, FALSE on failure writing to cache
*/
public function end()
{
$key = array_pop($this->keyStack);
if ($key === null) {
throw new Exception\RuntimeException('Output cache not started');
}
$output = ob_get_flush();
if ($output === false) {
throw new Exception\RuntimeException('Output buffering not active');
}
return $this->getOptions()->getStorage()->setItem($key, $output);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
interface PatternInterface
{
/**
* Set pattern options
*
* @param PatternOptions $options
* @return PatternInterface
*/
public function setOptions(PatternOptions $options);
/**
* Get all pattern options
*
* @return PatternOptions
*/
public function getOptions();
}

View File

@@ -0,0 +1,765 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Pattern;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\StorageFactory;
use Zend\Cache\Storage\StorageInterface as Storage;
use Zend\Stdlib\AbstractOptions;
class PatternOptions extends AbstractOptions
{
/**
* Used by:
* - ClassCache
* - ObjectCache
* @var bool
*/
protected $cacheByDefault = true;
/**
* Used by:
* - CallbackCache
* - ClassCache
* - ObjectCache
* @var bool
*/
protected $cacheOutput = true;
/**
* Used by:
* - ClassCache
* @var null|string
*/
protected $class;
/**
* Used by:
* - ClassCache
* @var array
*/
protected $classCacheMethods = [];
/**
* Used by:
* - ClassCache
* @var array
*/
protected $classNonCacheMethods = [];
/**
* Used by:
* - CaptureCache
* @var false|int
*/
protected $umask = false;
/**
* Used by:
* - CaptureCache
* @var false|int
*/
protected $dirPermission = 0700;
/**
* Used by:
* - CaptureCache
* @var false|int
*/
protected $filePermission = 0600;
/**
* Used by:
* - CaptureCache
* @var bool
*/
protected $fileLocking = true;
/**
* Used by:
* - CaptureCache
* @var string
*/
protected $indexFilename = 'index.html';
/**
* Used by:
* - ObjectCache
* @var null|object
*/
protected $object;
/**
* Used by:
* - ObjectCache
* @var bool
*/
protected $objectCacheMagicProperties = false;
/**
* Used by:
* - ObjectCache
* @var array
*/
protected $objectCacheMethods = [];
/**
* Used by:
* - ObjectCache
* @var null|string
*/
protected $objectKey;
/**
* Used by:
* - ObjectCache
* @var array
*/
protected $objectNonCacheMethods = ['__tostring'];
/**
* Used by:
* - CaptureCache
* @var null|string
*/
protected $publicDir;
/**
* Used by:
* - CallbackCache
* - ClassCache
* - ObjectCache
* - OutputCache
* @var null|Storage
*/
protected $storage;
/**
* Constructor
*
* @param array|Traversable|null $options
* @return PatternOptions
* @throws Exception\InvalidArgumentException
*/
public function __construct($options = null)
{
// disable file/directory permissions by default on windows systems
if (stripos(PHP_OS, 'WIN') === 0) {
$this->filePermission = false;
$this->dirPermission = false;
}
parent::__construct($options);
}
/**
* Set flag indicating whether or not to cache by default
*
* Used by:
* - ClassCache
* - ObjectCache
*
* @param bool $cacheByDefault
* @return PatternOptions Provides a fluent interface
*/
public function setCacheByDefault($cacheByDefault)
{
$this->cacheByDefault = $cacheByDefault;
return $this;
}
/**
* Do we cache by default?
*
* Used by:
* - ClassCache
* - ObjectCache
*
* @return bool
*/
public function getCacheByDefault()
{
return $this->cacheByDefault;
}
/**
* Set whether or not to cache output
*
* Used by:
* - CallbackCache
* - ClassCache
* - ObjectCache
*
* @param bool $cacheOutput
* @return PatternOptions Provides a fluent interface
*/
public function setCacheOutput($cacheOutput)
{
$this->cacheOutput = (bool) $cacheOutput;
return $this;
}
/**
* Will we cache output?
*
* Used by:
* - CallbackCache
* - ClassCache
* - ObjectCache
*
* @return bool
*/
public function getCacheOutput()
{
return $this->cacheOutput;
}
/**
* Set class name
*
* Used by:
* - ClassCache
*
* @param string $class
* @throws Exception\InvalidArgumentException
* @return PatternOptions Provides a fluent interface
*/
public function setClass($class)
{
if (! is_string($class)) {
throw new Exception\InvalidArgumentException('Invalid classname provided; must be a string');
}
$this->class = $class;
return $this;
}
/**
* Get class name
*
* Used by:
* - ClassCache
*
* @return null|string
*/
public function getClass()
{
return $this->class;
}
/**
* Set list of method return values to cache
*
* Used by:
* - ClassCache
*
* @param array $classCacheMethods
* @return PatternOptions Provides a fluent interface
*/
public function setClassCacheMethods(array $classCacheMethods)
{
$this->classCacheMethods = $this->recursiveStrtolower($classCacheMethods);
return $this;
}
/**
* Get list of methods from which to cache return values
*
* Used by:
* - ClassCache
*
* @return array
*/
public function getClassCacheMethods()
{
return $this->classCacheMethods;
}
/**
* Set list of method return values NOT to cache
*
* Used by:
* - ClassCache
*
* @param array $classNonCacheMethods
* @return PatternOptions Provides a fluent interface
*/
public function setClassNonCacheMethods(array $classNonCacheMethods)
{
$this->classNonCacheMethods = $this->recursiveStrtolower($classNonCacheMethods);
return $this;
}
/**
* Get list of methods from which NOT to cache return values
*
* Used by:
* - ClassCache
*
* @return array
*/
public function getClassNonCacheMethods()
{
return $this->classNonCacheMethods;
}
/**
* Set directory permission
*
* @param false|int $dirPermission
* @throws Exception\InvalidArgumentException
* @return PatternOptions Provides a fluent interface
*/
public function setDirPermission($dirPermission)
{
if ($dirPermission !== false) {
if (is_string($dirPermission)) {
$dirPermission = octdec($dirPermission);
} else {
$dirPermission = (int) $dirPermission;
}
// validate
if (($dirPermission & 0700) != 0700) {
throw new Exception\InvalidArgumentException(
'Invalid directory permission: need permission to execute, read and write by owner'
);
}
}
$this->dirPermission = $dirPermission;
return $this;
}
/**
* Gets directory permission
*
* @return false|int
*/
public function getDirPermission()
{
return $this->dirPermission;
}
/**
* Set umask
*
* Used by:
* - CaptureCache
*
* @param false|int $umask
* @throws Exception\InvalidArgumentException
* @return PatternOptions Provides a fluent interface
*/
public function setUmask($umask)
{
if ($umask !== false) {
if (is_string($umask)) {
$umask = octdec($umask);
} else {
$umask = (int) $umask;
}
// validate
if ($umask & 0700) {
throw new Exception\InvalidArgumentException(
'Invalid umask: need permission to execute, read and write by owner'
);
}
// normalize
$umask = $umask & ~0002;
}
$this->umask = $umask;
return $this;
}
/**
* Get umask
*
* Used by:
* - CaptureCache
*
* @return false|int
*/
public function getUmask()
{
return $this->umask;
}
/**
* Set whether or not file locking should be used
*
* Used by:
* - CaptureCache
*
* @param bool $fileLocking
* @return PatternOptions Provides a fluent interface
*/
public function setFileLocking($fileLocking)
{
$this->fileLocking = (bool) $fileLocking;
return $this;
}
/**
* Is file locking enabled?
*
* Used by:
* - CaptureCache
*
* @return bool
*/
public function getFileLocking()
{
return $this->fileLocking;
}
/**
* Set file permission
*
* @param false|int $filePermission
* @throws Exception\InvalidArgumentException
* @return PatternOptions Provides a fluent interface
*/
public function setFilePermission($filePermission)
{
if ($filePermission !== false) {
if (is_string($filePermission)) {
$filePermission = octdec($filePermission);
} else {
$filePermission = (int) $filePermission;
}
// validate
if (($filePermission & 0600) != 0600) {
throw new Exception\InvalidArgumentException(
'Invalid file permission: need permission to read and write by owner'
);
} elseif ($filePermission & 0111) {
throw new Exception\InvalidArgumentException(
"Invalid file permission: Files shouldn't be executable"
);
}
}
$this->filePermission = $filePermission;
return $this;
}
/**
* Gets file permission
*
* @return false|int
*/
public function getFilePermission()
{
return $this->filePermission;
}
/**
* Set value for index filename
*
* @param string $indexFilename
* @return PatternOptions Provides a fluent interface
*/
public function setIndexFilename($indexFilename)
{
$this->indexFilename = (string) $indexFilename;
return $this;
}
/**
* Get value for index filename
*
* @return string
*/
public function getIndexFilename()
{
return $this->indexFilename;
}
/**
* Set object to cache
*
* @param mixed $object
* @throws Exception\InvalidArgumentException
* @return PatternOptions Provides a fluent interface
*/
public function setObject($object)
{
if (! is_object($object)) {
throw new Exception\InvalidArgumentException(
sprintf('%s expects an object; received "%s"', __METHOD__, gettype($object))
);
}
$this->object = $object;
return $this;
}
/**
* Get object to cache
*
* @return null|object
*/
public function getObject()
{
return $this->object;
}
/**
* Set flag indicating whether or not to cache magic properties
*
* Used by:
* - ObjectCache
*
* @param bool $objectCacheMagicProperties
* @return PatternOptions Provides a fluent interface
*/
public function setObjectCacheMagicProperties($objectCacheMagicProperties)
{
$this->objectCacheMagicProperties = (bool) $objectCacheMagicProperties;
return $this;
}
/**
* Should we cache magic properties?
*
* Used by:
* - ObjectCache
*
* @return bool
*/
public function getObjectCacheMagicProperties()
{
return $this->objectCacheMagicProperties;
}
/**
* Set list of object methods for which to cache return values
*
* @param array $objectCacheMethods
* @return PatternOptions Provides a fluent interface
* @throws Exception\InvalidArgumentException
*/
public function setObjectCacheMethods(array $objectCacheMethods)
{
$this->objectCacheMethods = $this->normalizeObjectMethods($objectCacheMethods);
return $this;
}
/**
* Get list of object methods for which to cache return values
*
* @return array
*/
public function getObjectCacheMethods()
{
return $this->objectCacheMethods;
}
/**
* Set the object key part.
*
* Used to generate a callback key in order to speed up key generation.
*
* Used by:
* - ObjectCache
*
* @param null|string $objectKey The object key or NULL to use the objects class name
* @return PatternOptions Provides a fluent interface
*/
public function setObjectKey($objectKey)
{
if ($objectKey !== null) {
$this->objectKey = (string) $objectKey;
} else {
$this->objectKey = null;
}
return $this;
}
/**
* Get object key
*
* Used by:
* - ObjectCache
*
* @return string
*/
public function getObjectKey()
{
if ($this->objectKey === null) {
return get_class($this->getObject());
}
return $this->objectKey;
}
/**
* Set list of object methods for which NOT to cache return values
*
* @param array $objectNonCacheMethods
* @return PatternOptions Provides a fluent interface
* @throws Exception\InvalidArgumentException
*/
public function setObjectNonCacheMethods(array $objectNonCacheMethods)
{
$this->objectNonCacheMethods = $this->normalizeObjectMethods($objectNonCacheMethods);
return $this;
}
/**
* Get list of object methods for which NOT to cache return values
*
* @return array
*/
public function getObjectNonCacheMethods()
{
return $this->objectNonCacheMethods;
}
/**
* Set location of public directory
*
* Used by:
* - CaptureCache
*
* @param string $publicDir
* @throws Exception\InvalidArgumentException
* @return PatternOptions Provides a fluent interface
*/
public function setPublicDir($publicDir)
{
$publicDir = (string) $publicDir;
if (! is_dir($publicDir)) {
throw new Exception\InvalidArgumentException(
"Public directory '{$publicDir}' not found or not a directory"
);
} elseif (! is_writable($publicDir)) {
throw new Exception\InvalidArgumentException(
"Public directory '{$publicDir}' not writable"
);
} elseif (! is_readable($publicDir)) {
throw new Exception\InvalidArgumentException(
"Public directory '{$publicDir}' not readable"
);
}
$this->publicDir = rtrim(realpath($publicDir), DIRECTORY_SEPARATOR);
return $this;
}
/**
* Get location of public directory
*
* Used by:
* - CaptureCache
*
* @return null|string
*/
public function getPublicDir()
{
return $this->publicDir;
}
/**
* Set storage adapter
*
* Required for the following Pattern classes:
* - CallbackCache
* - ClassCache
* - ObjectCache
* - OutputCache
*
* @param string|array|Storage $storage
* @return PatternOptions Provides a fluent interface
*/
public function setStorage($storage)
{
$this->storage = $this->storageFactory($storage);
return $this;
}
/**
* Get storage adapter
*
* Used by:
* - CallbackCache
* - ClassCache
* - ObjectCache
* - OutputCache
*
* @return null|Storage
*/
public function getStorage()
{
return $this->storage;
}
/**
* Recursively apply strtolower on all values of an array, and return as a
* list of unique values
*
* @param array $array
* @return array
*/
protected function recursiveStrtolower(array $array)
{
return array_values(array_unique(array_map('strtolower', $array)));
}
/**
* Normalize object methods
*
* Recursively casts values to lowercase, then determines if any are in a
* list of methods not handled, raising an exception if so.
*
* @param array $methods
* @return array
* @throws Exception\InvalidArgumentException
*/
protected function normalizeObjectMethods(array $methods)
{
$methods = $this->recursiveStrtolower($methods);
$intersect = array_intersect(['__set', '__get', '__unset', '__isset'], $methods);
if (! empty($intersect)) {
throw new Exception\InvalidArgumentException(
"Magic properties are handled by option 'cache_magic_properties'"
);
}
return $methods;
}
/**
* Create a storage object from a given specification
*
* @param array|string|Storage $storage
* @throws Exception\InvalidArgumentException
* @return Storage
*/
protected function storageFactory($storage)
{
if (is_array($storage)) {
$storage = StorageFactory::factory($storage);
} elseif (is_string($storage)) {
$storage = StorageFactory::adapterFactory($storage);
} elseif (! ($storage instanceof Storage)) {
throw new Exception\InvalidArgumentException(
'The storage must be an instanceof Zend\Cache\Storage\StorageInterface '
. 'or an array passed to Zend\Cache\Storage::factory '
. 'or simply the name of the storage adapter'
);
}
return $storage;
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache;
use Traversable;
use Zend\Stdlib\ArrayUtils;
use Zend\ServiceManager\ServiceManager;
abstract class PatternFactory
{
/**
* The pattern manager
*
* @var null|PatternPluginManager
*/
protected static $plugins = null;
/**
* Instantiate a cache pattern
*
* @param string|Pattern\PatternInterface $patternName
* @param array|Traversable|Pattern\PatternOptions $options
* @return Pattern\PatternInterface
* @throws Exception\InvalidArgumentException
*/
public static function factory($patternName, $options = [])
{
if ($options instanceof Pattern\PatternOptions) {
$options = $options->toArray();
}
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (! is_array($options)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects an array, Traversable object, or %s\Pattern\PatternOptions object; received "%s"',
__METHOD__,
__NAMESPACE__,
(is_object($options) ? get_class($options) : gettype($options))
));
}
if ($patternName instanceof Pattern\PatternInterface) {
$patternName->setOptions(new Pattern\PatternOptions($options));
return $patternName;
}
return static::getPluginManager()->get($patternName, $options);
}
/**
* Get the pattern plugin manager
*
* @return PatternPluginManager
*/
public static function getPluginManager()
{
if (static::$plugins === null) {
static::$plugins = new PatternPluginManager(new ServiceManager);
}
return static::$plugins;
}
/**
* Set the pattern plugin manager
*
* @param PatternPluginManager $plugins
* @return void
*/
public static function setPluginManager(PatternPluginManager $plugins)
{
static::$plugins = $plugins;
}
/**
* Reset pattern plugin manager to default
*
* @return void
*/
public static function resetPluginManager()
{
static::$plugins = null;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\PatternPluginManager;
use Zend\Cache\Exception;
use Zend\Cache\Pattern;
use Zend\ServiceManager\Exception\InvalidServiceException;
/**
* Trait providing common logic between FormElementManager implementations.
*
* Trait does not define properties, as the properties common between the
* two versions are originally defined in their parent class, causing a
* resolution conflict.
*/
trait PatternPluginManagerTrait
{
/**
* Override build to inject options as PatternOptions instance.
*
* {@inheritDoc}
*/
public function build($plugin, array $options = null)
{
if (empty($options)) {
return parent::build($plugin);
}
$plugin = parent::build($plugin);
$plugin->setOptions(new Pattern\PatternOptions($options));
return $plugin;
}
/**
* Validate the plugin is of the expected type (v3).
*
* Validates against `$instanceOf`.
*
* @param mixed $instance
* @throws InvalidServiceException
*/
public function validate($instance)
{
if (! $instance instanceof $this->instanceOf) {
throw new InvalidServiceException(sprintf(
'%s can only create instances of %s; %s is invalid',
get_class($this),
$this->instanceOf,
(is_object($instance) ? get_class($instance) : gettype($instance))
));
}
}
/**
* Validate the plugin is of the expected type (v2).
*
* Proxies to `validate()`.
*
* @param mixed $plugin
* @throws Exception\RuntimeException if invalid
*/
public function validatePlugin($plugin)
{
try {
$this->validate($plugin);
} catch (InvalidServiceException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\PatternPluginManager;
use Zend\Cache\Pattern;
use Zend\ServiceManager\AbstractPluginManager;
use Zend\ServiceManager\Factory\InvokableFactory;
/**
* zend-servicemanager v2-compatible plugin manager implementation for cache pattern adapters.
*
* Enforces that retrieved adapters are instances of
* Pattern\PatternInterface. Additionally, it registers a number of default
* patterns available.
*/
class PatternPluginManagerV2Polyfill extends AbstractPluginManager
{
use PatternPluginManagerTrait;
protected $aliases = [
'callback' => Pattern\CallbackCache::class,
'Callback' => Pattern\CallbackCache::class,
'capture' => Pattern\CaptureCache::class,
'Capture' => Pattern\CaptureCache::class,
'class' => Pattern\ClassCache::class,
'Class' => Pattern\ClassCache::class,
'object' => Pattern\ObjectCache::class,
'Object' => Pattern\ObjectCache::class,
'output' => Pattern\OutputCache::class,
'Output' => Pattern\OutputCache::class,
];
protected $factories = [
Pattern\CallbackCache::class => InvokableFactory::class,
Pattern\CaptureCache::class => InvokableFactory::class,
Pattern\ClassCache::class => InvokableFactory::class,
Pattern\ObjectCache::class => InvokableFactory::class,
Pattern\OutputCache::class => InvokableFactory::class,
// v2 normalized FQCNs
'zendcachepatterncallbackcache' => InvokableFactory::class,
'zendcachepatterncapturecache' => InvokableFactory::class,
'zendcachepatternclasscache' => InvokableFactory::class,
'zendcachepatternobjectcache' => InvokableFactory::class,
'zendcachepatternoutputcache' => InvokableFactory::class,
];
/**
* Don't share by default
*
* @var bool
*/
protected $shareByDefault = false;
/**
* Don't share by default
*
* @var bool
*/
protected $sharedByDefault = false;
/**
* @var string
*/
protected $instanceOf = Pattern\PatternInterface::class;
/**
* Override get to inject options as PatternOptions instance.
*
* {@inheritDoc}
*/
public function get($plugin, $options = [], $usePeeringServiceManagers = true)
{
if (empty($options)) {
return parent::get($plugin, [], $usePeeringServiceManagers);
}
$plugin = parent::get($plugin, [], $usePeeringServiceManagers);
$plugin->setOptions(new Pattern\PatternOptions($options));
return $plugin;
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\PatternPluginManager;
use Zend\Cache\Pattern;
use Zend\ServiceManager\AbstractPluginManager;
use Zend\ServiceManager\Factory\InvokableFactory;
/**
* zend-servicemanager v3-compatible plugin manager implementation for cache pattern adapters.
*
* Enforces that retrieved adapters are instances of
* Pattern\PatternInterface. Additionally, it registers a number of default
* patterns available.
*/
class PatternPluginManagerV3Polyfill extends AbstractPluginManager
{
use PatternPluginManagerTrait;
protected $aliases = [
'callback' => Pattern\CallbackCache::class,
'Callback' => Pattern\CallbackCache::class,
'capture' => Pattern\CaptureCache::class,
'Capture' => Pattern\CaptureCache::class,
'class' => Pattern\ClassCache::class,
'Class' => Pattern\ClassCache::class,
'object' => Pattern\ObjectCache::class,
'Object' => Pattern\ObjectCache::class,
'output' => Pattern\OutputCache::class,
'Output' => Pattern\OutputCache::class,
];
protected $factories = [
Pattern\CallbackCache::class => InvokableFactory::class,
Pattern\CaptureCache::class => InvokableFactory::class,
Pattern\ClassCache::class => InvokableFactory::class,
Pattern\ObjectCache::class => InvokableFactory::class,
Pattern\OutputCache::class => InvokableFactory::class,
// v2 normalized FQCNs
'zendcachepatterncallbackcache' => InvokableFactory::class,
'zendcachepatterncapturecache' => InvokableFactory::class,
'zendcachepatternclasscache' => InvokableFactory::class,
'zendcachepatternobjectcache' => InvokableFactory::class,
'zendcachepatternoutputcache' => InvokableFactory::class,
];
/**
* Don't share by default
*
* @var bool
*/
protected $shareByDefault = false;
/**
* Don't share by default
*
* @var bool
*/
protected $sharedByDefault = false;
/**
* @var string
*/
protected $instanceOf = Pattern\PatternInterface::class;
/**
* Override get to inject options as PatternOptions instance.
*
* {@inheritDoc}
*/
public function get($plugin, array $options = null, $usePeeringServiceManagers = true)
{
if (empty($options)) {
return parent::get($plugin, null, $usePeeringServiceManagers);
}
$plugin = parent::get($plugin, null, $usePeeringServiceManagers);
$plugin->setOptions(new Pattern\PatternOptions($options));
return $plugin;
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr\CacheItemPool;
use Psr\Cache\CacheException as CacheExceptionInterface;
use RuntimeException;
class CacheException extends RuntimeException implements CacheExceptionInterface
{
}

View File

@@ -0,0 +1,172 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr\CacheItemPool;
use DateInterval;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Psr\Cache\CacheItemInterface;
final class CacheItem implements CacheItemInterface
{
/**
* Cache key
* @var string
*/
private $key;
/**
* Cache value
* @var mixed|null
*/
private $value;
/**
* True if the cache item lookup resulted in a cache hit or if they item is deferred or successfully saved
* @var bool
*/
private $isHit = false;
/**
* Timestamp item will expire at if expiresAt() called, null otherwise
* @var int|null
*/
private $expiration = null;
/**
* Seconds after item is stored it will expire at if expiresAfter() called, null otherwise
* @var int|null
*/
private $ttl = null;
/**
* @var DateTimeZone
*/
private $tz;
/**
* Constructor.
*
* @param string $key
* @param mixed $value
* @param bool $isHit
*/
public function __construct($key, $value, $isHit)
{
$this->key = $key;
$this->value = $isHit ? $value : null;
$this->isHit = $isHit;
$this->utc = new DateTimeZone('UTC');
}
/**
* {@inheritdoc}
*/
public function getKey()
{
return $this->key;
}
/**
* {@inheritdoc}
*/
public function get()
{
return $this->value;
}
/**
* {@inheritdoc}
*/
public function isHit()
{
if (! $this->isHit) {
return false;
}
$ttl = $this->getTtl();
return $ttl === null || $ttl > 0;
}
/**
* Sets isHit value
*
* This function is called by CacheItemPoolDecorator::saveDeferred() and is not intended for use by other calling
* code.
*
* @param boolean $isHit
* @return $this
*/
public function setIsHit($isHit)
{
$this->isHit = $isHit;
return $this;
}
/**
* {@inheritdoc}
*/
public function set($value)
{
$this->value = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function expiresAt($expiration)
{
if (! ($expiration === null || $expiration instanceof DateTimeInterface)) {
throw new InvalidArgumentException('$expiration must be null or an instance of DateTimeInterface');
}
$this->expiration = $expiration instanceof DateTimeInterface ? $expiration->getTimestamp() : null;
$this->ttl = null;
return $this;
}
/**
* {@inheritdoc}
*/
public function expiresAfter($time)
{
if ($time instanceof DateInterval) {
$now = new DateTimeImmutable('now', $this->utc);
$end = $now->add($time);
$this->ttl = $end->getTimestamp() - $now->getTimestamp();
} elseif (is_int($time) || $time === null) {
$this->ttl = $time;
} else {
throw new InvalidArgumentException(sprintf('Invalid $time "%s"', gettype($time)));
}
$this->expiration = null;
return $this;
}
/**
* Returns number of seconds until item expires
*
* If NULL, the pool should use the default TTL for the storage adapter. If <= 0, the item has expired.
*
* @return int|null
*/
public function getTtl()
{
$ttl = $this->ttl;
if ($this->expiration !== null) {
$ttl = $this->expiration - time();
}
return $ttl;
}
}

View File

@@ -0,0 +1,372 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr\CacheItemPool;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Zend\Cache\Exception;
use Zend\Cache\Psr\SerializationTrait;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\StorageInterface;
/**
* Decorate zend-cache adapters as PSR-6 cache item pools.
*
* @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-6-cache.md
* @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-6-cache-meta.md
*/
class CacheItemPoolDecorator implements CacheItemPoolInterface
{
use SerializationTrait;
/**
* @var StorageInterface
*/
private $storage;
/**
* @var CacheItem[]
*/
private $deferred = [];
/**
* Constructor.
*
* PSR-6 requires that all implementing libraries support TTL so the given storage adapter must also support static
* TTL or an exception will be raised. Currently the following adapters do *not* support static TTL: Dba,
* Filesystem, Memory, Session and Redis < v2.
*
* @param StorageInterface $storage
*
* @throws CacheException
*/
public function __construct(StorageInterface $storage)
{
$this->validateStorage($storage);
$this->storage = $storage;
}
/**
* Destructor.
*
* Saves any deferred items that have not been committed
*/
public function __destruct()
{
$this->commit();
}
/**
* {@inheritdoc}
*/
public function getItem($key)
{
$this->validateKey($key);
if (! $this->hasDeferredItem($key)) {
$value = null;
$isHit = false;
try {
$value = $this->storage->getItem($key, $isHit);
} catch (Exception\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
} catch (Exception\ExceptionInterface $e) {
// ignore
}
return new CacheItem($key, $value, $isHit);
} else {
return clone $this->deferred[$key];
}
}
/**
* {@inheritdoc}
*/
public function getItems(array $keys = [])
{
$this->validateKeys($keys);
$items = [];
// check deferred items first
foreach ($keys as $key) {
if ($this->hasDeferredItem($key)) {
// dereference deferred items
$items[$key] = clone $this->deferred[$key];
}
}
$keys = array_diff($keys, array_keys($items));
if ($keys) {
try {
$cacheItems = $this->storage->getItems($keys);
} catch (Exception\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
} catch (Exception\ExceptionInterface $e) {
$cacheItems = [];
}
foreach ($cacheItems as $key => $value) {
$isHit = true;
$items[$key] = new CacheItem($key, $value, $isHit);
}
// Return empty items for any keys that where not found
foreach (array_diff($keys, array_keys($cacheItems)) as $key) {
$items[$key] = new CacheItem($key, null, false);
}
}
return $items;
}
/**
* {@inheritdoc}
*/
public function hasItem($key)
{
$this->validateKey($key);
// check deferred items first
$hasItem = $this->hasDeferredItem($key);
if (! $hasItem) {
try {
$hasItem = $this->storage->hasItem($key);
} catch (Exception\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
} catch (Exception\ExceptionInterface $e) {
$hasItem = false;
}
}
return $hasItem;
}
/**
* {@inheritdoc}
*
* If the storage adapter supports namespaces and one has been set, only that namespace is cleared; otherwise
* entire cache is flushed.
*/
public function clear()
{
$this->deferred = [];
try {
$namespace = $this->storage->getOptions()->getNamespace();
if ($this->storage instanceof ClearByNamespaceInterface && $namespace) {
$cleared = $this->storage->clearByNamespace($namespace);
} else {
$cleared = $this->storage->flush();
}
} catch (Exception\ExceptionInterface $e) {
$cleared = false;
}
return $cleared;
}
/**
* {@inheritdoc}
*/
public function deleteItem($key)
{
return $this->deleteItems([$key]);
}
/**
* {@inheritdoc}
*/
public function deleteItems(array $keys)
{
$this->validateKeys($keys);
// remove deferred items first
$this->deferred = array_diff_key($this->deferred, array_flip($keys));
try {
return null !== $this->storage->removeItems($keys);
} catch (Exception\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
} catch (Exception\ExceptionInterface $e) {
}
return false;
}
/**
* {@inheritdoc}
*/
public function save(CacheItemInterface $item)
{
if (! $item instanceof CacheItem) {
throw new InvalidArgumentException('$item must be an instance of ' . CacheItem::class);
}
$itemTtl = $item->getTtl();
// delete expired item
if ($itemTtl < 0) {
$this->deleteItem($item->getKey());
$item->setIsHit(false);
return false;
}
$saved = true;
$options = $this->storage->getOptions();
$ttl = $options->getTtl();
try {
// get item value and serialize, if required
$value = $item->get();
// reset TTL on adapter, if required
if ($itemTtl > 0) {
$options->setTtl($itemTtl);
}
$saved = $this->storage->setItem($item->getKey(), $value);
// saved items are a hit? see integration test CachePoolTest::testIsHit()
$item->setIsHit($saved);
} catch (Exception\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
} catch (Exception\ExceptionInterface $e) {
$saved = false;
} finally {
$options->setTtl($ttl);
}
return $saved;
}
/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item)
{
if (! $item instanceof CacheItem) {
throw new InvalidArgumentException('$item must be an instance of ' . CacheItem::class);
}
// deferred items should always be a 'hit' until they expire
$item->setIsHit(true);
$this->deferred[$item->getKey()] = $item;
return true;
}
/**
* {@inheritdoc}
*/
public function commit()
{
$notSaved = [];
foreach ($this->deferred as &$item) {
if (! $this->save($item)) {
$notSaved[] = $item;
}
}
$this->deferred = $notSaved;
return empty($this->deferred);
}
/**
* Throws exception is storage is not compatible with PSR-6
* @param StorageInterface $storage
* @throws CacheException
*/
private function validateStorage(StorageInterface $storage)
{
if ($this->isSerializationRequired($storage)) {
throw new CacheException(sprintf(
'The storage adapter "%s" requires a serializer plugin; please see'
. ' https://docs.zendframework.com/zend-cache/storage/plugin/#quick-start'
. ' for details on how to attach the plugin to your adapter.',
get_class($storage)
));
}
// all current adapters implement this
if (! $storage instanceof FlushableInterface) {
throw new CacheException(sprintf(
'Storage %s does not implement %s',
get_class($storage),
FlushableInterface::class
));
}
// we've got to be able to set per-item TTL on write
$capabilities = $storage->getCapabilities();
if (! ($capabilities->getStaticTtl() && $capabilities->getMinTtl())) {
throw new CacheException(sprintf(
'Storage %s does not support static TTL',
get_class($storage)
));
}
if ($capabilities->getUseRequestTime()) {
throw new CacheException(sprintf(
'The capability "use-request-time" of storage %s violates PSR-6',
get_class($storage)
));
}
if ($capabilities->getLockOnExpire()) {
throw new CacheException(sprintf(
'The capability "lock-on-expire" of storage %s violates PSR-6',
get_class($storage)
));
}
}
/**
* Returns true if deferred item exists for given key and has not expired
* @param string $key
* @return bool
*/
private function hasDeferredItem($key)
{
if (isset($this->deferred[$key])) {
$ttl = $this->deferred[$key]->getTtl();
return $ttl === null || $ttl > 0;
}
return false;
}
/**
* Throws exception if given key is invalid
* @param mixed $key
* @throws InvalidArgumentException
*/
private function validateKey($key)
{
if (! is_string($key) || preg_match('#[{}()/\\\\@:]#', $key)) {
throw new InvalidArgumentException(sprintf(
"Key must be a string and not contain '{}()/\\@:'; '%s' given",
is_string($key) ? $key : gettype($key)
));
}
}
/**
* Throws exception if any of given keys is invalid
* @param array $keys
* @throws InvalidArgumentException
*/
private function validateKeys($keys)
{
foreach ($keys as $key) {
$this->validateKey($key);
}
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr\CacheItemPool;
use Psr\Cache\InvalidArgumentException as InvalidArgumentExceptionInterface;
class InvalidArgumentException extends \InvalidArgumentException implements InvalidArgumentExceptionInterface
{
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr;
use Zend\Cache\Storage\StorageInterface;
/**
* Provides common functionality surrounding value de/serialization as required
* by both PSR-6 and PSR-16
*/
trait SerializationTrait
{
/**
* Determine if the given storage adapter requires serialization.
*
* @param StorageInterface $storage
* @return bool
*/
private function isSerializationRequired(StorageInterface $storage)
{
$capabilities = $storage->getCapabilities();
$requiredTypes = ['string', 'integer', 'double', 'boolean', 'NULL', 'array', 'object'];
$types = $capabilities->getSupportedDatatypes();
foreach ($requiredTypes as $type) {
// 'object' => 'object' is OK
// 'integer' => 'string' is not (redis)
// 'integer' => 'integer' is not (memcache)
if (! (isset($types[$type]) && in_array($types[$type], [true, 'array', 'object'], true))) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,449 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018-2019 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr\SimpleCache;
use DateInterval;
use DateTimeImmutable;
use DateTimeZone;
use Exception;
use Psr\SimpleCache\CacheInterface as SimpleCacheInterface;
use Throwable;
use Traversable;
use Zend\Cache\Exception\InvalidArgumentException as ZendCacheInvalidArgumentException;
use Zend\Cache\Psr\SerializationTrait;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\StorageInterface;
/**
* Decorate a zend-cache storage adapter for usage as a PSR-16 implementation.
*/
class SimpleCacheDecorator implements SimpleCacheInterface
{
use SerializationTrait;
/**
* Characters reserved by PSR-16 that are not valid in cache keys.
*/
const INVALID_KEY_CHARS = ':@{}()/\\';
/**
* @var bool
*/
private $providesPerItemTtl = true;
/**
* @var StorageInterface
*/
private $storage;
/**
* Reference used by storage when calling getItem() to indicate status of
* operation.
*
* @var null|bool
*/
private $success;
/**
* @var DateTimeZone
*/
private $utc;
public function __construct(StorageInterface $storage)
{
if ($this->isSerializationRequired($storage)) {
throw new SimpleCacheException(sprintf(
'The storage adapter "%s" requires a serializer plugin; please see'
. ' https://docs.zendframework.com/zend-cache/storage/plugin/#quick-start'
. ' for details on how to attach the plugin to your adapter.',
get_class($storage)
));
}
$this->memoizeTtlCapabilities($storage);
$this->storage = $storage;
$this->utc = new DateTimeZone('UTC');
}
/**
* {@inheritDoc}
*/
public function get($key, $default = null)
{
$this->validateKey($key);
$this->success = null;
try {
$result = $this->storage->getItem($key, $this->success);
} catch (Throwable $e) {
throw static::translateException($e);
} catch (Exception $e) {
throw static::translateException($e);
}
$result = $result === null ? $default : $result;
return $this->success ? $result : $default;
}
/**
* {@inheritDoc}
*/
public function set($key, $value, $ttl = null)
{
$this->validateKey($key);
$ttl = $this->convertTtlToInteger($ttl);
// PSR-16 states that 0 or negative TTL values should result in cache
// invalidation for the item.
if (null !== $ttl && 1 > $ttl) {
return $this->delete($key);
}
// If a positive TTL is set, but the adapter does not support per-item
// TTL, we return false immediately.
if (null !== $ttl && ! $this->providesPerItemTtl) {
return false;
}
$options = $this->storage->getOptions();
$previousTtl = $options->getTtl();
if ($ttl !== null) {
$options->setTtl($ttl);
}
try {
$result = $this->storage->setItem($key, $value);
} catch (Throwable $e) {
throw static::translateException($e);
} catch (Exception $e) {
throw static::translateException($e);
} finally {
$options->setTtl($previousTtl);
}
return $result;
}
/**
* {@inheritDoc}
*/
public function delete($key)
{
$this->validateKey($key);
try {
return null !== $this->storage->removeItem($key);
} catch (Throwable $e) {
return false;
} catch (Exception $e) {
return false;
}
}
/**
* {@inheritDoc}
*/
public function clear()
{
$namespace = $this->storage->getOptions()->getNamespace();
if ($this->storage instanceof ClearByNamespaceInterface && $namespace) {
return $this->storage->clearByNamespace($namespace);
}
if ($this->storage instanceof FlushableInterface) {
return $this->storage->flush();
}
return false;
}
/**
* {@inheritDoc}
*/
public function getMultiple($keys, $default = null)
{
$keys = $this->convertIterableToArray($keys, false, __FUNCTION__);
array_walk($keys, [$this, 'validateKey']);
try {
$results = $this->storage->getItems($keys);
} catch (Throwable $e) {
throw static::translateException($e);
} catch (Exception $e) {
throw static::translateException($e);
}
foreach ($keys as $key) {
if (! isset($results[$key])) {
$results[$key] = $default;
continue;
}
}
return $results;
}
/**
* {@inheritDoc}
*/
public function setMultiple($values, $ttl = null)
{
$values = $this->convertIterableToArray($values, true, __FUNCTION__);
$keys = array_keys($values);
$ttl = $this->convertTtlToInteger($ttl);
// PSR-16 states that 0 or negative TTL values should result in cache
// invalidation for the items.
if (null !== $ttl && 1 > $ttl) {
return $this->deleteMultiple($keys);
}
array_walk($keys, [$this, 'validateKey']);
// If a positive TTL is set, but the adapter does not support per-item
// TTL, we return false -- but not until after we validate keys.
if (null !== $ttl && ! $this->providesPerItemTtl) {
return false;
}
$options = $this->storage->getOptions();
$previousTtl = $options->getTtl();
if ($ttl !== null) {
$options->setTtl($ttl);
}
try {
$result = $this->storage->setItems($values);
} catch (Throwable $e) {
throw static::translateException($e);
} catch (Exception $e) {
throw static::translateException($e);
} finally {
$options->setTtl($previousTtl);
}
if (empty($result)) {
return true;
}
foreach ($result as $index => $key) {
if (! $this->storage->hasItem($key)) {
unset($result[$index]);
}
}
return empty($result);
}
/**
* {@inheritDoc}
*/
public function deleteMultiple($keys)
{
$keys = $this->convertIterableToArray($keys, false, __FUNCTION__);
if (empty($keys)) {
return true;
}
array_walk($keys, [$this, 'validateKey']);
try {
$result = $this->storage->removeItems($keys);
} catch (Throwable $e) {
return false;
} catch (Exception $e) {
return false;
}
if (empty($result)) {
return true;
}
foreach ($result as $index => $key) {
if (! $this->storage->hasItem($key)) {
unset($result[$index]);
}
}
return empty($result);
}
/**
* {@inheritDoc}
*/
public function has($key)
{
$this->validateKey($key);
try {
return $this->storage->hasItem($key);
} catch (Throwable $e) {
throw static::translateException($e);
} catch (Exception $e) {
throw static::translateException($e);
}
}
/**
* @param Throwable|Exception $e
* @return SimpleCacheException
*/
private static function translateException($e)
{
$exceptionClass = $e instanceof ZendCacheInvalidArgumentException
? SimpleCacheInvalidArgumentException::class
: SimpleCacheException::class;
return new $exceptionClass($e->getMessage(), $e->getCode(), $e);
}
/**
* @param string $key
* @return void
* @throws SimpleCacheInvalidArgumentException if key is invalid
*/
private function validateKey($key)
{
if ('' === $key) {
throw new SimpleCacheInvalidArgumentException(
'Invalid key provided; cannot be empty'
);
}
if (0 === $key) {
// cache/integration-tests erroneously tests that ['0' => 'value']
// is a valid payload to setMultiple(). However, PHP silently
// converts '0' to 0, which would normally be invalid. For now,
// we need to catch just this single value so tests pass.
// I have filed an issue to correct the test:
// https://github.com/php-cache/integration-tests/issues/92
return $key;
}
if (! is_string($key)) {
throw new SimpleCacheInvalidArgumentException(sprintf(
'Invalid key provided of type "%s"%s; must be a string',
is_object($key) ? get_class($key) : gettype($key),
is_scalar($key) ? sprintf(' (%s)', var_export($key, true)) : ''
));
}
$regex = sprintf('/[%s]/', preg_quote(self::INVALID_KEY_CHARS, '/'));
if (preg_match($regex, $key)) {
throw new SimpleCacheInvalidArgumentException(sprintf(
'Invalid key "%s" provided; cannot contain any of (%s)',
$key,
self::INVALID_KEY_CHARS
));
}
if (preg_match('/^.{65,}/u', $key)) {
throw new SimpleCacheInvalidArgumentException(sprintf(
'Invalid key "%s" provided; key is too long. Must be no more than 64 characters',
$key
));
}
}
/**
* Determine if the storage adapter provides per-item TTL capabilities
*
* @param StorageInterface $storage
* @return void
*/
private function memoizeTtlCapabilities(StorageInterface $storage)
{
$capabilities = $storage->getCapabilities();
$this->providesPerItemTtl = $capabilities->getStaticTtl() && (0 < $capabilities->getMinTtl());
}
/**
* @param int|DateInterval
* @return null|int
* @throws SimpleCacheInvalidArgumentException for invalid arguments
*/
private function convertTtlToInteger($ttl)
{
// null === absence of a TTL
if (null === $ttl) {
return null;
}
// integers are always okay
if (is_int($ttl)) {
return $ttl;
}
// Numeric strings evaluating to integers can be cast
if (is_string($ttl)
&& ('0' === $ttl
|| preg_match('/^[1-9][0-9]+$/', $ttl)
)
) {
return (int) $ttl;
}
// DateIntervals require conversion
if ($ttl instanceof DateInterval) {
$now = new DateTimeImmutable('now', $this->utc);
$end = $now->add($ttl);
return $end->getTimestamp() - $now->getTimestamp();
}
// All others are invalid
throw new SimpleCacheInvalidArgumentException(sprintf(
'Invalid TTL "%s" provided; must be null, an integer, or a %s instance',
is_object($ttl) ? get_class($ttl) : var_export($ttl, true),
DateInterval::class
));
}
/**
* @param array|iterable $iterable
* @param bool $useKeys Whether or not to preserve keys during conversion
* @param string $forMethod Method that called this one; used for reporting
* invalid values.
* @return array
* @throws SimpleCacheInvalidArgumentException for invalid $iterable values
*/
private function convertIterableToArray($iterable, $useKeys, $forMethod)
{
if (is_array($iterable)) {
return $iterable;
}
if (! $iterable instanceof Traversable) {
throw new SimpleCacheInvalidArgumentException(sprintf(
'Invalid value provided to %s::%s; must be an array or Traversable',
__CLASS__,
$forMethod
));
}
$array = [];
foreach ($iterable as $key => $value) {
if (! $useKeys) {
$array[] = $value;
continue;
}
if (! is_string($key) && ! is_int($key) && ! is_float($key)) {
throw new SimpleCacheInvalidArgumentException(sprintf(
'Invalid key detected of type "%s"; must be a scalar',
is_object($key) ? get_class($key) : gettype($key)
));
}
$array[$key] = $value;
}
return $array;
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr\SimpleCache;
use Psr\SimpleCache\CacheException as PsrCacheException;
use RuntimeException;
class SimpleCacheException extends RuntimeException implements PsrCacheException
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Psr\SimpleCache;
use InvalidArgumentException;
use Psr\SimpleCache\InvalidArgumentException as PsrInvalidArgumentException;
class SimpleCacheInvalidArgumentException extends InvalidArgumentException implements PsrInvalidArgumentException
{
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* @link http://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Service;
use Interop\Container\ContainerInterface;
use Zend\Cache\PatternPluginManager;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class PatternPluginManagerFactory implements FactoryInterface
{
/**
* zend-servicemanager v2 support for invocation options.
*
* @param array
*/
protected $creationOptions;
/**
* {@inheritDoc}
*
* @return PatternPluginManager
*/
public function __invoke(ContainerInterface $container, $name, array $options = null)
{
return new PatternPluginManager($container, $options ?: []);
}
/**
* {@inheritDoc}
*
* @return PatternPluginManager
*/
public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
{
return $this($container, $requestedName ?: PatternPluginManager::class, $this->creationOptions);
}
/**
* zend-servicemanager v2 support for invocation options.
*
* @param array $options
* @return void
*/
public function setCreationOptions(array $options)
{
$this->creationOptions = $options;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* @link http://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Service;
use Interop\Container\ContainerInterface;
use Zend\Cache\StorageFactory;
use Zend\Cache\Storage\AdapterPluginManager;
use Zend\Cache\Storage\PluginManager;
trait PluginManagerLookupTrait
{
/**
* Prepare the storage factory with the adapter and plugins plugin managers.
*
* @param ContainerInterface $container
* @return void
*/
private function prepareStorageFactory(ContainerInterface $container)
{
StorageFactory::setAdapterPluginManager($this->lookupStorageAdapterPluginManager($container));
StorageFactory::setPluginManager($this->lookupStoragePluginManager($container));
}
/**
* Lookup the storage adapter plugin manager.
*
* Returns the Zend\Cache\Storage\AdapterPluginManager service if present,
* or creates a new instance otherwise.
*
* @param ContainerInterface $container
* @return AdapterPluginManager
*/
private function lookupStorageAdapterPluginManager(ContainerInterface $container)
{
if ($container->has(AdapterPluginManager::class)) {
return $container->get(AdapterPluginManager::class);
}
return new AdapterPluginManager($container);
}
/**
* Lookup the storage plugins plugin manager.
*
* Returns the Zend\Cache\Storage\PluginManager service if present, or
* creates a new instance otherwise.
*
* @param ContainerInterface $container
* @return PluginManager
*/
private function lookupStoragePluginManager(ContainerInterface $container)
{
if ($container->has(PluginManager::class)) {
return $container->get(PluginManager::class);
}
return new PluginManager($container);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* @link http://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Service;
use Interop\Container\ContainerInterface;
use Zend\Cache\Storage\AdapterPluginManager;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class StorageAdapterPluginManagerFactory implements FactoryInterface
{
/**
* zend-servicemanager v2 support for invocation options.
*
* @param array
*/
protected $creationOptions;
/**
* {@inheritDoc}
*
* @return AdapterPluginManager
*/
public function __invoke(ContainerInterface $container, $name, array $options = null)
{
return new AdapterPluginManager($container, $options ?: []);
}
/**
* {@inheritDoc}
*
* @return AdapterPluginManager
*/
public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
{
return $this($container, $requestedName ?: AdapterPluginManager::class, $this->creationOptions);
}
/**
* zend-servicemanager v2 support for invocation options.
*
* @param array $options
* @return void
*/
public function setCreationOptions(array $options)
{
$this->creationOptions = $options;
}
}

View File

@@ -0,0 +1,108 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Service;
use Interop\Container\ContainerInterface;
use Zend\Cache\StorageFactory;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
/**
* Storage cache factory for multiple caches.
*/
class StorageCacheAbstractServiceFactory implements AbstractFactoryInterface
{
use PluginManagerLookupTrait;
/**
* @var array
*/
protected $config;
/**
* Configuration key for cache objects
*
* @var string
*/
protected $configKey = 'caches';
/**
* @param ContainerInterface $container
* @param string $requestedName
* @return boolean
*/
public function canCreate(ContainerInterface $container, $requestedName)
{
$config = $this->getConfig($container);
if (empty($config)) {
return false;
}
return (isset($config[$requestedName]) && is_array($config[$requestedName]));
}
/**
* @param ServiceLocatorInterface $serviceLocator
* @param string $name
* @param string $requestedName
* @return boolean
*/
public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
return $this->canCreate($serviceLocator, $requestedName);
}
/**
* Create an object
*
* @param ContainerInterface $container
* @param string $requestedName
* @param null|array $options
* @return object
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$this->prepareStorageFactory($container);
$config = $this->getConfig($container);
return StorageFactory::factory($config[$requestedName]);
}
public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
return $this($serviceLocator, $requestedName);
}
/**
* Retrieve cache configuration, if any
*
* @param ContainerInterface $container
* @return array
*/
protected function getConfig(ContainerInterface $container)
{
if ($this->config !== null) {
return $this->config;
}
if (! $container->has('config')) {
$this->config = [];
return $this->config;
}
$config = $container->get('config');
if (! isset($config[$this->configKey])) {
$this->config = [];
return $this->config;
}
$this->config = $config[$this->configKey];
return $this->config;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Service;
use Interop\Container\ContainerInterface;
use Zend\Cache\Storage\StorageInterface;
use Zend\Cache\StorageFactory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
/**
* Storage cache factory.
*/
class StorageCacheFactory implements FactoryInterface
{
use PluginManagerLookupTrait;
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$this->prepareStorageFactory($container);
$config = $container->get('config');
$cacheConfig = isset($config['cache']) ? $config['cache'] : [];
return StorageFactory::factory($cacheConfig);
}
public function createService(ServiceLocatorInterface $serviceLocator)
{
return $this($serviceLocator, StorageInterface::class);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* @link http://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Service;
use Interop\Container\ContainerInterface;
use Zend\Cache\Storage\PluginManager;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class StoragePluginManagerFactory implements FactoryInterface
{
/**
* zend-servicemanager v2 support for invocation options.
*
* @param array
*/
protected $creationOptions;
/**
* {@inheritDoc}
*
* @return PluginManager
*/
public function __invoke(ContainerInterface $container, $name, array $options = null)
{
return new PluginManager($container, $options ?: []);
}
/**
* {@inheritDoc}
*
* @return PluginManager
*/
public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
{
return $this($container, $requestedName ?: PluginManager::class, $this->creationOptions);
}
/**
* zend-servicemanager v2 support for invocation options.
*
* @param array $options
* @return void
*/
public function setCreationOptions(array $options)
{
$this->creationOptions = $options;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,275 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use stdClass;
use Zend\Cache\Exception;
use Zend\Cache\Storage\Capabilities;
abstract class AbstractZendServer extends AbstractAdapter
{
/**
* The namespace separator used on Zend Data Cache functions
*
* @var string
*/
const NAMESPACE_SEPARATOR = '::';
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$namespace = $this->getOptions()->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . self::NAMESPACE_SEPARATOR;
$result = $this->zdcFetch($prefix . $normalizedKey);
if ($result === false) {
$result = null;
$success = false;
} else {
$success = true;
$casToken = $result;
}
return $result;
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$namespace = $this->getOptions()->getNamespace();
if ($namespace === '') {
return $this->zdcFetchMulti($normalizedKeys);
}
$prefix = $namespace . self::NAMESPACE_SEPARATOR;
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$fetch = $this->zdcFetchMulti($internalKeys);
$result = [];
$prefixL = strlen($prefix);
foreach ($fetch as $k => & $v) {
$result[substr($k, $prefixL)] = $v;
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$namespace = $this->getOptions()->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . self::NAMESPACE_SEPARATOR;
return ($this->zdcFetch($prefix . $normalizedKey) !== false);
}
/**
* Internal method to test multiple items.
*
* @param array $normalizedKeys
* @return array Array of found keys
* @throws Exception\ExceptionInterface
*/
protected function internalHasItems(array & $normalizedKeys)
{
$namespace = $this->getOptions()->getNamespace();
if ($namespace === '') {
return array_keys($this->zdcFetchMulti($normalizedKeys));
}
$prefix = $namespace . self::NAMESPACE_SEPARATOR;
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$fetch = $this->zdcFetchMulti($internalKeys);
$result = [];
$prefixL = strlen($prefix);
foreach ($fetch as $internalKey => & $value) {
$result[] = substr($internalKey, $prefixL);
}
return $result;
}
/**
* Get metadata for multiple items
*
* @param array $normalizedKeys
* @return array Associative array of keys and metadata
*
* @triggers getMetadatas.pre(PreEvent)
* @triggers getMetadatas.post(PostEvent)
* @triggers getMetadatas.exception(ExceptionEvent)
*/
protected function internalGetMetadatas(array & $normalizedKeys)
{
$namespace = $this->getOptions()->getNamespace();
if ($namespace === '') {
$result = $this->zdcFetchMulti($normalizedKeys);
return array_fill_keys(array_keys($result), []);
}
$prefix = $namespace . self::NAMESPACE_SEPARATOR;
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$fetch = $this->zdcFetchMulti($internalKeys);
$result = [];
$prefixL = strlen($prefix);
foreach ($fetch as $internalKey => $value) {
$result[substr($internalKey, $prefixL)] = [];
}
return $result;
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . self::NAMESPACE_SEPARATOR;
$this->zdcStore($prefix . $normalizedKey, $value, $options->getTtl());
return true;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$namespace = $this->getOptions()->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . self::NAMESPACE_SEPARATOR;
return $this->zdcDelete($prefix . $normalizedKey);
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$this->capabilityMarker = new stdClass();
$this->capabilities = new Capabilities(
$this,
$this->capabilityMarker,
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => 'object',
'resource' => false,
],
'supportedMetadata' => [],
'minTtl' => 1,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => false,
'lockOnExpire' => ini_get('zend_datacache.lock_on_expire') ? 120 : 0,
'maxKeyLength' => 0,
'namespaceIsPrefix' => true,
'namespaceSeparator' => self::NAMESPACE_SEPARATOR,
]
);
}
return $this->capabilities;
}
/* internal wrapper of zend_[disk|shm]_cache_* functions */
/**
* Store data into Zend Data Cache (zdc)
*
* @param string $internalKey
* @param mixed $value
* @param int $ttl
* @return void
* @throws Exception\RuntimeException
*/
abstract protected function zdcStore($internalKey, $value, $ttl);
/**
* Fetch a single item from Zend Data Cache (zdc)
*
* @param string $internalKey
* @return mixed The stored value or FALSE if item wasn't found
* @throws Exception\RuntimeException
*/
abstract protected function zdcFetch($internalKey);
/**
* Fetch multiple items from Zend Data Cache (zdc)
*
* @param array $internalKeys
* @return array All found items
* @throws Exception\RuntimeException
*/
abstract protected function zdcFetchMulti(array $internalKeys);
/**
* Delete data from Zend Data Cache (zdc)
*
* @param string $internalKey
* @return bool
* @throws Exception\RuntimeException
*/
abstract protected function zdcDelete($internalKey);
}

View File

@@ -0,0 +1,342 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use ArrayObject;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\Event;
use Zend\Cache\Storage\StorageInterface;
use Zend\EventManager\EventsCapableInterface;
use Zend\Stdlib\AbstractOptions;
use Zend\Stdlib\ErrorHandler;
/**
* Unless otherwise marked, all options in this class affect all adapters.
*/
class AdapterOptions extends AbstractOptions
{
// @codingStandardsIgnoreStart
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = [];
// @codingStandardsIgnoreEnd
/**
* The adapter using these options
*
* @var null|StorageInterface
*/
protected $adapter;
/**
* Validate key against pattern
*
* @var string
*/
protected $keyPattern = '';
/**
* Namespace option
*
* @var string
*/
protected $namespace = 'zfcache';
/**
* Readable option
*
* @var bool
*/
protected $readable = true;
/**
* TTL option
*
* @var int|float 0 means infinite or maximum of adapter
*/
protected $ttl = 0;
/**
* Writable option
*
* @var bool
*/
protected $writable = true;
/**
* Adapter using this instance
*
* @param StorageInterface|null $adapter
* @return AdapterOptions Provides a fluent interface
*/
public function setAdapter(StorageInterface $adapter = null)
{
$this->adapter = $adapter;
return $this;
}
/**
* Set key pattern
*
* @param string $keyPattern
* @throws Exception\InvalidArgumentException
* @return AdapterOptions Provides a fluent interface
*/
public function setKeyPattern($keyPattern)
{
$keyPattern = (string) $keyPattern;
if ($this->keyPattern !== $keyPattern) {
// validate pattern
if ($keyPattern !== '') {
ErrorHandler::start(E_WARNING);
$result = preg_match($keyPattern, '');
$error = ErrorHandler::stop();
if ($result === false) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid pattern "%s"%s',
$keyPattern,
($error ? ': ' . $error->getMessage() : '')
), 0, $error);
}
}
$this->triggerOptionEvent('key_pattern', $keyPattern);
$this->keyPattern = $keyPattern;
}
return $this;
}
/**
* Get key pattern
*
* @return string
*/
public function getKeyPattern()
{
return $this->keyPattern;
}
/**
* Set namespace.
*
* @param string $namespace
* @return AdapterOptions Provides a fluent interface
*/
public function setNamespace($namespace)
{
$namespace = (string) $namespace;
if ($this->namespace !== $namespace) {
$this->triggerOptionEvent('namespace', $namespace);
$this->namespace = $namespace;
}
return $this;
}
/**
* Get namespace
*
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* Enable/Disable reading data from cache.
*
* @param bool $readable
* @return AdapterOptions Provides a fluent interface
*/
public function setReadable($readable)
{
$readable = (bool) $readable;
if ($this->readable !== $readable) {
$this->triggerOptionEvent('readable', $readable);
$this->readable = $readable;
}
return $this;
}
/**
* If reading data from cache enabled.
*
* @return bool
*/
public function getReadable()
{
return $this->readable;
}
/**
* Set time to live.
*
* @param int|float $ttl
* @return AdapterOptions Provides a fluent interface
*/
public function setTtl($ttl)
{
$this->normalizeTtl($ttl);
if ($this->ttl !== $ttl) {
$this->triggerOptionEvent('ttl', $ttl);
$this->ttl = $ttl;
}
return $this;
}
/**
* Get time to live.
*
* @return float
*/
public function getTtl()
{
return $this->ttl;
}
/**
* Enable/Disable writing data to cache.
*
* @param bool $writable
* @return AdapterOptions Provides a fluent interface
*/
public function setWritable($writable)
{
$writable = (bool) $writable;
if ($this->writable !== $writable) {
$this->triggerOptionEvent('writable', $writable);
$this->writable = $writable;
}
return $this;
}
/**
* If writing data to cache enabled.
*
* @return bool
*/
public function getWritable()
{
return $this->writable;
}
/**
* Triggers an option event if this options instance has a connection to
* an adapter implements EventsCapableInterface.
*
* @param string $optionName
* @param mixed $optionValue
* @return void
*/
protected function triggerOptionEvent($optionName, $optionValue)
{
if ($this->adapter instanceof EventsCapableInterface) {
$event = new Event('option', $this->adapter, new ArrayObject([$optionName => $optionValue]));
$this->adapter->getEventManager()->triggerEvent($event);
}
}
/**
* Validates and normalize a TTL.
*
* @param int|float $ttl
* @throws Exception\InvalidArgumentException
* @return void
*/
protected function normalizeTtl(&$ttl)
{
if (! is_int($ttl)) {
$ttl = (float) $ttl;
// convert to int if possible
if ($ttl === (float) (int) $ttl) {
$ttl = (int) $ttl;
}
}
if ($ttl < 0) {
throw new Exception\InvalidArgumentException("TTL can't be negative");
}
}
/**
* Cast to array
*
* @return array
*/
public function toArray()
{
$array = [];
$transform = function ($letters) {
$letter = array_shift($letters);
return '_' . strtolower($letter);
};
foreach ($this as $key => $value) {
if ($key === '__strictMode__' || $key === '__prioritizedProperties__') {
continue;
}
$normalizedKey = preg_replace_callback('/([A-Z])/', $transform, $key);
$array[$normalizedKey] = $value;
}
return $array;
}
/**
* {@inheritdoc}
*
* NOTE: This method was overwritten just to support prioritized properties
* {@link https://github.com/zendframework/zf2/issues/6381}
*
* @param array|Traversable|AbstractOptions $options
* @throws Exception\InvalidArgumentException
* @return AbstractOptions Provides fluent interface
*/
public function setFromArray($options)
{
if ($this->__prioritizedProperties__) {
if ($options instanceof AbstractOptions) {
$options = $options->toArray();
}
if ($options instanceof Traversable) {
$options = iterator_to_array($options);
} elseif (! is_array($options)) {
throw new Exception\InvalidArgumentException(
sprintf(
'Parameter provided to %s must be an %s, %s or %s',
__METHOD__,
'array',
'Traversable',
'Zend\Stdlib\AbstractOptions'
)
);
}
// Sort prioritized options to top
$options = array_change_key_case($options, CASE_LOWER);
foreach (array_reverse($this->__prioritizedProperties__) as $key) {
if (isset($options[$key])) {
$options = [$key => $options[$key]] + $options;
} elseif (isset($options[($key = str_replace('_', '', $key))])) {
$options = [$key => $options[$key]] + $options;
}
}
}
return parent::setFromArray($options);
}
}

View File

@@ -0,0 +1,753 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use APCIterator as BaseApcIterator;
use stdClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\IterableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class Apc extends AbstractAdapter implements
AvailableSpaceCapableInterface,
ClearByNamespaceInterface,
ClearByPrefixInterface,
FlushableInterface,
IterableInterface,
TotalSpaceCapableInterface
{
/**
* Buffered total space in bytes
*
* @var null|int|float
*/
protected $totalSpace;
/**
* Constructor
*
* @param null|array|Traversable|ApcOptions $options
* @throws Exception\ExceptionInterface
*/
public function __construct($options = null)
{
if (! extension_loaded('apc')) {
throw new Exception\ExtensionNotLoadedException('Missing ext/apc');
}
$enabled = ini_get('apc.enabled');
if (PHP_SAPI == 'cli') {
$enabled = $enabled && (bool) ini_get('apc.enable_cli');
}
if (! $enabled) {
throw new Exception\ExtensionNotLoadedException(
"ext/apc is disabled - see 'apc.enabled' and 'apc.enable_cli'"
);
}
parent::__construct($options);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|ApcOptions $options
* @return Apc
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof ApcOptions) {
$options = new ApcOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return ApcOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new ApcOptions());
}
return $this->options;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
if ($this->totalSpace === null) {
$smaInfo = apc_sma_info(true);
$this->totalSpace = $smaInfo['num_seg'] * $smaInfo['seg_size'];
}
return $this->totalSpace;
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
$smaInfo = apc_sma_info(true);
return $smaInfo['avail_mem'];
}
/* IterableInterface */
/**
* Get the storage iterator
*
* @return ApcIterator
*/
public function getIterator()
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = '';
$pattern = null;
if ($namespace !== '') {
$prefix = $namespace . $options->getNamespaceSeparator();
$pattern = '/^' . preg_quote($prefix, '/') . '/';
}
$baseIt = new BaseApcIterator('user', $pattern, 0, 1, APC_LIST_ACTIVE);
return new ApcIterator($this, $baseIt, $prefix);
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
return apc_clear_cache('user');
}
/* ClearByNamespaceInterface */
/**
* Remove items by given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
$options = $this->getOptions();
$prefix = $namespace . $options->getNamespaceSeparator();
$pattern = '/^' . preg_quote($prefix, '/') . '/';
return apc_delete(new BaseApcIterator('user', $pattern, 0, 1, APC_LIST_ACTIVE));
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$nsPrefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$pattern = '/^' . preg_quote($nsPrefix . $prefix, '/') . '/';
return apc_delete(new BaseApcIterator('user', $pattern, 0, 1, APC_LIST_ACTIVE));
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$result = apc_fetch($internalKey, $success);
if (! $success) {
return;
}
$casToken = $result;
return $result;
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return apc_fetch($normalizedKeys);
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$fetch = apc_fetch($internalKeys);
// remove namespace prefix
$prefixL = strlen($prefix);
$result = [];
foreach ($fetch as $internalKey => $value) {
$result[substr($internalKey, $prefixL)] = $value;
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
return apc_exists($prefix . $normalizedKey);
}
/**
* Internal method to test multiple items.
*
* @param array $normalizedKeys
* @return array Array of found keys
* @throws Exception\ExceptionInterface
*/
protected function internalHasItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
// array_filter with no callback will remove entries equal to FALSE
return array_keys(array_filter(apc_exists($normalizedKeys)));
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$exists = apc_exists($internalKeys);
$result = [];
$prefixL = strlen($prefix);
foreach ($exists as $internalKey => $bool) {
if ($bool === true) {
$result[] = substr($internalKey, $prefixL);
}
}
return $result;
}
/**
* Get metadata of an item.
*
* @param string $normalizedKey
* @return array|bool Metadata on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
// @see http://pecl.php.net/bugs/bug.php?id=22564
if (! apc_exists($internalKey)) {
$metadata = false;
} else {
$format = APC_ITER_ALL ^ APC_ITER_VALUE ^ APC_ITER_TYPE ^ APC_ITER_REFCOUNT;
$regexp = '/^' . preg_quote($internalKey, '/') . '$/';
$it = new BaseApcIterator('user', $regexp, $format, 100, APC_LIST_ACTIVE);
$metadata = $it->current();
}
if (! $metadata) {
return false;
}
$this->normalizeMetadata($metadata);
return $metadata;
}
/**
* Get metadata of multiple items
*
* @param array $normalizedKeys
* @return array Associative array of keys and metadata
*
* @triggers getMetadatas.pre(PreEvent)
* @triggers getMetadatas.post(PostEvent)
* @triggers getMetadatas.exception(ExceptionEvent)
*/
protected function internalGetMetadatas(array & $normalizedKeys)
{
$keysRegExp = [];
foreach ($normalizedKeys as $normalizedKey) {
$keysRegExp[] = preg_quote($normalizedKey, '/');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefixL = 0;
if ($namespace === '') {
$pattern = '/^(' . implode('|', $keysRegExp) . ')' . '$/';
} else {
$prefix = $namespace . $options->getNamespaceSeparator();
$prefixL = strlen($prefix);
$pattern = '/^' . preg_quote($prefix, '/') . '(' . implode('|', $keysRegExp) . ')' . '$/';
}
$format = APC_ITER_ALL ^ APC_ITER_VALUE ^ APC_ITER_TYPE ^ APC_ITER_REFCOUNT;
$it = new BaseApcIterator('user', $pattern, $format, 100, APC_LIST_ACTIVE);
$result = [];
foreach ($it as $internalKey => $metadata) {
// @see http://pecl.php.net/bugs/bug.php?id=22564
if (! apc_exists($internalKey)) {
continue;
}
$this->normalizeMetadata($metadata);
$result[substr($internalKey, $prefixL)] = $metadata;
}
return $result;
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
if (! apc_store($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"apc_store('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to store multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return array_keys(apc_store($normalizedKeyValuePairs, null, $options->getTtl()));
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$internalKey = $prefix . $normalizedKey;
$internalKeyValuePairs[$internalKey] = $value;
}
$failedKeys = apc_store($internalKeyValuePairs, null, $options->getTtl());
$failedKeys = array_keys($failedKeys);
// remove prefix
$prefixL = strlen($prefix);
foreach ($failedKeys as & $key) {
$key = substr($key, $prefixL);
}
return $failedKeys;
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
if (! apc_add($internalKey, $value, $ttl)) {
if (apc_exists($internalKey)) {
return false;
}
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"apc_add('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to add multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalAddItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return array_keys(apc_add($normalizedKeyValuePairs, null, $options->getTtl()));
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$internalKey = $prefix . $normalizedKey;
$internalKeyValuePairs[$internalKey] = $value;
}
$failedKeys = apc_add($internalKeyValuePairs, null, $options->getTtl());
$failedKeys = array_keys($failedKeys);
// remove prefix
$prefixL = strlen($prefix);
foreach ($failedKeys as & $key) {
$key = substr($key, $prefixL);
}
return $failedKeys;
}
/**
* Internal method to replace an existing item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
if (! apc_exists($internalKey)) {
return false;
}
$ttl = $options->getTtl();
if (! apc_store($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"apc_store('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
return apc_delete($prefix . $normalizedKey);
}
/**
* Internal method to remove multiple items.
*
* @param array $normalizedKeys
* @return array Array of not removed keys
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return apc_delete($normalizedKeys);
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$failedKeys = apc_delete($internalKeys);
// remove prefix
$prefixL = strlen($prefix);
foreach ($failedKeys as & $key) {
$key = substr($key, $prefixL);
}
return $failedKeys;
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$value = (int) $value;
$newValue = apc_inc($internalKey, $value);
// initial value
if ($newValue === false) {
$ttl = $options->getTtl();
$newValue = $value;
if (! apc_add($internalKey, $newValue, $ttl)) {
throw new Exception\RuntimeException(
"apc_add('{$internalKey}', {$newValue}, {$ttl}) failed"
);
}
}
return $newValue;
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$value = (int) $value;
$newValue = apc_dec($internalKey, $value);
// initial value
if ($newValue === false) {
$ttl = $options->getTtl();
$newValue = -$value;
if (! apc_add($internalKey, $newValue, $ttl)) {
throw new Exception\RuntimeException(
"apc_add('{$internalKey}', {$newValue}, {$ttl}) failed"
);
}
}
return $newValue;
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$marker = new stdClass();
$capabilities = new Capabilities(
$this,
$marker,
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => 'object',
'resource' => false,
],
'supportedMetadata' => [
'internal_key',
'atime', 'ctime', 'mtime', 'rtime',
'size', 'hits', 'ttl',
],
'minTtl' => 1,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => (bool) ini_get('apc.use_request_time'),
'maxKeyLength' => 5182,
'namespaceIsPrefix' => true,
'namespaceSeparator' => $this->getOptions()->getNamespaceSeparator(),
]
);
// update namespace separator on change option
$this->getEventManager()->attach('option', function ($event) use ($capabilities, $marker) {
$params = $event->getParams();
if (isset($params['namespace_separator'])) {
$capabilities->setNamespaceSeparator($marker, $params['namespace_separator']);
}
});
$this->capabilities = $capabilities;
$this->capabilityMarker = $marker;
}
return $this->capabilities;
}
/* internal */
/**
* Normalize metadata to work with APC
*
* @param array $metadata
* @return void
*/
protected function normalizeMetadata(array & $metadata)
{
$apcMetadata = $metadata;
$metadata = [
'internal_key' => isset($metadata['key']) ? $metadata['key'] : $metadata['info'],
'atime' => isset($metadata['access_time']) ? $metadata['access_time'] : $metadata['atime'],
'ctime' => isset($metadata['creation_time']) ? $metadata['creation_time'] : $metadata['ctime'],
'mtime' => isset($metadata['modified_time']) ? $metadata['modified_time'] : $metadata['mtime'],
'rtime' => isset($metadata['deletion_time']) ? $metadata['deletion_time'] : $metadata['dtime'],
'size' => $metadata['mem_size'],
'hits' => isset($metadata['nhits']) ? $metadata['nhits'] : $metadata['num_hits'],
'ttl' => $metadata['ttl'],
];
}
/**
* Internal method to set an item only if token matches
*
* @param mixed $token
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @see getItem()
* @see setItem()
*/
protected function internalCheckAndSetItem(& $token, & $normalizedKey, & $value)
{
if (is_int($token) && is_int($value)) {
return apc_cas($normalizedKey, $token, $value);
}
return parent::internalCheckAndSetItem($token, $normalizedKey, $value);
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use APCIterator as BaseApcIterator;
use Zend\Cache\Storage\IteratorInterface;
class ApcIterator implements IteratorInterface
{
/**
* The apc storage instance
*
* @var Apc
*/
protected $storage;
/**
* The iterator mode
*
* @var int
*/
protected $mode = IteratorInterface::CURRENT_AS_KEY;
/**
* The base APCIterator instance
*
* @var BaseApcIterator
*/
protected $baseIterator;
/**
* The length of the namespace prefix
*
* @var int
*/
protected $prefixLength;
/**
* Constructor
*
* @param Apc $storage
* @param BaseApcIterator $baseIterator
* @param string $prefix
*/
public function __construct(Apc $storage, BaseApcIterator $baseIterator, $prefix)
{
$this->storage = $storage;
$this->baseIterator = $baseIterator;
$this->prefixLength = strlen($prefix);
}
/**
* Get storage instance
*
* @return Apc
*/
public function getStorage()
{
return $this->storage;
}
/**
* Get iterator mode
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode()
{
return $this->mode;
}
/**
* Set iterator mode
*
* @param int $mode
* @return ApcIterator Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (int) $mode;
return $this;
}
/* Iterator */
/**
* Get current key, value or metadata.
*
* @return mixed
*/
public function current()
{
if ($this->mode == IteratorInterface::CURRENT_AS_SELF) {
return $this;
}
$key = $this->key();
if ($this->mode == IteratorInterface::CURRENT_AS_VALUE) {
return $this->storage->getItem($key);
} elseif ($this->mode == IteratorInterface::CURRENT_AS_METADATA) {
return $this->storage->getMetadata($key);
}
return $key;
}
/**
* Get current key
*
* @return string
*/
public function key()
{
$key = $this->baseIterator->key();
// remove namespace prefix
return substr($key, $this->prefixLength);
}
/**
* Move forward to next element
*
* @return void
*/
public function next()
{
$this->baseIterator->next();
}
/**
* Checks if current position is valid
*
* @return bool
*/
public function valid()
{
return $this->baseIterator->valid();
}
/**
* Rewind the Iterator to the first element.
*
* @return void
*/
public function rewind()
{
return $this->baseIterator->rewind();
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
/**
* These are options specific to the APC adapter
*/
class ApcOptions extends AdapterOptions
{
/**
* Namespace separator
*
* @var string
*/
protected $namespaceSeparator = ':';
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return ApcOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
}

View File

@@ -0,0 +1,739 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use APCuIterator as BaseApcuIterator;
use stdClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\IterableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class Apcu extends AbstractAdapter implements
AvailableSpaceCapableInterface,
ClearByNamespaceInterface,
ClearByPrefixInterface,
FlushableInterface,
IterableInterface,
TotalSpaceCapableInterface
{
/**
* Buffered total space in bytes
*
* @var null|int|float
*/
protected $totalSpace;
/**
* Constructor
*
* @param null|array|Traversable|ApcuOptions $options
* @throws Exception\ExceptionInterface
*/
public function __construct($options = null)
{
if (version_compare(phpversion('apcu'), '5.1.0', '<')) {
throw new Exception\ExtensionNotLoadedException('Missing ext/apcu >= 5.1.0');
}
if (! ini_get('apc.enabled') || (PHP_SAPI === 'cli' && ! ini_get('apc.enable_cli'))) {
throw new Exception\ExtensionNotLoadedException(
"ext/apcu is disabled - see 'apc.enabled' and 'apc.enable_cli'"
);
}
parent::__construct($options);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|ApcuOptions $options
* @return Apcu
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof ApcuOptions) {
$options = new ApcuOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return ApcuOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new ApcuOptions());
}
return $this->options;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
if ($this->totalSpace === null) {
$smaInfo = apcu_sma_info(true);
$this->totalSpace = $smaInfo['num_seg'] * $smaInfo['seg_size'];
}
return $this->totalSpace;
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
$smaInfo = apcu_sma_info(true);
return $smaInfo['avail_mem'];
}
/* IterableInterface */
/**
* Get the storage iterator
*
* @return ApcuIterator
*/
public function getIterator()
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = '';
$pattern = null;
if ($namespace !== '') {
$prefix = $namespace . $options->getNamespaceSeparator();
$pattern = '/^' . preg_quote($prefix, '/') . '/';
}
$baseIt = new BaseApcuIterator($pattern, 0, 1, APC_LIST_ACTIVE);
return new ApcuIterator($this, $baseIt, $prefix);
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
return apcu_clear_cache();
}
/* ClearByNamespaceInterface */
/**
* Remove items by given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
$options = $this->getOptions();
$prefix = $namespace . $options->getNamespaceSeparator();
$pattern = '/^' . preg_quote($prefix, '/') . '/';
return apcu_delete(new BaseApcuIterator($pattern, 0, 1, APC_LIST_ACTIVE));
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$nsPrefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$pattern = '/^' . preg_quote($nsPrefix . $prefix, '/') . '/';
return apcu_delete(new BaseApcuIterator($pattern, 0, 1, APC_LIST_ACTIVE));
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$result = apcu_fetch($internalKey, $success);
if (! $success) {
return;
}
$casToken = $result;
return $result;
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return apcu_fetch($normalizedKeys);
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$fetch = apcu_fetch($internalKeys);
// remove namespace prefix
$prefixL = strlen($prefix);
$result = [];
foreach ($fetch as $internalKey => $value) {
$result[substr($internalKey, $prefixL)] = $value;
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
return apcu_exists($prefix . $normalizedKey);
}
/**
* Internal method to test multiple items.
*
* @param array $normalizedKeys
* @return array Array of found keys
* @throws Exception\ExceptionInterface
*/
protected function internalHasItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
// array_filter with no callback will remove entries equal to FALSE
return array_keys(array_filter(apcu_exists($normalizedKeys)));
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$exists = apcu_exists($internalKeys);
$result = [];
$prefixL = strlen($prefix);
foreach ($exists as $internalKey => $bool) {
if ($bool === true) {
$result[] = substr($internalKey, $prefixL);
}
}
return $result;
}
/**
* Get metadata of an item.
*
* @param string $normalizedKey
* @return array|bool Metadata on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$format = APC_ITER_ALL ^ APC_ITER_VALUE ^ APC_ITER_TYPE ^ APC_ITER_REFCOUNT;
$regexp = '/^' . preg_quote($internalKey, '/') . '$/';
$it = new BaseApcuIterator($regexp, $format, 100, APC_LIST_ACTIVE);
$metadata = $it->current();
if (! $metadata) {
return false;
}
$this->normalizeMetadata($metadata);
return $metadata;
}
/**
* Get metadata of multiple items
*
* @param array $normalizedKeys
* @return array Associative array of keys and metadata
*
* @triggers getMetadatas.pre(PreEvent)
* @triggers getMetadatas.post(PostEvent)
* @triggers getMetadatas.exception(ExceptionEvent)
*/
protected function internalGetMetadatas(array & $normalizedKeys)
{
$keysRegExp = [];
foreach ($normalizedKeys as $normalizedKey) {
$keysRegExp[] = preg_quote($normalizedKey, '/');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefixL = 0;
if ($namespace === '') {
$pattern = '/^(' . implode('|', $keysRegExp) . ')' . '$/';
} else {
$prefix = $namespace . $options->getNamespaceSeparator();
$prefixL = strlen($prefix);
$pattern = '/^' . preg_quote($prefix, '/') . '(' . implode('|', $keysRegExp) . ')' . '$/';
}
$format = APC_ITER_ALL ^ APC_ITER_VALUE ^ APC_ITER_TYPE ^ APC_ITER_REFCOUNT;
$it = new BaseApcuIterator($pattern, $format, 100, APC_LIST_ACTIVE);
$result = [];
foreach ($it as $internalKey => $metadata) {
$this->normalizeMetadata($metadata);
$result[substr($internalKey, $prefixL)] = $metadata;
}
return $result;
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
if (! apcu_store($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"apcu_store('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to store multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return array_keys(apcu_store($normalizedKeyValuePairs, null, $options->getTtl()));
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$internalKey = $prefix . $normalizedKey;
$internalKeyValuePairs[$internalKey] = $value;
}
$failedKeys = apcu_store($internalKeyValuePairs, null, $options->getTtl());
$failedKeys = array_keys($failedKeys);
// remove prefix
$prefixL = strlen($prefix);
foreach ($failedKeys as $key) {
$key = substr($key, $prefixL);
}
return $failedKeys;
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
if (! apcu_add($internalKey, $value, $ttl)) {
if (apcu_exists($internalKey)) {
return false;
}
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"apcu_add('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to add multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalAddItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return array_keys(apcu_add($normalizedKeyValuePairs, null, $options->getTtl()));
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$internalKey = $prefix . $normalizedKey;
$internalKeyValuePairs[$internalKey] = $value;
}
$failedKeys = apcu_add($internalKeyValuePairs, null, $options->getTtl());
$failedKeys = array_keys($failedKeys);
// remove prefix
$prefixL = strlen($prefix);
foreach ($failedKeys as & $key) {
$key = substr($key, $prefixL);
}
return $failedKeys;
}
/**
* Internal method to replace an existing item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$ttl = $options->getTtl();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
if (! apcu_exists($internalKey)) {
return false;
}
if (! apcu_store($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"apcu_store('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to set an item only if token matches
*
* @param mixed $token
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @see getItem()
* @see setItem()
*/
protected function internalCheckAndSetItem(& $token, & $normalizedKey, & $value)
{
if (is_int($token) && is_int($value)) {
return apcu_cas($normalizedKey, $token, $value);
}
return parent::internalCheckAndSetItem($token, $normalizedKey, $value);
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
return apcu_delete($prefix . $normalizedKey);
}
/**
* Internal method to remove multiple items.
*
* @param array $normalizedKeys
* @return array Array of not removed keys
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return apcu_delete($normalizedKeys);
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$failedKeys = apcu_delete($internalKeys);
// remove prefix
$prefixL = strlen($prefix);
foreach ($failedKeys as & $key) {
$key = substr($key, $prefixL);
}
return $failedKeys;
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$value = (int) $value;
$newValue = apcu_inc($internalKey, $value);
// initial value
if ($newValue === false) {
$ttl = $options->getTtl();
$newValue = $value;
if (! apcu_add($internalKey, $newValue, $ttl)) {
throw new Exception\RuntimeException(
"apcu_add('{$internalKey}', {$newValue}, {$ttl}) failed"
);
}
}
return $newValue;
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$value = (int) $value;
$newValue = apcu_dec($internalKey, $value);
// initial value
if ($newValue === false) {
$ttl = $options->getTtl();
$newValue = -$value;
if (! apcu_add($internalKey, $newValue, $ttl)) {
throw new Exception\RuntimeException(
"apcu_add('{$internalKey}', {$newValue}, {$ttl}) failed"
);
}
}
return $newValue;
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$marker = new stdClass();
$capabilities = new Capabilities(
$this,
$marker,
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => 'object',
'resource' => false,
],
'supportedMetadata' => [
'internal_key',
'atime', 'ctime', 'mtime', 'rtime',
'size', 'hits', 'ttl',
],
'minTtl' => 1,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => (bool) ini_get('apc.use_request_time'),
'maxKeyLength' => 5182,
'namespaceIsPrefix' => true,
'namespaceSeparator' => $this->getOptions()->getNamespaceSeparator(),
]
);
// update namespace separator on change option
$this->getEventManager()->attach('option', function ($event) use ($capabilities, $marker) {
$params = $event->getParams();
if (isset($params['namespace_separator'])) {
$capabilities->setNamespaceSeparator($marker, $params['namespace_separator']);
}
});
$this->capabilities = $capabilities;
$this->capabilityMarker = $marker;
}
return $this->capabilities;
}
/* internal */
/**
* Normalize metadata to work with APC
*
* @param array $metadata
* @return void
*/
protected function normalizeMetadata(array & $metadata)
{
$apcMetadata = $metadata;
$metadata = [
'internal_key' => $metadata['key'],
'atime' => $metadata['access_time'],
'ctime' => $metadata['creation_time'],
'mtime' => $metadata['mtime'],
'rtime' => $metadata['deletion_time'],
'size' => $metadata['mem_size'],
'hits' => $metadata['num_hits'],
'ttl' => $metadata['ttl'],
];
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Storage\IteratorInterface;
use APCuIterator as BaseApcuIterator;
class ApcuIterator implements IteratorInterface
{
/**
* The storage instance
*
* @var Apcu
*/
protected $storage;
/**
* The iterator mode
*
* @var int
*/
protected $mode = IteratorInterface::CURRENT_AS_KEY;
/**
* The base APCIterator instance
*
* @var APCIterator
*/
protected $baseIterator;
/**
* The length of the namespace prefix
*
* @var int
*/
protected $prefixLength;
/**
* Constructor
*
* @param Apcu $storage
* @param BaseApcuIterator $baseIterator
* @param string $prefix
*/
public function __construct(Apcu $storage, BaseApcuIterator $baseIterator, $prefix)
{
$this->storage = $storage;
$this->baseIterator = $baseIterator;
$this->prefixLength = strlen($prefix);
}
/**
* Get storage instance
*
* @return Apcu
*/
public function getStorage()
{
return $this->storage;
}
/**
* Get iterator mode
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode()
{
return $this->mode;
}
/**
* Set iterator mode
*
* @param int $mode
* @return ApcuIterator Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (int) $mode;
return $this;
}
/* Iterator */
/**
* Get current key, value or metadata.
*
* @return mixed
*/
public function current()
{
if ($this->mode == IteratorInterface::CURRENT_AS_SELF) {
return $this;
}
$key = $this->key();
if ($this->mode == IteratorInterface::CURRENT_AS_VALUE) {
return $this->storage->getItem($key);
} elseif ($this->mode == IteratorInterface::CURRENT_AS_METADATA) {
return $this->storage->getMetadata($key);
}
return $key;
}
/**
* Get current key
*
* @return string
*/
public function key()
{
$key = $this->baseIterator->key();
// remove namespace prefix
return substr($key, $this->prefixLength);
}
/**
* Move forward to next element
*
* @return void
*/
public function next()
{
$this->baseIterator->next();
}
/**
* Checks if current position is valid
*
* @return bool
*/
public function valid()
{
return $this->baseIterator->valid();
}
/**
* Rewind the Iterator to the first element.
*
* @return void
*/
public function rewind()
{
return $this->baseIterator->rewind();
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
/**
* These are options specific to the APCu adapter
*/
class ApcuOptions extends AdapterOptions
{
/**
* Namespace separator
*
* @var string
*/
protected $namespaceSeparator = ':';
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return ApcuOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
}

View File

@@ -0,0 +1,502 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use stdClass;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Storage\ClearExpiredInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\IterableInterface;
use Zend\Cache\Storage\OptimizableInterface;
use Zend\Cache\Storage\StorageInterface;
use Zend\Cache\Storage\TaggableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class BlackHole implements
StorageInterface,
AvailableSpaceCapableInterface,
ClearByNamespaceInterface,
ClearByPrefixInterface,
ClearExpiredInterface,
FlushableInterface,
IterableInterface,
OptimizableInterface,
TaggableInterface,
TotalSpaceCapableInterface
{
/**
* Capabilities of this adapter
*
* @var null|Capabilities
*/
protected $capabilities = null;
/**
* Marker to change capabilities
*
* @var null|object
*/
protected $capabilityMarker;
/**
* options
*
* @var null|AdapterOptions
*/
protected $options;
/**
* Constructor
*
* @param null|array|\Traversable|AdapterOptions $options
*/
public function __construct($options = null)
{
if ($options) {
$this->setOptions($options);
}
}
/**
* Set options.
*
* @param array|\Traversable|AdapterOptions $options
* @return BlackHole Provides a fluent interface
*/
public function setOptions($options)
{
if ($this->options !== $options) {
if (! $options instanceof AdapterOptions) {
$options = new AdapterOptions($options);
}
if ($this->options) {
$this->options->setAdapter(null);
}
$options->setAdapter($this);
$this->options = $options;
}
return $this;
}
/**
* Get options
*
* @return AdapterOptions
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new AdapterOptions());
}
return $this->options;
}
/**
* Get an item.
*
* @param string $key
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
*/
public function getItem($key, & $success = null, & $casToken = null)
{
$success = false;
return;
}
/**
* Get multiple items.
*
* @param array $keys
* @return array Associative array of keys and values
*/
public function getItems(array $keys)
{
return [];
}
/**
* Test if an item exists.
*
* @param string $key
* @return bool
*/
public function hasItem($key)
{
return false;
}
/**
* Test multiple items.
*
* @param array $keys
* @return array Array of found keys
*/
public function hasItems(array $keys)
{
return [];
}
/**
* Get metadata of an item.
*
* @param string $key
* @return array|bool Metadata on success, false on failure
*/
public function getMetadata($key)
{
return false;
}
/**
* Get multiple metadata
*
* @param array $keys
* @return array Associative array of keys and metadata
*/
public function getMetadatas(array $keys)
{
return [];
}
/**
* Store an item.
*
* @param string $key
* @param mixed $value
* @return bool
*/
public function setItem($key, $value)
{
return false;
}
/**
* Store multiple items.
*
* @param array $keyValuePairs
* @return array Array of not stored keys
*/
public function setItems(array $keyValuePairs)
{
return array_keys($keyValuePairs);
}
/**
* Add an item.
*
* @param string $key
* @param mixed $value
* @return bool
*/
public function addItem($key, $value)
{
return false;
}
/**
* Add multiple items.
*
* @param array $keyValuePairs
* @return array Array of not stored keys
*/
public function addItems(array $keyValuePairs)
{
return array_keys($keyValuePairs);
}
/**
* Replace an existing item.
*
* @param string $key
* @param mixed $value
* @return bool
*/
public function replaceItem($key, $value)
{
return false;
}
/**
* Replace multiple existing items.
*
* @param array $keyValuePairs
* @return array Array of not stored keys
*/
public function replaceItems(array $keyValuePairs)
{
return array_keys($keyValuePairs);
}
/**
* Set an item only if token matches
*
* It uses the token received from getItem() to check if the item has
* changed before overwriting it.
*
* @param mixed $token
* @param string $key
* @param mixed $value
* @return bool
*/
public function checkAndSetItem($token, $key, $value)
{
return false;
}
/**
* Reset lifetime of an item
*
* @param string $key
* @return bool
*/
public function touchItem($key)
{
return false;
}
/**
* Reset lifetime of multiple items.
*
* @param array $keys
* @return array Array of not updated keys
*/
public function touchItems(array $keys)
{
return $keys;
}
/**
* Remove an item.
*
* @param string $key
* @return bool
*/
public function removeItem($key)
{
return false;
}
/**
* Remove multiple items.
*
* @param array $keys
* @return array Array of not removed keys
*/
public function removeItems(array $keys)
{
return $keys;
}
/**
* Increment an item.
*
* @param string $key
* @param int $value
* @return int|bool The new value on success, false on failure
*/
public function incrementItem($key, $value)
{
return false;
}
/**
* Increment multiple items.
*
* @param array $keyValuePairs
* @return array Associative array of keys and new values
*/
public function incrementItems(array $keyValuePairs)
{
return [];
}
/**
* Decrement an item.
*
* @param string $key
* @param int $value
* @return int|bool The new value on success, false on failure
*/
public function decrementItem($key, $value)
{
return false;
}
/**
* Decrement multiple items.
*
* @param array $keyValuePairs
* @return array Associative array of keys and new values
*/
public function decrementItems(array $keyValuePairs)
{
return [];
}
/**
* Capabilities of this storage
*
* @return Capabilities
*/
public function getCapabilities()
{
if ($this->capabilities === null) {
// use default capabilities only
$this->capabilityMarker = new stdClass();
$this->capabilities = new Capabilities($this, $this->capabilityMarker);
}
return $this->capabilities;
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
return 0;
}
/* ClearByNamespaceInterface */
/**
* Remove items of given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
return false;
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
return false;
}
/* ClearExpiredInterface */
/**
* Remove expired items
*
* @return bool
*/
public function clearExpired()
{
return false;
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
return false;
}
/* IterableInterface */
/**
* Get the storage iterator
*
* @return KeyListIterator
*/
public function getIterator()
{
return new KeyListIterator($this, []);
}
/* OptimizableInterface */
/**
* Optimize the storage
*
* @return bool
*/
public function optimize()
{
return false;
}
/* TaggableInterface */
/**
* Set tags to an item by given key.
* An empty array will remove all tags.
*
* @param string $key
* @param string[] $tags
* @return bool
*/
public function setTags($key, array $tags)
{
return false;
}
/**
* Get tags of an item by given key
*
* @param string $key
* @return string[]|FALSE
*/
public function getTags($key)
{
return false;
}
/**
* Remove items matching given tags.
*
* If $disjunction only one of the given tags must match
* else all given tags must match.
*
* @param string[] $tags
* @param bool $disjunction
* @return bool
*/
public function clearByTags(array $tags, $disjunction = false)
{
return false;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
return 0;
}
}

View File

@@ -0,0 +1,547 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use stdClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\IterableInterface;
use Zend\Cache\Storage\OptimizableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
use Zend\Stdlib\ErrorHandler;
class Dba extends AbstractAdapter implements
AvailableSpaceCapableInterface,
ClearByNamespaceInterface,
ClearByPrefixInterface,
FlushableInterface,
IterableInterface,
OptimizableInterface,
TotalSpaceCapableInterface
{
/**
* The DBA resource handle
*
* @var null|resource
*/
protected $handle;
/**
* Buffered total space in bytes
*
* @var null|int|float
*/
protected $totalSpace;
/**
* Constructor
*
* @param null|array|Traversable|DbaOptions $options
* @throws Exception\ExceptionInterface
*/
public function __construct($options = null)
{
if (! extension_loaded('dba')) {
throw new Exception\ExtensionNotLoadedException('Missing ext/dba');
}
parent::__construct($options);
}
/**
* Destructor
*
* Closes an open dba resource
*
* @see AbstractAdapter::__destruct()
* @return void
*/
public function __destruct()
{
$this->_close();
parent::__destruct();
}
/* options */
/**
* Set options.
*
* @param array|Traversable|DbaOptions $options
* @return self
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof DbaOptions) {
$options = new DbaOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return DbaOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new DbaOptions());
}
return $this->options;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
if ($this->totalSpace === null) {
$pathname = $this->getOptions()->getPathname();
if ($pathname === '') {
throw new Exception\LogicException('No pathname to database file');
}
ErrorHandler::start();
$total = disk_total_space(dirname($pathname));
$error = ErrorHandler::stop();
if ($total === false) {
throw new Exception\RuntimeException("Can't detect total space of '{$pathname}'", 0, $error);
}
$this->totalSpace = $total;
// clean total space buffer on change pathname
$events = $this->getEventManager();
$handle = null;
$totalSpace = & $this->totalSpace;
$callback = function ($event) use (& $events, & $handle, & $totalSpace) {
$params = $event->getParams();
if (isset($params['pathname'])) {
$totalSpace = null;
$events->detach($handle);
}
};
$events->attach('option', $callback);
}
return $this->totalSpace;
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return float
*/
public function getAvailableSpace()
{
$pathname = $this->getOptions()->getPathname();
if ($pathname === '') {
throw new Exception\LogicException('No pathname to database file');
}
ErrorHandler::start();
$avail = disk_free_space(dirname($pathname));
$error = ErrorHandler::stop();
if ($avail === false) {
throw new Exception\RuntimeException("Can't detect free space of '{$pathname}'", 0, $error);
}
return $avail;
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
$pathname = $this->getOptions()->getPathname();
if ($pathname === '') {
throw new Exception\LogicException('No pathname to database file');
}
if (file_exists($pathname)) {
// close the dba file before delete
// and reopen (create) on next use
$this->_close();
ErrorHandler::start();
$result = unlink($pathname);
$error = ErrorHandler::stop();
if (! $result) {
throw new Exception\RuntimeException("unlink('{$pathname}') failed", 0, $error);
}
}
return true;
}
/* ClearByNamespaceInterface */
/**
* Remove items by given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
$prefix = $namespace . $this->getOptions()->getNamespaceSeparator();
$result = true;
$this->_open();
do {
// Workaround for PHP-Bug #62491 & #62492
$recheck = false;
$internalKey = dba_firstkey($this->handle);
while ($internalKey !== false && $internalKey !== null) {
if (strpos($internalKey, $prefix) === 0) {
$result = dba_delete($internalKey, $this->handle) && $result;
}
$internalKey = dba_nextkey($this->handle);
}
} while ($recheck);
return $result;
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator() . $prefix;
$result = true;
$this->_open();
// Workaround for PHP-Bug #62491 & #62492
do {
$recheck = false;
$internalKey = dba_firstkey($this->handle);
while ($internalKey !== false && $internalKey !== null) {
if (strpos($internalKey, $prefix) === 0) {
$result = dba_delete($internalKey, $this->handle) && $result;
$recheck = true;
}
$internalKey = dba_nextkey($this->handle);
}
} while ($recheck);
return $result;
}
/* IterableInterface */
/**
* Get the storage iterator
*
* @return DbaIterator
*/
public function getIterator()
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
return new DbaIterator($this, $this->handle, $prefix);
}
/* OptimizableInterface */
/**
* Optimize the storage
*
* @return bool
* @throws Exception\RuntimeException
*/
public function optimize()
{
$this->_open();
if (! dba_optimize($this->handle)) {
throw new Exception\RuntimeException('dba_optimize failed');
}
return true;
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$this->_open();
$value = dba_fetch($prefix . $normalizedKey, $this->handle);
if ($value === false) {
$success = false;
return;
}
$success = true;
$casToken = $value;
return $value;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$this->_open();
return dba_exists($prefix . $normalizedKey, $this->handle);
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$cacheableValue = (string) $value; // dba_replace requires a string
$this->_open();
if (! dba_replace($internalKey, $cacheableValue, $this->handle)) {
throw new Exception\RuntimeException("dba_replace('{$internalKey}', ...) failed");
}
return true;
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$this->_open();
// Workaround for PHP-Bug #54242 & #62489
if (dba_exists($internalKey, $this->handle)) {
return false;
}
// Workaround for PHP-Bug #54242 & #62489
// dba_insert returns true if key already exists
ErrorHandler::start();
$result = dba_insert($internalKey, $value, $this->handle);
$error = ErrorHandler::stop();
if (! $result || $error) {
return false;
}
return true;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$this->_open();
// Workaround for PHP-Bug #62490
if (! dba_exists($internalKey, $this->handle)) {
return false;
}
return dba_delete($internalKey, $this->handle);
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$marker = new stdClass();
$capabilities = new Capabilities(
$this,
$marker,
[
'supportedDatatypes' => [
'NULL' => 'string',
'boolean' => 'string',
'integer' => 'string',
'double' => 'string',
'string' => true,
'array' => false,
'object' => false,
'resource' => false,
],
'minTtl' => 0,
'supportedMetadata' => [],
'maxKeyLength' => 0, // TODO: maxKeyLength ????
'namespaceIsPrefix' => true,
'namespaceSeparator' => $this->getOptions()->getNamespaceSeparator(),
]
);
// update namespace separator on change option
$this->getEventManager()->attach('option', function ($event) use ($capabilities, $marker) {
$params = $event->getParams();
if (isset($params['namespace_separator'])) {
$capabilities->setNamespaceSeparator($marker, $params['namespace_separator']);
}
});
$this->capabilities = $capabilities;
$this->capabilityMarker = $marker;
}
return $this->capabilities;
}
/**
* Open the database if not already done.
*
* @return void
* @throws Exception\LogicException
* @throws Exception\RuntimeException
*/
// @codingStandardsIgnoreStart
protected function _open()
{
// @codingStandardsIgnoreEnd
if (! $this->handle) {
$options = $this->getOptions();
$pathname = $options->getPathname();
$mode = $options->getMode();
$handler = $options->getHandler();
if ($pathname === '') {
throw new Exception\LogicException('No pathname to database file');
}
ErrorHandler::start();
$dba = dba_open($pathname, $mode, $handler);
$err = ErrorHandler::stop();
if (! $dba) {
throw new Exception\RuntimeException(
"dba_open('{$pathname}', '{$mode}', '{$handler}') failed",
0,
$err
);
}
$this->handle = $dba;
}
}
/**
* Close database file if opened
*
* @return void
*/
// @codingStandardsIgnoreStart
protected function _close()
{
// @codingStandardsIgnoreEnd
if ($this->handle) {
ErrorHandler::start(E_NOTICE);
dba_close($this->handle);
ErrorHandler::stop();
$this->handle = null;
}
}
}

View File

@@ -0,0 +1,190 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Exception;
use Zend\Cache\Storage\IteratorInterface;
class DbaIterator implements IteratorInterface
{
/**
* The apc storage instance
*
* @var Dba
*/
protected $storage;
/**
* The iterator mode
*
* @var int
*/
protected $mode = IteratorInterface::CURRENT_AS_KEY;
/**
* The dba resource handle
*
* @var resource
*/
protected $handle;
/**
* The length of the namespace prefix
*
* @var int
*/
protected $prefixLength;
/**
* The current internal key
*
* @var string|bool
*/
protected $currentInternalKey;
/**
* Constructor
*
* @param Dba $storage
* @param resource $handle
* @param string $prefix
*/
public function __construct(Dba $storage, $handle, $prefix)
{
$this->storage = $storage;
$this->handle = $handle;
$this->prefixLength = strlen($prefix);
$this->rewind();
}
/**
* Get storage instance
*
* @return Dba
*/
public function getStorage()
{
return $this->storage;
}
/**
* Get iterator mode
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode()
{
return $this->mode;
}
/**
* Set iterator mode
*
* @param int $mode
* @return DbaIterator Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (int) $mode;
return $this;
}
/* Iterator */
/**
* Get current key, value or metadata.
*
* @return mixed
* @throws Exception\RuntimeException
*/
public function current()
{
if ($this->mode == IteratorInterface::CURRENT_AS_SELF) {
return $this;
}
$key = $this->key();
if ($this->mode == IteratorInterface::CURRENT_AS_VALUE) {
return $this->storage->getItem($key);
} elseif ($this->mode == IteratorInterface::CURRENT_AS_METADATA) {
return $this->storage->getMetadata($key);
}
return $key;
}
/**
* Get current key
*
* @return string
* @throws Exception\RuntimeException
*/
public function key()
{
if ($this->currentInternalKey === false) {
throw new Exception\RuntimeException("Iterator is on an invalid state");
}
// remove namespace prefix
return substr($this->currentInternalKey, $this->prefixLength);
}
/**
* Move forward to next element
*
* @return void
* @throws Exception\RuntimeException
*/
public function next()
{
if ($this->currentInternalKey === false) {
throw new Exception\RuntimeException("Iterator is on an invalid state");
}
$this->currentInternalKey = dba_nextkey($this->handle);
// Workaround for PHP-Bug #62492
if ($this->currentInternalKey === null) {
$this->currentInternalKey = false;
}
}
/**
* Checks if current position is valid
*
* @return bool
*/
public function valid()
{
return ($this->currentInternalKey !== false);
}
/**
* Rewind the Iterator to the first element.
*
* @return void
* @throws Exception\RuntimeException
*/
public function rewind()
{
if ($this->currentInternalKey === false) {
throw new Exception\RuntimeException("Iterator is on an invalid state");
}
$this->currentInternalKey = dba_firstkey($this->handle);
// Workaround for PHP-Bug #62492
if ($this->currentInternalKey === null) {
$this->currentInternalKey = false;
}
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Exception;
/**
* These are options specific to the APC adapter
*/
class DbaOptions extends AdapterOptions
{
/**
* Namespace separator
*
* @var string
*/
protected $namespaceSeparator = ':';
/**
* Pathname to the database file
*
* @var string
*/
protected $pathname = '';
/**
* The mode to open the database
*
* @var string
*/
protected $mode = 'c';
/**
* The name of the handler which shall be used for accessing the database.
*
* @var string
*/
protected $handler = 'flatfile';
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return DbaOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Set pathname to database file
*
* @param string $pathname
* @return DbaOptions Provides a fluent interface
*/
public function setPathname($pathname)
{
$this->pathname = (string) $pathname;
$this->triggerOptionEvent('pathname', $pathname);
return $this;
}
/**
* Get pathname to database file
*
* @return string
*/
public function getPathname()
{
return $this->pathname;
}
/**
*
*
* @param string $mode
* @return DbaOptions Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (string) $mode;
$this->triggerOptionEvent('mode', $mode);
return $this;
}
public function getMode()
{
return $this->mode;
}
/**
*
*
* @param string $handler
* @return DbaOptions Provides a fluent interface
*/
public function setHandler($handler)
{
$handler = (string) $handler;
if (! function_exists('dba_handlers') || ! in_array($handler, dba_handlers())) {
throw new Exception\ExtensionNotLoadedException("DBA-Handler '{$handler}' not supported");
}
if ($handler === 'inifile') {
throw new Exception\ExtensionNotLoadedException(
"DBA-Handler 'inifile' does not reliably support write operations"
);
}
$this->triggerOptionEvent('handler', $handler);
$this->handler = $handler;
return $this;
}
public function getHandler()
{
return $this->handler;
}
}

View File

@@ -0,0 +1,284 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use MongoDB\Client;
use MongoDB\Collection;
use MongoDB\BSON\UTCDateTime as MongoDate;
use MongoDB\Driver\Exception\Exception as MongoDriverException;
use stdClass;
use Zend\Cache\Exception;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\FlushableInterface;
/**
* Cache storage adapter for ext-mongodb
*
* If you are using ext-mongo, use the MongoDb adapter instead.
*/
class ExtMongoDb extends AbstractAdapter implements FlushableInterface
{
/**
* Has this instance be initialized
*
* @var bool
*/
private $initialized = false;
/**
* the mongodb resource manager
*
* @var null|ExtMongoDbResourceManager
*/
private $resourceManager;
/**
* The mongodb resource id
*
* @var null|string
*/
private $resourceId;
/**
* The namespace prefix
*
* @var string
*/
private $namespacePrefix = '';
/**
* {@inheritDoc}
*
* @throws Exception\ExtensionNotLoadedException
*/
public function __construct($options = null)
{
if (! extension_loaded('mongodb') || ! class_exists(Client::class)) {
throw new Exception\ExtensionNotLoadedException(
'mongodb extension not loaded or Mongo PHP client library not installed'
);
}
parent::__construct($options);
$initialized = & $this->initialized;
$this->getEventManager()->attach(
'option',
function () use (& $initialized) {
$initialized = false;
}
);
}
/**
* get mongodb resource
*
* @return Collection
*/
private function getMongoCollection()
{
if (! $this->initialized) {
$options = $this->getOptions();
$this->resourceManager = $options->getResourceManager();
$this->resourceId = $options->getResourceId();
$namespace = $options->getNamespace();
$this->namespacePrefix = ($namespace === '' ? '' : $namespace . $options->getNamespaceSeparator());
$this->initialized = true;
}
return $this->resourceManager->getResource($this->resourceId);
}
/**
* {@inheritDoc}
*/
public function setOptions($options)
{
return parent::setOptions(
$options instanceof ExtMongoDbOptions
? $options
: new ExtMongoDbOptions($options)
);
}
/**
* Get options.
*
* @see setOptions()
* @return ExtMongoDbOptions
*/
public function getOptions()
{
return $this->options;
}
/**
* {@inheritDoc}
*
* @throws Exception\RuntimeException
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$result = $this->fetchFromCollection($normalizedKey);
$success = false;
if (null === $result) {
return;
}
if (isset($result['expires'])) {
if (! $result['expires'] instanceof MongoDate) {
throw new Exception\RuntimeException(sprintf(
"The found item _id '%s' for key '%s' is not a valid cache item"
. ": the field 'expired' isn't an instance of MongoDate, '%s' found instead",
(string) $result['_id'],
$this->namespacePrefix . $normalizedKey,
is_object($result['expires']) ? get_class($result['expires']) : gettype($result['expires'])
));
}
if ($result['expires']->sec < (new MongoDate())) {
$this->internalRemoveItem($normalizedKey);
return;
}
}
if (! array_key_exists('value', $result)) {
throw new Exception\RuntimeException(sprintf(
"The found item _id '%s' for key '%s' is not a valid cache item: missing the field 'value'",
(string) $result['_id'],
$this->namespacePrefix . $normalizedKey
));
}
$success = true;
return $casToken = $result['value'];
}
/**
* {@inheritDoc}
*
* @throws Exception\RuntimeException
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$mongo = $this->getMongoCollection();
$key = $this->namespacePrefix . $normalizedKey;
$ttl = $this->getOptions()->getTTl();
$expires = null;
$cacheItem = [
'key' => $key,
'value' => $value,
];
if ($ttl > 0) {
$ttlSeconds = round((microtime(true) + $ttl) * 1000);
$cacheItem['expires'] = new MongoDate($ttlSeconds);
}
try {
$mongo->deleteOne(['key' => $key]);
$result = $mongo->insertOne($cacheItem);
} catch (MongoDriverException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
return null !== $result && $result->isAcknowledged();
}
/**
* {@inheritDoc}
*
* @throws Exception\RuntimeException
*/
protected function internalRemoveItem(& $normalizedKey)
{
try {
$result = $this->getMongoCollection()->deleteOne(['key' => $this->namespacePrefix . $normalizedKey]);
} catch (MongoDriverException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
return null !== $result && $result->getDeletedCount() > 0;
}
/**
* {@inheritDoc}
*/
public function flush()
{
$result = $this->getMongoCollection()->drop();
return ((float) 1) === $result['ok'];
}
/**
* {@inheritDoc}
*/
protected function internalGetCapabilities()
{
if ($this->capabilities) {
return $this->capabilities;
}
return $this->capabilities = new Capabilities(
$this,
$this->capabilityMarker = new stdClass(),
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => false,
'resource' => false,
],
'supportedMetadata' => [
'_id',
],
'minTtl' => 1,
'staticTtl' => true,
'maxKeyLength' => 255,
'namespaceIsPrefix' => true,
]
);
}
/**
* {@inheritDoc}
*
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$result = $this->fetchFromCollection($normalizedKey);
return null !== $result ? ['_id' => $result['_id']] : false;
}
/**
* Return raw records from MongoCollection
*
* @param string $normalizedKey
*
* @return array|null
*
* @throws Exception\RuntimeException
*/
private function fetchFromCollection(& $normalizedKey)
{
try {
return $this->getMongoCollection()->findOne(['key' => $this->namespacePrefix . $normalizedKey]);
} catch (MongoDriverException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
}
}

View File

@@ -0,0 +1,187 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
/**
* Options for the ext-mongodb adapter implementation.
*
* If you are using ext-mongo, use the MongoDbOptions class instead.
*/
class ExtMongoDbOptions extends AdapterOptions
{
// @codingStandardsIgnoreStart
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = [
'resource_manager',
'resource_id'
];
// @codingStandardsIgnoreEnd
/**
* The namespace separator
*
* @var string
*/
private $namespaceSeparator = ':';
/**
* The ext-mongodb resource manager
*
* @var null|ExtMongoDbResourceManager
*/
private $resourceManager;
/**
* The resource id of the resource manager
*
* @var string
*/
private $resourceId = 'default';
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return self Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
if ($this->namespaceSeparator !== $namespaceSeparator) {
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
}
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Set the ext-mongodb resource manager to use
*
* @param null|ExtMongoDbResourceManager $resourceManager
* @return self Provides a fluent interface
*/
public function setResourceManager(ExtMongoDbResourceManager $resourceManager = null)
{
if ($this->resourceManager !== $resourceManager) {
$this->triggerOptionEvent('resource_manager', $resourceManager);
$this->resourceManager = $resourceManager;
}
return $this;
}
/**
* Get the ext-mongodb resource manager
*
* @return ExtMongoDbResourceManager
*/
public function getResourceManager()
{
return $this->resourceManager ?: $this->resourceManager = new ExtMongoDbResourceManager();
}
/**
* Get the ext-mongodb resource id
*
* @return string
*/
public function getResourceId()
{
return $this->resourceId;
}
/**
* Set the ext-mongodb resource id
*
* @param string $resourceId
* @return self Provides a fluent interface
*/
public function setResourceId($resourceId)
{
$resourceId = (string) $resourceId;
if ($this->resourceId !== $resourceId) {
$this->triggerOptionEvent('resource_id', $resourceId);
$this->resourceId = $resourceId;
}
return $this;
}
/**
* Set the ext-mongodb server
*
* @param string $server
* @return self Provides a fluent interface
*/
public function setServer($server)
{
$this->getResourceManager()->setServer($this->getResourceId(), $server);
return $this;
}
/**
* @param array $connectionOptions
* @return self Provides a fluent interface
*/
public function setConnectionOptions(array $connectionOptions)
{
$this->getResourceManager()->setConnectionOptions($this->getResourceId(), $connectionOptions);
return $this;
}
/**
* @param array $driverOptions ext-mongodb driver options
* @return self Provides a fluent interface
*/
public function setDriverOptions(array $driverOptions)
{
$this->getResourceManager()->setDriverOptions($this->getResourceId(), $driverOptions);
return $this;
}
/**
* @param string $database
* @return string Provides a fluent interface
*/
public function setDatabase($database)
{
$this->getResourceManager()->setDatabase($this->getResourceId(), $database);
return $this;
}
/**
* @param string $collection
* @return self Provides a fluent interface
*/
public function setCollection($collection)
{
$this->getResourceManager()->setCollection($this->getResourceId(), $collection);
return $this;
}
}

View File

@@ -0,0 +1,253 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2018-2019 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use MongoDB\Client;
use MongoDB\Collection;
use MongoDB\Driver\Exception\Exception as MongoDriverException;
use Zend\Cache\Exception;
/**
* Resource manager for the ext-mongodb adapter.
*
* If you are using ext-mongo, use the MongoDbResourceManager instead.
*/
class ExtMongoDbResourceManager
{
/**
* Registered resources
*
* @var array[]
*/
private $resources = [];
/**
* Check if a resource exists
*
* @param string $id
*
* @return bool
*/
public function hasResource($id)
{
return isset($this->resources[$id]);
}
/**
* Set a resource
*
* @param string $id
* @param array|Collection $resource
* @return self Provides a fluent interface
* @throws Exception\RuntimeException
*/
public function setResource($id, $resource)
{
if ($resource instanceof Collection) {
$this->resources[$id] = [
'db' => (string) $resource->db,
'db_instance' => $resource->db,
'collection' => (string) $resource,
'collection_instance' => $resource,
];
return $this;
}
if (! is_array($resource)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects an array or %s; received %s',
__METHOD__,
Collection::class,
is_object($resource) ? get_class($resource) : gettype($resource)
));
}
$this->resources[$id] = $resource;
return $this;
}
/**
* Instantiate and return the Collection resource
*
* @param string $id
* @return Collection
* @throws Exception\RuntimeException
*/
public function getResource($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = $this->resources[$id];
if (! isset($resource['collection_instance'])) {
try {
if (! isset($resource['db_instance'])) {
if (! isset($resource['client_instance'])) {
$resource['client_instance'] = new Client(
isset($resource['server']) ? $resource['server'] : null,
isset($resource['connection_options']) ? $resource['connection_options'] : [],
isset($resource['driver_options']) ? $resource['driver_options'] : []
);
}
}
$collection = $resource['client_instance']->selectCollection(
isset($resource['db']) ? $resource['db'] : 'zend',
isset($resource['collection']) ? $resource['collection'] : 'cache'
);
$collection->createIndex(['key' => 1]);
$this->resources[$id]['collection_instance'] = $collection;
} catch (MongoDriverException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
}
return $this->resources[$id]['collection_instance'];
}
/**
* @param string $id
* @param string $server
* @return void
*/
public function setServer($id, $server)
{
$this->resources[$id]['server'] = (string) $server;
unset($this->resources[$id]['client_instance']);
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
/**
* @param string $id
* @return null|string
* @throws Exception\RuntimeException if no matching resource discovered
*/
public function getServer($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['server']) ? $this->resources[$id]['server'] : null;
}
/**
* @param string $id
* @param array $connectionOptions
* @return void
*/
public function setConnectionOptions($id, array $connectionOptions)
{
$this->resources[$id]['connection_options'] = $connectionOptions;
unset($this->resources[$id]['client_instance']);
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
/**
* @param string $id
* @return array
* @throws Exception\RuntimeException if no matching resource discovered
*/
public function getConnectionOptions($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['connection_options'])
? $this->resources[$id]['connection_options']
: [];
}
/**
* @param string $id
* @param array $driverOptions
* @return void
*/
public function setDriverOptions($id, array $driverOptions)
{
$this->resources[$id]['driver_options'] = $driverOptions;
unset($this->resources[$id]['client_instance']);
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
/**
* @param string $id
* @return array
* @throws Exception\RuntimeException if no matching resource discovered
*/
public function getDriverOptions($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['driver_options']) ? $this->resources[$id]['driver_options'] : [];
}
/**
* @param string $id
* @param string $database
* @return void
*/
public function setDatabase($id, $database)
{
$this->resources[$id]['db'] = (string) $database;
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
/**
* @param string $id
* @return string
* @throws Exception\RuntimeException if no matching resource discovered
*/
public function getDatabase($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['db']) ? $this->resources[$id]['db'] : '';
}
/**
* @param string $id
* @param string $collection
* @return void
*/
public function setCollection($id, $collection)
{
$this->resources[$id]['collection'] = (string) $collection;
unset($this->resources[$id]['collection_instance']);
}
/**
* @param string $id
* @return string
* @throws Exception\RuntimeException if no matching resource discovered
*/
public function getCollection($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['collection']) ? $this->resources[$id]['collection'] : '';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use GlobIterator;
use Zend\Cache\Storage\IteratorInterface;
class FilesystemIterator implements IteratorInterface
{
/**
* The Filesystem storage instance
*
* @var Filesystem
*/
protected $storage;
/**
* The iterator mode
*
* @var int
*/
protected $mode = IteratorInterface::CURRENT_AS_KEY;
/**
* The GlobIterator instance
*
* @var GlobIterator
*/
protected $globIterator;
/**
* The namespace sprefix
*
* @var string
*/
protected $prefix;
/**
* String length of namespace prefix
*
* @var int
*/
protected $prefixLength;
/**
* Constructor
*
* @param Filesystem $storage
* @param string $path
* @param string $prefix
*/
public function __construct(Filesystem $storage, $path, $prefix)
{
$this->storage = $storage;
$this->globIterator = new GlobIterator($path, GlobIterator::KEY_AS_FILENAME);
$this->prefix = $prefix;
$this->prefixLength = strlen($prefix);
}
/**
* Get storage instance
*
* @return Filesystem
*/
public function getStorage()
{
return $this->storage;
}
/**
* Get iterator mode
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode()
{
return $this->mode;
}
/**
* Set iterator mode
*
* @param int $mode
* @return FilesystemIterator Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (int) $mode;
return $this;
}
/* Iterator */
/**
* Get current key, value or metadata.
*
* @return mixed
*/
public function current()
{
if ($this->mode == IteratorInterface::CURRENT_AS_SELF) {
return $this;
}
$key = $this->key();
if ($this->mode == IteratorInterface::CURRENT_AS_VALUE) {
return $this->storage->getItem($key);
} elseif ($this->mode == IteratorInterface::CURRENT_AS_METADATA) {
return $this->storage->getMetadata($key);
}
return $key;
}
/**
* Get current key
*
* @return string
*/
public function key()
{
$filename = $this->globIterator->key();
// return without namespace prefix and file suffix
return substr($filename, $this->prefixLength, -4);
}
/**
* Move forward to next element
*
* @return void
*/
public function next()
{
$this->globIterator->next();
}
/**
* Checks if current position is valid
*
* @return bool
*/
public function valid()
{
try {
return $this->globIterator->valid();
} catch (\LogicException $e) {
// @link https://bugs.php.net/bug.php?id=55701
// GlobIterator throws LogicException with message
// 'The parent constructor was not called: the object is in an invalid state'
return false;
}
}
/**
* Rewind the Iterator to the first element.
*
* @return bool false if the operation failed.
*/
public function rewind()
{
try {
return $this->globIterator->rewind();
} catch (\LogicException $e) {
// @link https://bugs.php.net/bug.php?id=55701
// GlobIterator throws LogicException with message
// 'The parent constructor was not called: the object is in an invalid state'
return false;
}
}
}

View File

@@ -0,0 +1,511 @@
<?php
/**
* @see https://github.com/zendframework/zend-cache for the canonical source repository
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-cache/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Traversable;
use Zend\Cache\Exception;
/**
* These are options specific to the Filesystem adapter
*/
class FilesystemOptions extends AdapterOptions
{
/**
* Directory to store cache files
*
* @var null|string The cache directory
* or NULL for the systems temporary directory
*/
protected $cacheDir = null;
/**
* Call clearstatcache enabled?
*
* @var bool
*/
protected $clearStatCache = true;
/**
* How much sub-directaries should be created?
*
* @var int
*/
protected $dirLevel = 1;
/**
* Permission creating new directories
*
* @var false|int
*/
protected $dirPermission = 0700;
/**
* Lock files on writing
*
* @var bool
*/
protected $fileLocking = true;
/**
* Permission creating new files
*
* @var false|int
*/
protected $filePermission = 0600;
/**
* Overwrite default key pattern
*
* Defined in AdapterOptions
*
* @var string
*/
protected $keyPattern = '/^[a-z0-9_\+\-]*$/Di';
/**
* Namespace separator
*
* @var string
*/
protected $namespaceSeparator = '-';
/**
* Don't get 'fileatime' as 'atime' on metadata
*
* @var bool
*/
protected $noAtime = true;
/**
* Don't get 'filectime' as 'ctime' on metadata
*
* @var bool
*/
protected $noCtime = true;
/**
* Umask to create files and directories
*
* @var false|int
*/
protected $umask = false;
/**
* Suffix for cache files
*
* @var string
*/
protected $suffix = 'dat';
/**
* Suffix for tag files
*
* @var string
*/
protected $tagSuffix = 'tag';
/**
* Constructor
*
* @param array|Traversable|null $options
* @return FilesystemOptions
* @throws Exception\InvalidArgumentException
*/
public function __construct($options = null)
{
// disable file/directory permissions by default on windows systems
if (stripos(PHP_OS, 'WIN') === 0) {
$this->filePermission = false;
$this->dirPermission = false;
}
parent::__construct($options);
}
/**
* Set cache dir
*
* @param string $cacheDir
* @return FilesystemOptions Provides a fluent interface
* @throws Exception\InvalidArgumentException
*/
public function setCacheDir($cacheDir)
{
if ($cacheDir !== null) {
if (! is_dir($cacheDir)) {
throw new Exception\InvalidArgumentException(
"Cache directory '{$cacheDir}' not found or not a directory"
);
} elseif (! is_writable($cacheDir)) {
throw new Exception\InvalidArgumentException(
"Cache directory '{$cacheDir}' not writable"
);
} elseif (! is_readable($cacheDir)) {
throw new Exception\InvalidArgumentException(
"Cache directory '{$cacheDir}' not readable"
);
}
$cacheDir = rtrim(realpath($cacheDir), DIRECTORY_SEPARATOR);
} else {
$cacheDir = sys_get_temp_dir();
}
$this->triggerOptionEvent('cache_dir', $cacheDir);
$this->cacheDir = $cacheDir;
return $this;
}
/**
* Get cache dir
*
* @return null|string
*/
public function getCacheDir()
{
if ($this->cacheDir === null) {
$this->setCacheDir(null);
}
return $this->cacheDir;
}
/**
* Set clear stat cache
*
* @param bool $clearStatCache
* @return FilesystemOptions Provides a fluent interface
*/
public function setClearStatCache($clearStatCache)
{
$clearStatCache = (bool) $clearStatCache;
$this->triggerOptionEvent('clear_stat_cache', $clearStatCache);
$this->clearStatCache = $clearStatCache;
return $this;
}
/**
* Get clear stat cache
*
* @return bool
*/
public function getClearStatCache()
{
return $this->clearStatCache;
}
/**
* Set dir level
*
* @param int $dirLevel
* @return FilesystemOptions Provides a fluent interface
* @throws Exception\InvalidArgumentException
*/
public function setDirLevel($dirLevel)
{
$dirLevel = (int) $dirLevel;
if ($dirLevel < 0 || $dirLevel > 16) {
throw new Exception\InvalidArgumentException(
"Directory level '{$dirLevel}' must be between 0 and 16"
);
}
$this->triggerOptionEvent('dir_level', $dirLevel);
$this->dirLevel = $dirLevel;
return $this;
}
/**
* Get dir level
*
* @return int
*/
public function getDirLevel()
{
return $this->dirLevel;
}
/**
* Set permission to create directories on unix systems
*
* @param false|string|int $dirPermission FALSE to disable explicit permission or an octal number
* @return FilesystemOptions Provides a fluent interface
* @see setUmask
* @see setFilePermission
* @link http://php.net/manual/function.chmod.php
*/
public function setDirPermission($dirPermission)
{
if ($dirPermission !== false) {
if (is_string($dirPermission)) {
$dirPermission = octdec($dirPermission);
} else {
$dirPermission = (int) $dirPermission;
}
// validate
if (($dirPermission & 0700) != 0700) {
throw new Exception\InvalidArgumentException(
'Invalid directory permission: need permission to execute, read and write by owner'
);
}
}
if ($this->dirPermission !== $dirPermission) {
$this->triggerOptionEvent('dir_permission', $dirPermission);
$this->dirPermission = $dirPermission;
}
return $this;
}
/**
* Get permission to create directories on unix systems
*
* @return false|int
*/
public function getDirPermission()
{
return $this->dirPermission;
}
/**
* Set file locking
*
* @param bool $fileLocking
* @return FilesystemOptions Provides a fluent interface
*/
public function setFileLocking($fileLocking)
{
$fileLocking = (bool) $fileLocking;
$this->triggerOptionEvent('file_locking', $fileLocking);
$this->fileLocking = $fileLocking;
return $this;
}
/**
* Get file locking
*
* @return bool
*/
public function getFileLocking()
{
return $this->fileLocking;
}
/**
* Set permission to create files on unix systems
*
* @param false|string|int $filePermission FALSE to disable explicit permission or an octal number
* @return FilesystemOptions Provides a fluent interface
* @see setUmask
* @see setDirPermission
* @link http://php.net/manual/function.chmod.php
*/
public function setFilePermission($filePermission)
{
if ($filePermission !== false) {
if (is_string($filePermission)) {
$filePermission = octdec($filePermission);
} else {
$filePermission = (int) $filePermission;
}
// validate
if (($filePermission & 0600) != 0600) {
throw new Exception\InvalidArgumentException(
'Invalid file permission: need permission to read and write by owner'
);
} elseif ($filePermission & 0111) {
throw new Exception\InvalidArgumentException(
"Invalid file permission: Cache files shouldn't be executable"
);
}
}
if ($this->filePermission !== $filePermission) {
$this->triggerOptionEvent('file_permission', $filePermission);
$this->filePermission = $filePermission;
}
return $this;
}
/**
* Get permission to create files on unix systems
*
* @return false|int
*/
public function getFilePermission()
{
return $this->filePermission;
}
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return FilesystemOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Set no atime
*
* @param bool $noAtime
* @return FilesystemOptions Provides a fluent interface
*/
public function setNoAtime($noAtime)
{
$noAtime = (bool) $noAtime;
$this->triggerOptionEvent('no_atime', $noAtime);
$this->noAtime = $noAtime;
return $this;
}
/**
* Get no atime
*
* @return bool
*/
public function getNoAtime()
{
return $this->noAtime;
}
/**
* Set no ctime
*
* @param bool $noCtime
* @return FilesystemOptions
*/
public function setNoCtime($noCtime)
{
$noCtime = (bool) $noCtime;
$this->triggerOptionEvent('no_ctime', $noCtime);
$this->noCtime = $noCtime;
return $this;
}
/**
* Get no ctime
*
* @return bool
*/
public function getNoCtime()
{
return $this->noCtime;
}
/**
* Set the umask to create files and directories on unix systems
*
* Note: On multithreaded webservers it's better to explicit set file and dir permission.
*
* @param false|string|int $umask FALSE to disable umask or an octal number
* @return FilesystemOptions
* @see setFilePermission
* @see setDirPermission
* @link http://php.net/manual/function.umask.php
* @link http://en.wikipedia.org/wiki/Umask
*/
public function setUmask($umask)
{
if ($umask !== false) {
if (is_string($umask)) {
$umask = octdec($umask);
} else {
$umask = (int) $umask;
}
// validate
if ($umask & 0700) {
throw new Exception\InvalidArgumentException(
'Invalid umask: need permission to execute, read and write by owner'
);
}
// normalize
$umask = $umask & ~0002;
}
if ($this->umask !== $umask) {
$this->triggerOptionEvent('umask', $umask);
$this->umask = $umask;
}
return $this;
}
/**
* Get the umask to create files and directories on unix systems
*
* @return false|int
*/
public function getUmask()
{
return $this->umask;
}
/**
* Get the suffix for cache files
*
* @return string
*/
public function getSuffix()
{
return $this->suffix;
}
/**
* Set the suffix for cache files
*
* @param string $suffix
*/
public function setSuffix($suffix)
{
$this->suffix = $suffix;
return $this;
}
/**
* Get the suffix for tag files
*
* @return the $tagSuffix
*/
public function getTagSuffix()
{
return $this->tagSuffix;
}
/**
* Set the suffix for cache files
*
* @param string $tagSuffix
*/
public function setTagSuffix($tagSuffix)
{
$this->tagSuffix = $tagSuffix;
return $this;
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Countable;
use Zend\Cache\Storage\IteratorInterface;
use Zend\Cache\Storage\StorageInterface;
class KeyListIterator implements IteratorInterface, Countable
{
/**
* The storage instance
*
* @var StorageInterface
*/
protected $storage;
/**
* The iterator mode
*
* @var int
*/
protected $mode = IteratorInterface::CURRENT_AS_KEY;
/**
* Keys to iterate over
*
* @var string[]
*/
protected $keys;
/**
* Number of keys
*
* @var int
*/
protected $count;
/**
* Current iterator position
*
* @var int
*/
protected $position = 0;
/**
* Constructor
*
* @param StorageInterface $storage
* @param array $keys
*/
public function __construct(StorageInterface $storage, array $keys)
{
$this->storage = $storage;
$this->keys = $keys;
$this->count = count($keys);
}
/**
* Get storage instance
*
* @return StorageInterface
*/
public function getStorage()
{
return $this->storage;
}
/**
* Get iterator mode
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode()
{
return $this->mode;
}
/**
* Set iterator mode
*
* @param int $mode
* @return KeyListIterator Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (int) $mode;
return $this;
}
/**
* Get current key, value or metadata.
*
* @return mixed
*/
public function current()
{
if ($this->mode == IteratorInterface::CURRENT_AS_SELF) {
return $this;
}
$key = $this->key();
if ($this->mode == IteratorInterface::CURRENT_AS_METADATA) {
return $this->storage->getMetadata($key);
} elseif ($this->mode == IteratorInterface::CURRENT_AS_VALUE) {
return $this->storage->getItem($key);
}
return $key;
}
/**
* Get current key
*
* @return string
*/
public function key()
{
return $this->keys[$this->position];
}
/**
* Checks if current position is valid
*
* @return bool
*/
public function valid()
{
return $this->position < $this->count;
}
/**
* Move forward to next element
*
* @return void
*/
public function next()
{
$this->position++;
}
/**
* Rewind the Iterator to the first element.
*
* @return void
*/
public function rewind()
{
$this->position = 0;
}
/**
* Count number of items
*
* @return int
*/
public function count()
{
return $this->count;
}
}

View File

@@ -0,0 +1,573 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Memcache as MemcacheResource;
use stdClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class Memcache extends AbstractAdapter implements
AvailableSpaceCapableInterface,
FlushableInterface,
TotalSpaceCapableInterface
{
/**
* Has this instance been initialized
*
* @var bool
*/
protected $initialized = false;
/**
* The memcache resource manager
*
* @var null|MemcacheResourceManager
*/
protected $resourceManager;
/**
* The memcache resource id
*
* @var null|string
*/
protected $resourceId;
/**
* The namespace prefix
*
* @var string
*/
protected $namespacePrefix = '';
/**
* Constructor
*
* @param null|array|Traversable|MemcacheOptions $options
* @throws Exception\ExceptionInterface
*/
public function __construct($options = null)
{
if (version_compare('2.0.0', phpversion('memcache')) > 0) {
throw new Exception\ExtensionNotLoadedException("Missing ext/memcache version >= 2.0.0");
}
parent::__construct($options);
// reset initialized flag on update option(s)
$initialized = & $this->initialized;
$this->getEventManager()->attach('option', function () use (& $initialized) {
$initialized = false;
});
}
/**
* Initialize the internal memcache resource
*
* @return MemcacheResource
*/
protected function getMemcacheResource()
{
if ($this->initialized) {
return $this->resourceManager->getResource($this->resourceId);
}
$options = $this->getOptions();
// get resource manager and resource id
$this->resourceManager = $options->getResourceManager();
$this->resourceId = $options->getResourceId();
// init namespace prefix
$this->namespacePrefix = '';
$namespace = $options->getNamespace();
if ($namespace !== '') {
$this->namespacePrefix = $namespace . $options->getNamespaceSeparator();
}
// update initialized flag
$this->initialized = true;
return $this->resourceManager->getResource($this->resourceId);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|MemcacheOptions $options
* @return Memcache
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof MemcacheOptions) {
$options = new MemcacheOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return MemcacheOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new MemcacheOptions());
}
return $this->options;
}
/**
* @param mixed $value
* @return int
*/
protected function getWriteFlag(& $value)
{
if (! $this->getOptions()->getCompression()) {
return 0;
}
// Don't compress numeric or boolean types
return (is_bool($value) || is_int($value) || is_float($value)) ? 0 : MEMCACHE_COMPRESSED;
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
$memc = $this->getMemcacheResource();
if (! $memc->flush()) {
return new Exception\RuntimeException("Memcache flush failed");
}
return true;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
$memc = $this->getMemcacheResource();
$stats = $memc->getExtendedStats();
if ($stats === false) {
return new Exception\RuntimeException("Memcache getStats failed");
}
$mem = array_pop($stats);
return $mem['limit_maxbytes'];
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
$memc = $this->getMemcacheResource();
$stats = $memc->getExtendedStats();
if ($stats === false) {
throw new Exception\RuntimeException('Memcache getStats failed');
}
$mem = array_pop($stats);
return $mem['limit_maxbytes'] - $mem['bytes'];
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$memc = $this->getMemcacheResource();
$internalKey = $this->namespacePrefix . $normalizedKey;
$result = $memc->get($internalKey);
$success = ($result !== false);
if ($result === false) {
return;
}
$casToken = $result;
return $result;
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$memc = $this->getMemcacheResource();
foreach ($normalizedKeys as & $normalizedKey) {
$normalizedKey = $this->namespacePrefix . $normalizedKey;
}
$result = $memc->get($normalizedKeys);
if ($result === false) {
return [];
}
// remove namespace prefix from result
if ($this->namespacePrefix !== '') {
$tmp = [];
$nsPrefixLength = strlen($this->namespacePrefix);
foreach ($result as $internalKey => & $value) {
$tmp[substr($internalKey, $nsPrefixLength)] = & $value;
}
$result = $tmp;
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$memc = $this->getMemcacheResource();
$value = $memc->get($this->namespacePrefix . $normalizedKey);
return ($value !== false);
}
/**
* Internal method to test multiple items.
*
* @param array $normalizedKeys
* @return array Array of found keys
* @throws Exception\ExceptionInterface
*/
protected function internalHasItems(array & $normalizedKeys)
{
$memc = $this->getMemcacheResource();
foreach ($normalizedKeys as & $normalizedKey) {
$normalizedKey = $this->namespacePrefix . $normalizedKey;
}
$result = $memc->get($normalizedKeys);
if ($result === false) {
return [];
}
// Convert to a single list
$result = array_keys($result);
// remove namespace prefix
if ($result && $this->namespacePrefix !== '') {
$nsPrefixLength = strlen($this->namespacePrefix);
foreach ($result as & $internalKey) {
$internalKey = substr($internalKey, $nsPrefixLength);
}
}
return $result;
}
/**
* Get metadata of multiple items
*
* @param array $normalizedKeys
* @return array Associative array of keys and metadata
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadatas(array & $normalizedKeys)
{
$memc = $this->getMemcacheResource();
foreach ($normalizedKeys as & $normalizedKey) {
$normalizedKey = $this->namespacePrefix . $normalizedKey;
}
$result = $memc->get($normalizedKeys);
if ($result === false) {
return [];
}
// remove namespace prefix and use an empty array as metadata
if ($this->namespacePrefix === '') {
foreach ($result as & $value) {
$value = [];
}
return $result;
}
$final = [];
$nsPrefixLength = strlen($this->namespacePrefix);
foreach (array_keys($result) as $internalKey) {
$final[substr($internalKey, $nsPrefixLength)] = [];
}
return $final;
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcacheResource();
$expiration = $this->expirationTime();
$flag = $this->getWriteFlag($value);
if (! $memc->set($this->namespacePrefix . $normalizedKey, $value, $flag, $expiration)) {
throw new Exception\RuntimeException('Memcache set value failed');
}
return true;
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcacheResource();
$expiration = $this->expirationTime();
$flag = $this->getWriteFlag($value);
return $memc->add($this->namespacePrefix . $normalizedKey, $value, $flag, $expiration);
}
/**
* Internal method to replace an existing item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcacheResource();
$expiration = $this->expirationTime();
$flag = $this->getWriteFlag($value);
return $memc->replace($this->namespacePrefix . $normalizedKey, $value, $flag, $expiration);
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$memc = $this->getMemcacheResource();
// Delete's second parameter (timeout) is deprecated and not supported.
// Values other than 0 may cause delete to fail.
// http://www.php.net/manual/memcache.delete.php
return $memc->delete($this->namespacePrefix . $normalizedKey, 0);
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcacheResource();
$internalKey = $this->namespacePrefix . $normalizedKey;
$value = (int) $value;
$newValue = $memc->increment($internalKey, $value);
if ($newValue !== false) {
return $newValue;
}
// Set initial value. Don't use compression!
// http://www.php.net/manual/memcache.increment.php
$newValue = $value;
if (! $memc->add($internalKey, $newValue, 0, $this->expirationTime())) {
throw new Exception\RuntimeException('Memcache unable to add increment value');
}
return $newValue;
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcacheResource();
$internalKey = $this->namespacePrefix . $normalizedKey;
$value = (int) $value;
$newValue = $memc->decrement($internalKey, $value);
if ($newValue !== false) {
return $newValue;
}
// Set initial value. Don't use compression!
// http://www.php.net/manual/memcache.decrement.php
$newValue = -$value;
if (! $memc->add($internalKey, $newValue, 0, $this->expirationTime())) {
throw new Exception\RuntimeException('Memcache unable to add decrement value');
}
return $newValue;
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities !== null) {
return $this->capabilities;
}
if (version_compare('3.0.3', phpversion('memcache')) <= 0) {
// In ext/memcache v3.0.3:
// Scalar data types (int, bool, double) are preserved by get/set.
// http://pecl.php.net/package/memcache/3.0.3
//
// This effectively removes support for `boolean` types since
// "not found" return values are === false.
$supportedDatatypes = [
'NULL' => true,
'boolean' => false,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => 'object',
'resource' => false,
];
} else {
// In stable 2.x ext/memcache versions, scalar data types are
// converted to strings and must be manually cast back to original
// types by the user.
//
// ie. It is impossible to know if the saved value: (string)"1"
// was previously: (bool)true, (int)1, or (string)"1".
// Similarly, the saved value: (string)""
// might have previously been: (bool)false or (string)""
$supportedDatatypes = [
'NULL' => true,
'boolean' => 'boolean',
'integer' => 'integer',
'double' => 'double',
'string' => true,
'array' => true,
'object' => 'object',
'resource' => false,
];
}
$this->capabilityMarker = new stdClass();
$this->capabilities = new Capabilities(
$this,
$this->capabilityMarker,
[
'supportedDatatypes' => $supportedDatatypes,
'supportedMetadata' => [],
'minTtl' => 1,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => false,
'maxKeyLength' => 255,
'namespaceIsPrefix' => true,
]
);
return $this->capabilities;
}
/* internal */
/**
* Get expiration time by ttl
*
* Some storage commands involve sending an expiration value (relative to
* an item or to an operation requested by the client) to the server. In
* all such cases, the actual value sent may either be Unix time (number of
* seconds since January 1, 1970, as an integer), or a number of seconds
* starting from current time. In the latter case, this number of seconds
* may not exceed 60*60*24*30 (number of seconds in 30 days); if the
* expiration value is larger than that, the server will consider it to be
* real Unix time value rather than an offset from current time.
*
* @return int
*/
protected function expirationTime()
{
$ttl = $this->getOptions()->getTtl();
if ($ttl > 2592000) {
return time() + $ttl;
}
return $ttl;
}
}

View File

@@ -0,0 +1,294 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Exception;
/**
* These are options specific to the Memcache adapter
*/
class MemcacheOptions extends AdapterOptions
{
// @codingStandardsIgnoreStart
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id'];
// @codingStandardsIgnoreEnd
/**
* The namespace separator
* @var string
*/
protected $namespaceSeparator = ':';
/**
* The memcache resource manager
*
* @var null|MemcacheResourceManager
*/
protected $resourceManager;
/**
* The resource id of the resource manager
*
* @var string
*/
protected $resourceId = 'default';
/**
* Enable compression when data is written
*
* @var bool
*/
protected $compression = false;
/**
* Set namespace.
*
* It can't be longer than 128 characters.
*
* @see AdapterOptions::setNamespace()
* @see MemcacheOptions::setPrefixKey()
*/
public function setNamespace($namespace)
{
$namespace = (string) $namespace;
if (128 < strlen($namespace)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a prefix key of no longer than 128 characters',
__METHOD__
));
}
return parent::setNamespace($namespace);
}
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return MemcacheOptions
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
if ($this->namespaceSeparator !== $namespaceSeparator) {
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
}
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Set the memcache resource manager to use
*
* @param null|MemcacheResourceManager $resourceManager
* @return MemcacheOptions
*/
public function setResourceManager(MemcacheResourceManager $resourceManager = null)
{
if ($this->resourceManager !== $resourceManager) {
$this->triggerOptionEvent('resource_manager', $resourceManager);
$this->resourceManager = $resourceManager;
}
return $this;
}
/**
* Get the memcache resource manager
*
* @return MemcacheResourceManager
*/
public function getResourceManager()
{
if (! $this->resourceManager) {
$this->resourceManager = new MemcacheResourceManager();
}
return $this->resourceManager;
}
/**
* Get the memcache resource id
*
* @return string
*/
public function getResourceId()
{
return $this->resourceId;
}
/**
* Set the memcache resource id
*
* @param string $resourceId
* @return MemcacheOptions
*/
public function setResourceId($resourceId)
{
$resourceId = (string) $resourceId;
if ($this->resourceId !== $resourceId) {
$this->triggerOptionEvent('resource_id', $resourceId);
$this->resourceId = $resourceId;
}
return $this;
}
/**
* Is compressed writes turned on?
*
* @return boolean
*/
public function getCompression()
{
return $this->compression;
}
/**
* Set whether compressed writes are turned on or not
*
* @param boolean $compression
* @return $this
*/
public function setCompression($compression)
{
$compression = (bool) $compression;
if ($this->compression !== $compression) {
$this->triggerOptionEvent('compression', $compression);
$this->compression = $compression;
}
return $this;
}
/**
* Sets a list of memcache servers to add on initialize
*
* @param string|array $servers list of servers
* @return MemcacheOptions
* @throws Exception\InvalidArgumentException
*/
public function setServers($servers)
{
$this->getResourceManager()->addServers($this->getResourceId(), $servers);
return $this;
}
/**
* Get Servers
*
* @return array
*/
public function getServers()
{
return $this->getResourceManager()->getServers($this->getResourceId());
}
/**
* Set compress threshold
*
* @param int|string|array|\ArrayAccess|null $threshold
* @return MemcacheOptions
*/
public function setAutoCompressThreshold($threshold)
{
$this->getResourceManager()->setAutoCompressThreshold($this->getResourceId(), $threshold);
return $this;
}
/**
* Get compress threshold
*
* @return int|null
*/
public function getAutoCompressThreshold()
{
return $this->getResourceManager()->getAutoCompressThreshold($this->getResourceId());
}
/**
* Set compress min savings option
*
* @param float|string|null $minSavings
* @return MemcacheOptions
*/
public function setAutoCompressMinSavings($minSavings)
{
$this->getResourceManager()->setAutoCompressMinSavings($this->getResourceId(), $minSavings);
return $this;
}
/**
* Get compress min savings
*
* @throws Exception\RuntimeException
*/
public function getAutoCompressMinSavings()
{
return $this->getResourceManager()->getAutoCompressMinSavings($this->getResourceId());
}
/**
* Set default server values
*
* @param array $serverDefaults
* @return MemcacheOptions
*/
public function setServerDefaults(array $serverDefaults)
{
$this->getResourceManager()->setServerDefaults($this->getResourceId(), $serverDefaults);
return $this;
}
/**
* Get default server values
*
* @return array
*/
public function getServerDefaults()
{
return $this->getResourceManager()->getServerDefaults($this->getResourceId());
}
/**
* Set callback for server connection failures
*
* @param callable $callback
* @return $this
*/
public function setFailureCallback($callback)
{
$this->getResourceManager()->setFailureCallback($this->getResourceId(), $callback);
return $this;
}
/**
* Get callback for server connection failures
*
* @return callable
*/
public function getFailureCallback()
{
return $this->getResourceManager()->getFailureCallback($this->getResourceId());
}
}

View File

@@ -0,0 +1,649 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use ArrayAccess;
use Memcache as MemcacheResource;
use Traversable;
use Zend\Cache\Exception;
use Zend\Stdlib\ArrayUtils;
/**
* This is a resource manager for memcache
*/
class MemcacheResourceManager
{
/**
* Registered resources
*
* @var array
*/
protected $resources = [];
/**
* Default server values per resource
*
* @var array
*/
protected $serverDefaults = [];
/**
* Failure callback per resource
*
* @var callable[]
*/
protected $failureCallbacks = [];
/**
* Check if a resource exists
*
* @param string $id
* @return bool
*/
public function hasResource($id)
{
return isset($this->resources[$id]);
}
/**
* Gets a memcache resource
*
* @param string $id
* @return MemcacheResource
* @throws Exception\RuntimeException
*/
public function getResource($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = $this->resources[$id];
if ($resource instanceof MemcacheResource) {
return $resource;
}
$memc = new MemcacheResource();
$this->setResourceAutoCompressThreshold(
$memc,
$resource['auto_compress_threshold'],
$resource['auto_compress_min_savings']
);
foreach ($resource['servers'] as $server) {
$this->addServerToResource(
$memc,
$server,
$this->serverDefaults[$id],
$this->failureCallbacks[$id]
);
}
// buffer and return
$this->resources[$id] = $memc;
return $memc;
}
/**
* Set a resource
*
* @param string $id
* @param array|Traversable|MemcacheResource $resource
* @param callable $failureCallback
* @param array|Traversable $serverDefaults
* @return MemcacheResourceManager Provides a fluent interface
*/
public function setResource($id, $resource, $failureCallback = null, $serverDefaults = [])
{
$id = (string) $id;
if ($serverDefaults instanceof Traversable) {
$serverDefaults = ArrayUtils::iteratorToArray($serverDefaults);
} elseif (! is_array($serverDefaults)) {
throw new Exception\InvalidArgumentException(
'ServerDefaults must be an instance Traversable or an array'
);
}
if (! ($resource instanceof MemcacheResource)) {
if ($resource instanceof Traversable) {
$resource = ArrayUtils::iteratorToArray($resource);
} elseif (! is_array($resource)) {
throw new Exception\InvalidArgumentException(
'Resource must be an instance of Memcache or an array or Traversable'
);
}
if (isset($resource['server_defaults'])) {
$serverDefaults = array_merge($serverDefaults, $resource['server_defaults']);
unset($resource['server_defaults']);
}
$resourceOptions = [
'servers' => [],
'auto_compress_threshold' => null,
'auto_compress_min_savings' => null,
];
$resource = array_merge($resourceOptions, $resource);
// normalize and validate params
$this->normalizeAutoCompressThreshold(
$resource['auto_compress_threshold'],
$resource['auto_compress_min_savings']
);
$this->normalizeServers($resource['servers']);
}
$this->normalizeServerDefaults($serverDefaults);
$this->resources[$id] = $resource;
$this->failureCallbacks[$id] = $failureCallback;
$this->serverDefaults[$id] = $serverDefaults;
return $this;
}
/**
* Remove a resource
*
* @param string $id
* @return MemcacheResourceManager Provides a fluent interface
*/
public function removeResource($id)
{
unset($this->resources[$id]);
return $this;
}
/**
* Normalize compress threshold options
*
* @param int|string|array|ArrayAccess $threshold
* @param float|string $minSavings
*/
protected function normalizeAutoCompressThreshold(& $threshold, & $minSavings)
{
if (is_array($threshold) || ($threshold instanceof ArrayAccess)) {
$tmpThreshold = (isset($threshold['threshold'])) ? $threshold['threshold'] : null;
$minSavings = (isset($threshold['min_savings'])) ? $threshold['min_savings'] : $minSavings;
$threshold = $tmpThreshold;
}
if (isset($threshold)) {
$threshold = (int) $threshold;
}
if (isset($minSavings)) {
$minSavings = (float) $minSavings;
}
}
/**
* Set compress threshold on a Memcache resource
*
* @param MemcacheResource $resource
* @param int $threshold
* @param float $minSavings
*/
protected function setResourceAutoCompressThreshold(MemcacheResource $resource, $threshold, $minSavings)
{
if (! isset($threshold)) {
return;
}
if (isset($minSavings)) {
$resource->setCompressThreshold($threshold, $minSavings);
} else {
$resource->setCompressThreshold($threshold);
}
}
/**
* Get compress threshold
*
* @param string $id
* @return int|null
* @throws \Zend\Cache\Exception\RuntimeException
*/
public function getAutoCompressThreshold($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource instanceof MemcacheResource) {
// Cannot get options from Memcache resource once created
throw new Exception\RuntimeException("Cannot get compress threshold once resource is created");
}
return $resource['auto_compress_threshold'];
}
/**
* Set compress threshold
*
* @param string $id
* @param int|string|array|ArrayAccess|null $threshold
* @param float|string|bool $minSavings
* @return MemcacheResourceManager Provides a fluent interface
*/
public function setAutoCompressThreshold($id, $threshold, $minSavings = false)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'auto_compress_threshold' => $threshold,
]);
}
$this->normalizeAutoCompressThreshold($threshold, $minSavings);
$resource = & $this->resources[$id];
if ($resource instanceof MemcacheResource) {
$this->setResourceAutoCompressThreshold($resource, $threshold, $minSavings);
} else {
$resource['auto_compress_threshold'] = $threshold;
if ($minSavings !== false) {
$resource['auto_compress_min_savings'] = $minSavings;
}
}
return $this;
}
/**
* Get compress min savings
*
* @param string $id
* @return float|null
* @throws Exception\RuntimeException
*/
public function getAutoCompressMinSavings($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource instanceof MemcacheResource) {
// Cannot get options from Memcache resource once created
throw new Exception\RuntimeException("Cannot get compress min savings once resource is created");
}
return $resource['auto_compress_min_savings'];
}
/**
* Set compress min savings
*
* @param string $id
* @param float|string|null $minSavings
* @return MemcacheResourceManager Provides a fluent interface
* @throws \Zend\Cache\Exception\RuntimeException
*/
public function setAutoCompressMinSavings($id, $minSavings)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'auto_compress_min_savings' => $minSavings,
]);
}
$minSavings = (float) $minSavings;
$resource = & $this->resources[$id];
if ($resource instanceof MemcacheResource) {
throw new Exception\RuntimeException(
"Cannot set compress min savings without a threshold value once a resource is created"
);
} else {
$resource['auto_compress_min_savings'] = $minSavings;
}
return $this;
}
/**
* Set default server values
* array(
* 'persistent' => <persistent>, 'weight' => <weight>,
* 'timeout' => <timeout>, 'retry_interval' => <retryInterval>,
* )
* @param string $id
* @param array $serverDefaults
* @return MemcacheResourceManager Provides a fluent interface
*/
public function setServerDefaults($id, array $serverDefaults)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'server_defaults' => $serverDefaults
]);
}
$this->normalizeServerDefaults($serverDefaults);
$this->serverDefaults[$id] = $serverDefaults;
return $this;
}
/**
* Get default server values
*
* @param string $id
* @return array
* @throws Exception\RuntimeException
*/
public function getServerDefaults($id)
{
if (! isset($this->serverDefaults[$id])) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return $this->serverDefaults[$id];
}
/**
* @param array $serverDefaults
* @throws Exception\InvalidArgumentException
*/
protected function normalizeServerDefaults(& $serverDefaults)
{
if (! is_array($serverDefaults) && ! ($serverDefaults instanceof Traversable)) {
throw new Exception\InvalidArgumentException(
"Server defaults must be an array or an instance of Traversable"
);
}
// Defaults
$result = [
'persistent' => true,
'weight' => 1,
'timeout' => 1, // seconds
'retry_interval' => 15, // seconds
];
foreach ($serverDefaults as $key => $value) {
switch ($key) {
case 'persistent':
$value = (bool) $value;
break;
case 'weight':
case 'timeout':
case 'retry_interval':
$value = (int) $value;
break;
}
$result[$key] = $value;
}
$serverDefaults = $result;
}
/**
* Set callback for server connection failures
*
* @param string $id
* @param callable|null $failureCallback
* @return MemcacheResourceManager Provides a fluent interface
*/
public function setFailureCallback($id, $failureCallback)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [], $failureCallback);
}
$this->failureCallbacks[$id] = $failureCallback;
return $this;
}
/**
* Get callback for server connection failures
*
* @param string $id
* @return callable
* @throws Exception\RuntimeException
*/
public function getFailureCallback($id)
{
if (! isset($this->failureCallbacks[$id])) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return $this->failureCallbacks[$id];
}
/**
* Get servers
*
* @param string $id
* @throws Exception\RuntimeException
* @return array array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
*/
public function getServers($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource instanceof MemcacheResource) {
throw new Exception\RuntimeException("Cannot get server list once resource is created");
}
return $resource['servers'];
}
/**
* Add servers
*
* @param string $id
* @param string|array $servers
* @return MemcacheResourceManager Provides a fluent interface
*/
public function addServers($id, $servers)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'servers' => $servers
]);
}
$this->normalizeServers($servers);
$resource = & $this->resources[$id];
if ($resource instanceof MemcacheResource) {
foreach ($servers as $server) {
$this->addServerToResource(
$resource,
$server,
$this->serverDefaults[$id],
$this->failureCallbacks[$id]
);
}
} else {
// don't add servers twice
$resource['servers'] = array_merge(
$resource['servers'],
array_udiff($servers, $resource['servers'], [$this, 'compareServers'])
);
}
return $this;
}
/**
* Add one server
*
* @param string $id
* @param string|array $server
* @return MemcacheResourceManager
*/
public function addServer($id, $server)
{
return $this->addServers($id, [$server]);
}
/**
* @param MemcacheResource $resource
* @param array $server
* @param array $serverDefaults
* @param callable|null $failureCallback
*/
protected function addServerToResource(
MemcacheResource $resource,
array $server,
array $serverDefaults,
$failureCallback
) {
// Apply server defaults
$server = array_merge($serverDefaults, $server);
// Reorder parameters
$params = [
$server['host'],
$server['port'],
$server['persistent'],
$server['weight'],
$server['timeout'],
$server['retry_interval'],
$server['status'],
];
if (isset($failureCallback)) {
$params[] = $failureCallback;
}
call_user_func_array([$resource, 'addServer'], $params);
}
/**
* Normalize a list of servers into the following format:
* array(array('host' => <host>, 'port' => <port>, 'weight' => <weight>)[, ...])
*
* @param string|array $servers
*/
protected function normalizeServers(& $servers)
{
if (is_string($servers)) {
// Convert string into a list of servers
$servers = explode(',', $servers);
}
$result = [];
foreach ($servers as $server) {
$this->normalizeServer($server);
$result[$server['host'] . ':' . $server['port']] = $server;
}
$servers = array_values($result);
}
/**
* Normalize one server into the following format:
* array(
* 'host' => <host>, 'port' => <port>, 'weight' => <weight>,
* 'status' => <status>, 'persistent' => <persistent>,
* 'timeout' => <timeout>, 'retry_interval' => <retryInterval>,
* )
*
* @param string|array $server
* @throws Exception\InvalidArgumentException
*/
protected function normalizeServer(& $server)
{
// WARNING: The order of this array is important.
// Used for converting an ordered array to a keyed array.
// Append new options, do not insert or you will break BC.
$sTmp = [
'host' => null,
'port' => 11211,
'weight' => null,
'status' => true,
'persistent' => null,
'timeout' => null,
'retry_interval' => null,
];
// convert a single server into an array
if ($server instanceof Traversable) {
$server = ArrayUtils::iteratorToArray($server);
}
if (is_array($server)) {
if (isset($server[0])) {
// Convert ordered array to keyed array
// array(<host>[, <port>[, <weight>[, <status>[, <persistent>[, <timeout>[, <retryInterval>]]]]]])
$server = array_combine(
array_slice(array_keys($sTmp), 0, count($server)),
$server
);
}
$sTmp = array_merge($sTmp, $server);
} elseif (is_string($server)) {
// parse server from URI host{:?port}{?weight}
$server = trim($server);
if (strpos($server, '://') === false) {
$server = 'tcp://' . $server;
}
$urlParts = parse_url($server);
if (! $urlParts) {
throw new Exception\InvalidArgumentException("Invalid server given");
}
$sTmp = array_merge($sTmp, array_intersect_key($urlParts, $sTmp));
if (isset($urlParts['query'])) {
$query = null;
parse_str($urlParts['query'], $query);
$sTmp = array_merge($sTmp, array_intersect_key($query, $sTmp));
}
}
if (! $sTmp['host']) {
throw new Exception\InvalidArgumentException('Missing required server host');
}
// Filter values
foreach ($sTmp as $key => $value) {
if (isset($value)) {
switch ($key) {
case 'host':
$value = (string) $value;
break;
case 'status':
case 'persistent':
$value = (bool) $value;
break;
case 'port':
case 'weight':
case 'timeout':
case 'retry_interval':
$value = (int) $value;
break;
}
}
$sTmp[$key] = $value;
}
$sTmp = array_filter(
$sTmp,
function ($val) {
return isset($val);
}
);
$server = $sTmp;
}
/**
* Compare 2 normalized server arrays
* (Compares only the host and the port)
*
* @param array $serverA
* @param array $serverB
* @return int
*/
protected function compareServers(array $serverA, array $serverB)
{
$keyA = $serverA['host'] . ':' . $serverA['port'];
$keyB = $serverB['host'] . ':' . $serverB['port'];
if ($keyA === $keyB) {
return 0;
}
return $keyA > $keyB ? 1 : -1;
}
}

View File

@@ -0,0 +1,654 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Memcached as MemcachedResource;
use stdClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class Memcached extends AbstractAdapter implements
AvailableSpaceCapableInterface,
FlushableInterface,
TotalSpaceCapableInterface
{
/**
* Has this instance be initialized
*
* @var bool
*/
protected $initialized = false;
/**
* The memcached resource manager
*
* @var null|MemcachedResourceManager
*/
protected $resourceManager;
/**
* The memcached resource id
*
* @var null|string
*/
protected $resourceId;
/**
* The namespace prefix
*
* @var string
*/
protected $namespacePrefix = '';
/**
* Constructor
*
* @param null|array|Traversable|MemcachedOptions $options
* @throws Exception\ExceptionInterface
*/
public function __construct($options = null)
{
if (phpversion('memcached') < 1) {
throw new Exception\ExtensionNotLoadedException('Need ext/memcached version >= 1.0.0');
}
parent::__construct($options);
// reset initialized flag on update option(s)
$initialized = & $this->initialized;
$this->getEventManager()->attach('option', function () use (& $initialized) {
$initialized = false;
});
}
/**
* Initialize the internal memcached resource
*
* @return MemcachedResource
*/
protected function getMemcachedResource()
{
if (! $this->initialized) {
$options = $this->getOptions();
// get resource manager and resource id
$this->resourceManager = $options->getResourceManager();
$this->resourceId = $options->getResourceId();
// init namespace prefix
$namespace = $options->getNamespace();
if ($namespace !== '') {
$this->namespacePrefix = $namespace . $options->getNamespaceSeparator();
} else {
$this->namespacePrefix = '';
}
// update initialized flag
$this->initialized = true;
}
return $this->resourceManager->getResource($this->resourceId);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|MemcachedOptions $options
* @return Memcached
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof MemcachedOptions) {
$options = new MemcachedOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return MemcachedOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new MemcachedOptions());
}
return $this->options;
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
$memc = $this->getMemcachedResource();
if (! $memc->flush()) {
throw $this->getExceptionByResultCode($memc->getResultCode());
}
return true;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
$memc = $this->getMemcachedResource();
$stats = $memc->getStats();
if ($stats === false) {
throw new Exception\RuntimeException($memc->getResultMessage());
}
$mem = array_pop($stats);
return $mem['limit_maxbytes'];
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
$memc = $this->getMemcachedResource();
$stats = $memc->getStats();
if ($stats === false) {
throw new Exception\RuntimeException($memc->getResultMessage());
}
$mem = array_pop($stats);
return $mem['limit_maxbytes'] - $mem['bytes'];
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$memc = $this->getMemcachedResource();
$internalKey = $this->namespacePrefix . $normalizedKey;
if (func_num_args() > 2) {
if (defined('Memcached::GET_EXTENDED')) {
$output = $memc->get($internalKey, null, \Memcached::GET_EXTENDED);
$casToken = $output ? $output['cas'] : $casToken;
$result = $output ? $output['value'] : false;
} else {
$result = $memc->get($internalKey, null, $casToken);
}
} else {
$result = $memc->get($internalKey);
}
$success = true;
if ($result === false) {
$rsCode = $memc->getResultCode();
if ($rsCode == MemcachedResource::RES_NOTFOUND) {
$result = null;
$success = false;
} elseif ($rsCode) {
$success = false;
throw $this->getExceptionByResultCode($rsCode);
}
}
return $result;
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$memc = $this->getMemcachedResource();
foreach ($normalizedKeys as & $normalizedKey) {
$normalizedKey = $this->namespacePrefix . $normalizedKey;
}
$result = $memc->getMulti($normalizedKeys);
if ($result === false) {
throw $this->getExceptionByResultCode($memc->getResultCode());
}
// if $result is empty the loop below can be avouded
// and HHVM returns NULL instead of an empty array in this case
if (empty($result)) {
return [];
}
// remove namespace prefix from result
if ($this->namespacePrefix !== '') {
$tmp = [];
$nsPrefixLength = strlen($this->namespacePrefix);
foreach ($result as $internalKey => $value) {
$tmp[substr($internalKey, $nsPrefixLength)] = $value;
}
$result = $tmp;
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$memc = $this->getMemcachedResource();
$value = $memc->get($this->namespacePrefix . $normalizedKey);
if ($value === false) {
$rsCode = $memc->getResultCode();
if ($rsCode == MemcachedResource::RES_SUCCESS) {
return true;
} elseif ($rsCode == MemcachedResource::RES_NOTFOUND) {
return false;
} else {
throw $this->getExceptionByResultCode($rsCode);
}
}
return true;
}
/**
* Internal method to test multiple items.
*
* @param array $normalizedKeys
* @return array Array of found keys
* @throws Exception\ExceptionInterface
*/
protected function internalHasItems(array & $normalizedKeys)
{
return array_keys($this->internalGetItems($normalizedKeys));
}
/**
* Get metadata of multiple items
*
* @param array $normalizedKeys
* @return array Associative array of keys and metadata
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadatas(array & $normalizedKeys)
{
return array_fill_keys(array_keys($this->internalGetItems($normalizedKeys)), []);
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcachedResource();
$expiration = $this->expirationTime();
if (! $memc->set($this->namespacePrefix . $normalizedKey, $value, $expiration)) {
throw $this->getExceptionByResultCode($memc->getResultCode());
}
return true;
}
/**
* Internal method to store multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$memc = $this->getMemcachedResource();
$expiration = $this->expirationTime();
$namespacedKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$namespacedKeyValuePairs[$this->namespacePrefix . $normalizedKey] = $value;
}
if (! $memc->setMulti($namespacedKeyValuePairs, $expiration)) {
throw $this->getExceptionByResultCode($memc->getResultCode());
}
return [];
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcachedResource();
$expiration = $this->expirationTime();
if (! $memc->add($this->namespacePrefix . $normalizedKey, $value, $expiration)) {
if ($memc->getResultCode() == MemcachedResource::RES_NOTSTORED) {
return false;
}
throw $this->getExceptionByResultCode($memc->getResultCode());
}
return true;
}
/**
* Internal method to replace an existing item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcachedResource();
$expiration = $this->expirationTime();
if (! $memc->replace($this->namespacePrefix . $normalizedKey, $value, $expiration)) {
$rsCode = $memc->getResultCode();
if ($rsCode == MemcachedResource::RES_NOTSTORED) {
return false;
}
throw $this->getExceptionByResultCode($rsCode);
}
return true;
}
/**
* Internal method to set an item only if token matches
*
* @param mixed $token
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
* @see getItem()
* @see setItem()
*/
protected function internalCheckAndSetItem(& $token, & $normalizedKey, & $value)
{
$memc = $this->getMemcachedResource();
$expiration = $this->expirationTime();
$result = $memc->cas($token, $this->namespacePrefix . $normalizedKey, $value, $expiration);
if ($result === false) {
$rsCode = $memc->getResultCode();
if ($rsCode !== 0 && $rsCode != MemcachedResource::RES_DATA_EXISTS) {
throw $this->getExceptionByResultCode($rsCode);
}
}
return $result;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$memc = $this->getMemcachedResource();
$result = $memc->delete($this->namespacePrefix . $normalizedKey);
if ($result === false) {
$rsCode = $memc->getResultCode();
if ($rsCode == MemcachedResource::RES_NOTFOUND) {
return false;
} elseif ($rsCode != MemcachedResource::RES_SUCCESS) {
throw $this->getExceptionByResultCode($rsCode);
}
}
return true;
}
/**
* Internal method to remove multiple items.
*
* @param array $normalizedKeys
* @return array Array of not removed keys
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItems(array & $normalizedKeys)
{
$memc = $this->getMemcachedResource();
// support for removing multiple items at once has been added in ext/memcached-2.0.0
// and HHVM doesn't support this feature yet
if (! method_exists($memc, 'deleteMulti')) {
return parent::internalRemoveItems($normalizedKeys);
}
foreach ($normalizedKeys as & $normalizedKey) {
$normalizedKey = $this->namespacePrefix . $normalizedKey;
}
$missingKeys = [];
foreach ($memc->deleteMulti($normalizedKeys) as $normalizedKey => $rsCode) {
if ($rsCode !== true && $rsCode != MemcachedResource::RES_SUCCESS) {
if ($rsCode != MemcachedResource::RES_NOTFOUND) {
throw $this->getExceptionByResultCode($rsCode);
}
$missingKeys[] = $normalizedKey;
}
}
// remove namespace prefix
if ($missingKeys && $this->namespacePrefix !== '') {
$nsPrefixLength = strlen($this->namespacePrefix);
foreach ($missingKeys as & $missingKey) {
$missingKey = substr($missingKey, $nsPrefixLength);
}
}
return $missingKeys;
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcachedResource();
$internalKey = $this->namespacePrefix . $normalizedKey;
$value = (int) $value;
$newValue = $memc->increment($internalKey, $value);
if ($newValue === false) {
$rsCode = $memc->getResultCode();
// initial value
if ($rsCode == MemcachedResource::RES_NOTFOUND) {
$newValue = $value;
$memc->add($internalKey, $newValue, $this->expirationTime());
$rsCode = $memc->getResultCode();
}
if ($rsCode) {
throw $this->getExceptionByResultCode($rsCode);
}
}
return $newValue;
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$memc = $this->getMemcachedResource();
$internalKey = $this->namespacePrefix . $normalizedKey;
$value = (int) $value;
$newValue = $memc->decrement($internalKey, $value);
if ($newValue === false) {
$rsCode = $memc->getResultCode();
// initial value
if ($rsCode == MemcachedResource::RES_NOTFOUND) {
$newValue = -$value;
$memc->add($internalKey, $newValue, $this->expirationTime());
$rsCode = $memc->getResultCode();
}
if ($rsCode) {
throw $this->getExceptionByResultCode($rsCode);
}
}
return $newValue;
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$this->capabilityMarker = new stdClass();
$this->capabilities = new Capabilities(
$this,
$this->capabilityMarker,
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => 'object',
'resource' => false,
],
'supportedMetadata' => [],
'minTtl' => 1,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => false,
'maxKeyLength' => 255,
'namespaceIsPrefix' => true,
]
);
}
return $this->capabilities;
}
/* internal */
/**
* Get expiration time by ttl
*
* Some storage commands involve sending an expiration value (relative to
* an item or to an operation requested by the client) to the server. In
* all such cases, the actual value sent may either be Unix time (number of
* seconds since January 1, 1970, as an integer), or a number of seconds
* starting from current time. In the latter case, this number of seconds
* may not exceed 60*60*24*30 (number of seconds in 30 days); if the
* expiration value is larger than that, the server will consider it to be
* real Unix time value rather than an offset from current time.
*
* @return int
*/
protected function expirationTime()
{
$ttl = $this->getOptions()->getTtl();
if ($ttl > 2592000) {
return time() + $ttl;
}
return $ttl;
}
/**
* Generate exception based of memcached result code
*
* @param int $code
* @throws Exception\RuntimeException
* @throws Exception\InvalidArgumentException On success code
*/
protected function getExceptionByResultCode($code)
{
switch ($code) {
case MemcachedResource::RES_SUCCESS:
throw new Exception\InvalidArgumentException(
"The result code '{$code}' (SUCCESS) isn't an error"
);
default:
return new Exception\RuntimeException($this->getMemcachedResource()->getResultMessage());
}
}
}

View File

@@ -0,0 +1,329 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Memcached as MemcachedResource;
use Zend\Cache\Exception;
/**
* These are options specific to the Memcached adapter
*/
class MemcachedOptions extends AdapterOptions
{
// @codingStandardsIgnoreStart
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id'];
// @codingStandardsIgnoreEnd
/**
* The namespace separator
* @var string
*/
protected $namespaceSeparator = ':';
/**
* The memcached resource manager
*
* @var null|MemcachedResourceManager
*/
protected $resourceManager;
/**
* The resource id of the resource manager
*
* @var string
*/
protected $resourceId = 'default';
/**
* Set namespace.
*
* The option Memcached::OPT_PREFIX_KEY will be used as the namespace.
* It can't be longer than 128 characters.
*
* @see AdapterOptions::setNamespace()
* @see MemcachedOptions::setPrefixKey()
*/
public function setNamespace($namespace)
{
$namespace = (string) $namespace;
if (128 < strlen($namespace)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a prefix key of no longer than 128 characters',
__METHOD__
));
}
return parent::setNamespace($namespace);
}
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return MemcachedOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
if ($this->namespaceSeparator !== $namespaceSeparator) {
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
}
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* A memcached resource to share
*
* @param null|MemcachedResource $memcachedResource
* @return MemcachedOptions Provides a fluent interface
* @deprecated Please use the resource manager instead
*/
public function setMemcachedResource(MemcachedResource $memcachedResource = null)
{
trigger_error(
'This method is deprecated and will be removed in the feature'
. ', please use the resource manager instead',
E_USER_DEPRECATED
);
if ($memcachedResource !== null) {
$this->triggerOptionEvent('memcached_resource', $memcachedResource);
$resourceManager = $this->getResourceManager();
$resourceId = $this->getResourceId();
$resourceManager->setResource($resourceId, $memcachedResource);
}
return $this;
}
/**
* Get memcached resource to share
*
* @return MemcachedResource
* @deprecated Please use the resource manager instead
*/
public function getMemcachedResource()
{
trigger_error(
'This method is deprecated and will be removed in the feature'
. ', please use the resource manager instead',
E_USER_DEPRECATED
);
return $this->resourceManager->getResource($this->getResourceId());
}
/**
* Set the memcached resource manager to use
*
* @param null|MemcachedResourceManager $resourceManager
* @return MemcachedOptions Provides a fluent interface
*/
public function setResourceManager(MemcachedResourceManager $resourceManager = null)
{
if ($this->resourceManager !== $resourceManager) {
$this->triggerOptionEvent('resource_manager', $resourceManager);
$this->resourceManager = $resourceManager;
}
return $this;
}
/**
* Get the memcached resource manager
*
* @return MemcachedResourceManager
*/
public function getResourceManager()
{
if (! $this->resourceManager) {
$this->resourceManager = new MemcachedResourceManager();
}
return $this->resourceManager;
}
/**
* Get the memcached resource id
*
* @return string
*/
public function getResourceId()
{
return $this->resourceId;
}
/**
* Set the memcached resource id
*
* @param string $resourceId
* @return MemcachedOptions Provides a fluent interface
*/
public function setResourceId($resourceId)
{
$resourceId = (string) $resourceId;
if ($this->resourceId !== $resourceId) {
$this->triggerOptionEvent('resource_id', $resourceId);
$this->resourceId = $resourceId;
}
return $this;
}
/**
* Get the persistent id
*
* @return string
*/
public function getPersistentId()
{
return $this->getResourceManager()->getPersistentId($this->getResourceId());
}
/**
* Set the persistent id
*
* @param string $persistentId
* @return MemcachedOptions Provides a fluent interface
*/
public function setPersistentId($persistentId)
{
$this->triggerOptionEvent('persistent_id', $persistentId);
$this->getResourceManager()->setPersistentId($this->getResourceId(), $persistentId);
return $this;
}
/**
* Add a server to the list
*
* @param string $host
* @param int $port
* @param int $weight
* @return MemcachedOptions Provides a fluent interface
* @deprecated Please use the resource manager instead
*/
public function addServer($host, $port = 11211, $weight = 0)
{
trigger_error(
'This method is deprecated and will be removed in the feature'
. ', please use the resource manager instead',
E_USER_DEPRECATED
);
$this->getResourceManager()->addServer($this->getResourceId(), [
'host' => $host,
'port' => $port,
'weight' => $weight
]);
return $this;
}
/**
* Set a list of memcached servers to add on initialize
*
* @param string|array $servers list of servers
* @return MemcachedOptions Provides a fluent interface
* @throws Exception\InvalidArgumentException
*/
public function setServers($servers)
{
$this->getResourceManager()->setServers($this->getResourceId(), $servers);
return $this;
}
/**
* Get Servers
*
* @return array
*/
public function getServers()
{
return $this->getResourceManager()->getServers($this->getResourceId());
}
/**
* Set libmemcached options
*
* @param array $libOptions
* @return MemcachedOptions Provides a fluent interface
* @link http://php.net/manual/memcached.constants.php
*/
public function setLibOptions(array $libOptions)
{
$this->getResourceManager()->setLibOptions($this->getResourceId(), $libOptions);
return $this;
}
/**
* Set libmemcached option
*
* @param string|int $key
* @param mixed $value
* @return MemcachedOptions Provides a fluent interface
* @link http://php.net/manual/memcached.constants.php
* @deprecated Please use lib_options or the resource manager instead
*/
public function setLibOption($key, $value)
{
trigger_error(
'This method is deprecated and will be removed in the feature'
. ', please use "lib_options" or the resource manager instead',
E_USER_DEPRECATED
);
$this->getResourceManager()->setLibOption($this->getResourceId(), $key, $value);
return $this;
}
/**
* Get libmemcached options
*
* @return array
* @link http://php.net/manual/memcached.constants.php
*/
public function getLibOptions()
{
return $this->getResourceManager()->getLibOptions($this->getResourceId());
}
/**
* Get libmemcached option
*
* @param string|int $key
* @return mixed
* @link http://php.net/manual/memcached.constants.php
* @deprecated Please use lib_options or the resource manager instead
*/
public function getLibOption($key)
{
trigger_error(
'This method is deprecated and will be removed in the feature'
. ', please use "lib_options" or the resource manager instead',
E_USER_DEPRECATED
);
return $this->getResourceManager()->getLibOption($this->getResourceId(), $key);
}
}

View File

@@ -0,0 +1,547 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Memcached as MemcachedResource;
use ReflectionClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Stdlib\ArrayUtils;
/**
* This is a resource manager for memcached
*/
class MemcachedResourceManager
{
/**
* Registered resources
*
* @var array
*/
protected $resources = [];
/**
* Get servers
* @param string $id
* @throws Exception\RuntimeException
* @return array array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
*/
public function getServers($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
return $resource->getServerList();
}
return $resource['servers'];
}
/**
* Normalize one server into the following format:
* array('host' => <host>, 'port' => <port>, 'weight' => <weight>)
*
* @param string|array &$server
* @throws Exception\InvalidArgumentException
*/
protected function normalizeServer(&$server)
{
$host = null;
$port = 11211;
$weight = 0;
// convert a single server into an array
if ($server instanceof Traversable) {
$server = ArrayUtils::iteratorToArray($server);
}
if (is_array($server)) {
// array(<host>[, <port>[, <weight>]])
if (isset($server[0])) {
$host = (string) $server[0];
$port = isset($server[1]) ? (int) $server[1] : $port;
$weight = isset($server[2]) ? (int) $server[2] : $weight;
}
// array('host' => <host>[, 'port' => <port>[, 'weight' => <weight>]])
if (! isset($server[0]) && isset($server['host'])) {
$host = (string) $server['host'];
$port = isset($server['port']) ? (int) $server['port'] : $port;
$weight = isset($server['weight']) ? (int) $server['weight'] : $weight;
}
} else {
// parse server from URI host{:?port}{?weight}
$server = trim($server);
if (strpos($server, '://') === false) {
$server = 'tcp://' . $server;
}
$server = parse_url($server);
if (! $server) {
throw new Exception\InvalidArgumentException("Invalid server given");
}
$host = $server['host'];
$port = isset($server['port']) ? (int) $server['port'] : $port;
if (isset($server['query'])) {
$query = null;
parse_str($server['query'], $query);
if (isset($query['weight'])) {
$weight = (int) $query['weight'];
}
}
}
if (! $host) {
throw new Exception\InvalidArgumentException('Missing required server host');
}
$server = [
'host' => $host,
'port' => $port,
'weight' => $weight,
];
}
/**
* Check if a resource exists
*
* @param string $id
* @return bool
*/
public function hasResource($id)
{
return isset($this->resources[$id]);
}
/**
* Gets a memcached resource
*
* @param string $id
* @return MemcachedResource
* @throws Exception\RuntimeException
*/
public function getResource($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = $this->resources[$id];
if ($resource instanceof MemcachedResource) {
return $resource;
}
if ($resource['persistent_id'] !== '') {
$memc = new MemcachedResource($resource['persistent_id']);
} else {
$memc = new MemcachedResource();
}
if (method_exists($memc, 'setOptions')) {
$memc->setOptions($resource['lib_options']);
} else {
foreach ($resource['lib_options'] as $k => $v) {
$memc->setOption($k, $v);
}
}
// merge and add servers (with persistence id servers could be added already)
$servers = array_udiff($resource['servers'], $memc->getServerList(), [$this, 'compareServers']);
if ($servers) {
$memc->addServers(array_values(array_map('array_values', $servers)));
}
// buffer and return
$this->resources[$id] = $memc;
return $memc;
}
/**
* Set a resource
*
* @param string $id
* @param array|Traversable|MemcachedResource $resource
* @return MemcachedResourceManager Provides a fluent interface
*/
public function setResource($id, $resource)
{
$id = (string) $id;
if (! ($resource instanceof MemcachedResource)) {
if ($resource instanceof Traversable) {
$resource = ArrayUtils::iteratorToArray($resource);
} elseif (! is_array($resource)) {
throw new Exception\InvalidArgumentException(
'Resource must be an instance of Memcached or an array or Traversable'
);
}
$resource = array_merge([
'persistent_id' => '',
'lib_options' => [],
'servers' => [],
], $resource);
// normalize and validate params
$this->normalizePersistentId($resource['persistent_id']);
$this->normalizeLibOptions($resource['lib_options']);
$this->normalizeServers($resource['servers']);
}
$this->resources[$id] = $resource;
return $this;
}
/**
* Remove a resource
*
* @param string $id
* @return MemcachedResourceManager Provides a fluent interface
*/
public function removeResource($id)
{
unset($this->resources[$id]);
return $this;
}
/**
* Set the persistent id
*
* @param string $id
* @param string $persistentId
* @return MemcachedResourceManager Provides a fluent interface
* @throws Exception\RuntimeException
*/
public function setPersistentId($id, $persistentId)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'persistent_id' => $persistentId
]);
}
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
throw new Exception\RuntimeException(
"Can't change persistent id of resource {$id} after instanziation"
);
}
$this->normalizePersistentId($persistentId);
$resource['persistent_id'] = $persistentId;
return $this;
}
/**
* Get the persistent id
*
* @param string $id
* @return string
* @throws Exception\RuntimeException
*/
public function getPersistentId($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
throw new Exception\RuntimeException(
"Can't get persistent id of an instantiated memcached resource"
);
}
return $resource['persistent_id'];
}
/**
* Normalize the persistent id
*
* @param string $persistentId
*/
protected function normalizePersistentId(& $persistentId)
{
$persistentId = (string) $persistentId;
}
/**
* Set Libmemcached options
*
* @param string $id
* @param array $libOptions
* @return MemcachedResourceManager Provides a fluent interface
*/
public function setLibOptions($id, array $libOptions)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'lib_options' => $libOptions
]);
}
$this->normalizeLibOptions($libOptions);
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
if (method_exists($resource, 'setOptions')) {
$resource->setOptions($libOptions);
} else {
foreach ($libOptions as $key => $value) {
$resource->setOption($key, $value);
}
}
} else {
$resource['lib_options'] = $libOptions;
}
return $this;
}
/**
* Get Libmemcached options
*
* @param string $id
* @return array
* @throws Exception\RuntimeException
*/
public function getLibOptions($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
$libOptions = [];
$reflection = new ReflectionClass('Memcached');
$constants = $reflection->getConstants();
foreach ($constants as $constName => $constValue) {
if (strpos($constName, 'OPT_') === 0) {
$libOptions[$constValue] = $resource->getOption($constValue);
}
}
return $libOptions;
}
return $resource['lib_options'];
}
/**
* Set one Libmemcached option
*
* @param string $id
* @param string|int $key
* @param mixed $value
* @return MemcachedResourceManager Fluent interface
*/
public function setLibOption($id, $key, $value)
{
return $this->setLibOptions($id, [$key => $value]);
}
/**
* Get one Libmemcached option
*
* @param string $id
* @param string|int $key
* @return mixed
* @throws Exception\RuntimeException
*/
public function getLibOption($id, $key)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$this->normalizeLibOptionKey($key);
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
return $resource->getOption($key);
}
return isset($resource['lib_options'][$key]) ? $resource['lib_options'][$key] : null;
}
/**
* Normalize libmemcached options
*
* @param array|Traversable $libOptions
* @throws Exception\InvalidArgumentException
*/
protected function normalizeLibOptions(& $libOptions)
{
if (! is_array($libOptions) && ! ($libOptions instanceof Traversable)) {
throw new Exception\InvalidArgumentException(
"Lib-Options must be an array or an instance of Traversable"
);
}
$result = [];
foreach ($libOptions as $key => $value) {
$this->normalizeLibOptionKey($key);
$result[$key] = $value;
}
$libOptions = $result;
}
/**
* Convert option name into it's constant value
*
* @param string|int $key
* @throws Exception\InvalidArgumentException
*/
protected function normalizeLibOptionKey(& $key)
{
// convert option name into it's constant value
if (is_string($key)) {
$const = 'Memcached::OPT_' . str_replace([' ', '-'], '_', strtoupper($key));
if (! defined($const)) {
throw new Exception\InvalidArgumentException("Unknown libmemcached option '{$key}' ({$const})");
}
$key = constant($const);
} else {
$key = (int) $key;
}
}
/**
* Set servers
*
* $servers can be an array list or a comma separated list of servers.
* One server in the list can be descripted as follows:
* - URI: [tcp://]<host>[:<port>][?weight=<weight>]
* - Assoc: array('host' => <host>[, 'port' => <port>][, 'weight' => <weight>])
* - List: array(<host>[, <port>][, <weight>])
*
* @param string $id
* @param string|array $servers
* @return MemcachedResourceManager Provides a fluent interface
*/
public function setServers($id, $servers)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'servers' => $servers
]);
}
$this->normalizeServers($servers);
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
// don't add servers twice
$servers = array_udiff($servers, $resource->getServerList(), [$this, 'compareServers']);
if ($servers) {
$resource->addServers($servers);
}
} else {
$resource['servers'] = $servers;
}
return $this;
}
/**
* Add servers
*
* @param string $id
* @param string|array $servers
* @return MemcachedResourceManager Provides a fluent interface
*/
public function addServers($id, $servers)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'servers' => $servers
]);
}
$this->normalizeServers($servers);
$resource = & $this->resources[$id];
if ($resource instanceof MemcachedResource) {
// don't add servers twice
$servers = array_udiff($servers, $resource->getServerList(), [$this, 'compareServers']);
if ($servers) {
$resource->addServers($servers);
}
} else {
// don't add servers twice
$resource['servers'] = array_merge(
$resource['servers'],
array_udiff($servers, $resource['servers'], [$this, 'compareServers'])
);
}
return $this;
}
/**
* Add one server
*
* @param string $id
* @param string|array $server
* @return MemcachedResourceManager
*/
public function addServer($id, $server)
{
return $this->addServers($id, [$server]);
}
/**
* Normalize a list of servers into the following format:
* array(array('host' => <host>, 'port' => <port>, 'weight' => <weight>)[, ...])
*
* @param string|array $servers
*/
protected function normalizeServers(& $servers)
{
if (! is_array($servers) && ! $servers instanceof Traversable) {
// Convert string into a list of servers
$servers = explode(',', $servers);
}
$result = [];
foreach ($servers as $server) {
$this->normalizeServer($server);
$result[$server['host'] . ':' . $server['port']] = $server;
}
$servers = array_values($result);
}
/**
* Compare 2 normalized server arrays
* (Compares only the host and the port)
*
* @param array $serverA
* @param array $serverB
* @return int
*/
protected function compareServers(array $serverA, array $serverB)
{
$keyA = $serverA['host'] . ':' . $serverA['port'];
$keyB = $serverB['host'] . ':' . $serverB['port'];
if ($keyA === $keyB) {
return 0;
}
return $keyA > $keyB ? 1 : -1;
}
}

View File

@@ -0,0 +1,745 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use stdClass;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Storage\ClearExpiredInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\IterableInterface;
use Zend\Cache\Storage\TaggableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class Memory extends AbstractAdapter implements
AvailableSpaceCapableInterface,
ClearByPrefixInterface,
ClearByNamespaceInterface,
ClearExpiredInterface,
FlushableInterface,
IterableInterface,
TaggableInterface,
TotalSpaceCapableInterface
{
/**
* Data Array
*
* Format:
* array(
* <NAMESPACE> => array(
* <KEY> => array(
* 0 => <VALUE>
* 1 => <MICROTIME>
* ['tags' => <TAGS>]
* )
* )
* )
*
* @var array
*/
protected $data = [];
/**
* Set options.
*
* @param array|\Traversable|MemoryOptions $options
* @return Memory
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof MemoryOptions) {
$options = new MemoryOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return MemoryOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new MemoryOptions());
}
return $this->options;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int
*/
public function getTotalSpace()
{
return $this->getOptions()->getMemoryLimit();
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
$total = $this->getOptions()->getMemoryLimit();
$avail = $total - (float) memory_get_usage(true);
return ($avail > 0) ? $avail : 0;
}
/* IterableInterface */
/**
* Get the storage iterator
*
* @return KeyListIterator
*/
public function getIterator()
{
$ns = $this->getOptions()->getNamespace();
$keys = [];
if (isset($this->data[$ns])) {
foreach ($this->data[$ns] as $key => & $tmp) {
if ($this->internalHasItem($key)) {
$keys[] = $key;
}
}
}
return new KeyListIterator($this, $keys);
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
$this->data = [];
return true;
}
/* ClearExpiredInterface */
/**
* Remove expired items
*
* @return bool
*/
public function clearExpired()
{
$ttl = $this->getOptions()->getTtl();
if ($ttl <= 0) {
return true;
}
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns])) {
return true;
}
$data = & $this->data[$ns];
foreach ($data as $key => & $item) {
if (microtime(true) >= $data[$key][1] + $ttl) {
unset($data[$key]);
}
}
return true;
}
/* ClearByNamespaceInterface */
public function clearByNamespace($namespace)
{
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
unset($this->data[$namespace]);
return true;
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns])) {
return true;
}
$data = & $this->data[$ns];
foreach ($data as $key => & $item) {
if (strpos($key, $prefix) === 0) {
unset($data[$key]);
}
}
return true;
}
/* TaggableInterface */
/**
* Set tags to an item by given key.
* An empty array will remove all tags.
*
* @param string $key
* @param string[] $tags
* @return bool
*/
public function setTags($key, array $tags)
{
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns][$key])) {
return false;
}
$this->data[$ns][$key]['tags'] = $tags;
return true;
}
/**
* Get tags of an item by given key
*
* @param string $key
* @return string[]|FALSE
*/
public function getTags($key)
{
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns][$key])) {
return false;
}
return isset($this->data[$ns][$key]['tags']) ? $this->data[$ns][$key]['tags'] : [];
}
/**
* Remove items matching given tags.
*
* If $disjunction only one of the given tags must match
* else all given tags must match.
*
* @param string[] $tags
* @param bool $disjunction
* @return bool
*/
public function clearByTags(array $tags, $disjunction = false)
{
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns])) {
return true;
}
$tagCount = count($tags);
$data = & $this->data[$ns];
foreach ($data as $key => & $item) {
if (isset($item['tags'])) {
$diff = array_diff($tags, $item['tags']);
if (($disjunction && count($diff) < $tagCount) || (! $disjunction && ! $diff)) {
unset($data[$key]);
}
}
}
return true;
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$options = $this->getOptions();
$ns = $options->getNamespace();
$success = isset($this->data[$ns][$normalizedKey]);
if ($success) {
$data = & $this->data[$ns][$normalizedKey];
$ttl = $options->getTtl();
if ($ttl && microtime(true) >= ($data[1] + $ttl)) {
$success = false;
}
}
if (! $success) {
return;
}
$casToken = $data[0];
return $data[0];
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$ns = $options->getNamespace();
if (! isset($this->data[$ns])) {
return [];
}
$data = & $this->data[$ns];
$ttl = $options->getTtl();
$now = microtime(true);
$result = [];
foreach ($normalizedKeys as $normalizedKey) {
if (isset($data[$normalizedKey])) {
if (! $ttl || $now < ($data[$normalizedKey][1] + $ttl)) {
$result[$normalizedKey] = $data[$normalizedKey][0];
}
}
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
*/
protected function internalHasItem(& $normalizedKey)
{
$options = $this->getOptions();
$ns = $options->getNamespace();
if (! isset($this->data[$ns][$normalizedKey])) {
return false;
}
// check if expired
$ttl = $options->getTtl();
if ($ttl && microtime(true) >= ($this->data[$ns][$normalizedKey][1] + $ttl)) {
return false;
}
return true;
}
/**
* Internal method to test multiple items.
*
* @param array $normalizedKeys
* @return array Array of found keys
*/
protected function internalHasItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$ns = $options->getNamespace();
if (! isset($this->data[$ns])) {
return [];
}
$data = & $this->data[$ns];
$ttl = $options->getTtl();
$now = microtime(true);
$result = [];
foreach ($normalizedKeys as $normalizedKey) {
if (isset($data[$normalizedKey])) {
if (! $ttl || $now < ($data[$normalizedKey][1] + $ttl)) {
$result[] = $normalizedKey;
}
}
}
return $result;
}
/**
* Get metadata of an item.
*
* @param string $normalizedKey
* @return array|bool Metadata on success, false on failure
* @throws Exception\ExceptionInterface
*
* @triggers getMetadata.pre(PreEvent)
* @triggers getMetadata.post(PostEvent)
* @triggers getMetadata.exception(ExceptionEvent)
*/
protected function internalGetMetadata(& $normalizedKey)
{
if (! $this->internalHasItem($normalizedKey)) {
return false;
}
$ns = $this->getOptions()->getNamespace();
return [
'mtime' => $this->data[$ns][$normalizedKey][1],
];
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
if (! $this->hasAvailableSpace()) {
$memoryLimit = $options->getMemoryLimit();
throw new Exception\OutOfSpaceException(
"Memory usage exceeds limit ({$memoryLimit})."
);
}
$ns = $options->getNamespace();
$this->data[$ns][$normalizedKey] = [$value, microtime(true)];
return true;
}
/**
* Internal method to store multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
if (! $this->hasAvailableSpace()) {
$memoryLimit = $options->getMemoryLimit();
throw new Exception\OutOfSpaceException(
"Memory usage exceeds limit ({$memoryLimit})."
);
}
$ns = $options->getNamespace();
if (! isset($this->data[$ns])) {
$this->data[$ns] = [];
}
$data = & $this->data[$ns];
$now = microtime(true);
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$data[$normalizedKey] = [$value, $now];
}
return [];
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
if (! $this->hasAvailableSpace()) {
$memoryLimit = $options->getMemoryLimit();
throw new Exception\OutOfSpaceException(
"Memory usage exceeds limit ({$memoryLimit})."
);
}
$ns = $options->getNamespace();
if (isset($this->data[$ns][$normalizedKey])) {
return false;
}
$this->data[$ns][$normalizedKey] = [$value, microtime(true)];
return true;
}
/**
* Internal method to add multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalAddItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
if (! $this->hasAvailableSpace()) {
$memoryLimit = $options->getMemoryLimit();
throw new Exception\OutOfSpaceException(
"Memory usage exceeds limit ({$memoryLimit})."
);
}
$ns = $options->getNamespace();
if (! isset($this->data[$ns])) {
$this->data[$ns] = [];
}
$result = [];
$data = & $this->data[$ns];
$now = microtime(true);
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
if (isset($data[$normalizedKey])) {
$result[] = $normalizedKey;
} else {
$data[$normalizedKey] = [$value, $now];
}
}
return $result;
}
/**
* Internal method to replace an existing item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItem(& $normalizedKey, & $value)
{
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns][$normalizedKey])) {
return false;
}
$this->data[$ns][$normalizedKey] = [$value, microtime(true)];
return true;
}
/**
* Internal method to replace multiple existing items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItems(array & $normalizedKeyValuePairs)
{
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns])) {
return array_keys($normalizedKeyValuePairs);
}
$result = [];
$data = & $this->data[$ns];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
if (! isset($data[$normalizedKey])) {
$result[] = $normalizedKey;
} else {
$data[$normalizedKey] = [$value, microtime(true)];
}
}
return $result;
}
/**
* Internal method to reset lifetime of an item
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalTouchItem(& $normalizedKey)
{
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns][$normalizedKey])) {
return false;
}
$this->data[$ns][$normalizedKey][1] = microtime(true);
return true;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$ns = $this->getOptions()->getNamespace();
if (! isset($this->data[$ns][$normalizedKey])) {
return false;
}
unset($this->data[$ns][$normalizedKey]);
// remove empty namespace
if (! $this->data[$ns]) {
unset($this->data[$ns]);
}
return true;
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$ns = $this->getOptions()->getNamespace();
if (isset($this->data[$ns][$normalizedKey])) {
$data = & $this->data[$ns][$normalizedKey];
$data[0] += $value;
$data[1] = microtime(true);
$newValue = $data[0];
} else {
// initial value
$newValue = $value;
$this->data[$ns][$normalizedKey] = [$newValue, microtime(true)];
}
return $newValue;
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$ns = $this->getOptions()->getNamespace();
if (isset($this->data[$ns][$normalizedKey])) {
$data = & $this->data[$ns][$normalizedKey];
$data[0] -= $value;
$data[1] = microtime(true);
$newValue = $data[0];
} else {
// initial value
$newValue = -$value;
$this->data[$ns][$normalizedKey] = [$newValue, microtime(true)];
}
return $newValue;
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$this->capabilityMarker = new stdClass();
$this->capabilities = new Capabilities(
$this,
$this->capabilityMarker,
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => true,
'resource' => true,
],
'supportedMetadata' => ['mtime'],
'minTtl' => 1,
'maxTtl' => PHP_INT_MAX,
'staticTtl' => false,
'ttlPrecision' => 0.05,
'maxKeyLength' => 0,
'namespaceIsPrefix' => false,
'namespaceSeparator' => '',
]
);
}
return $this->capabilities;
}
/* internal */
/**
* Has space available to store items?
*
* @return bool
*/
protected function hasAvailableSpace()
{
$total = $this->getOptions()->getMemoryLimit();
// check memory limit disabled
if ($total <= 0) {
return true;
}
$free = $total - (float) memory_get_usage(true);
return ($free > 0);
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Exception;
/**
* These are options specific to the APC adapter
*/
class MemoryOptions extends AdapterOptions
{
/**
* memory limit
*
* @var null|int
*/
protected $memoryLimit = null;
/**
* Set memory limit
*
* - A number less or equal 0 will disable the memory limit
* - When a number is used, the value is measured in bytes. Shorthand notation may also be used.
* - If the used memory of PHP exceeds this limit an OutOfSpaceException
* will be thrown.
*
* @link http://php.net/manual/faq.using.php#faq.using.shorthandbytes
* @param string|int $memoryLimit
* @return MemoryOptions Provides a fluent interface
*/
public function setMemoryLimit($memoryLimit)
{
$memoryLimit = $this->normalizeMemoryLimit($memoryLimit);
if ($this->memoryLimit != $memoryLimit) {
$this->triggerOptionEvent('memory_limit', $memoryLimit);
$this->memoryLimit = $memoryLimit;
}
return $this;
}
/**
* Get memory limit
*
* If the used memory of PHP exceeds this limit an OutOfSpaceException
* will be thrown.
*
* @return int
*/
public function getMemoryLimit()
{
if ($this->memoryLimit === null) {
// By default use half of PHP's memory limit if possible
$memoryLimit = $this->normalizeMemoryLimit(ini_get('memory_limit'));
if ($memoryLimit >= 0) {
$this->memoryLimit = (int) ($memoryLimit / 2);
} else {
// disable memory limit
$this->memoryLimit = 0;
}
}
return $this->memoryLimit;
}
/**
* Normalized a given value of memory limit into the number of bytes
*
* @param string|int $value
* @throws Exception\InvalidArgumentException
* @return int
*/
protected function normalizeMemoryLimit($value)
{
if (is_numeric($value)) {
return (int) $value;
}
if (! preg_match('/(\-?\d+)\s*(\w*)/', ini_get('memory_limit'), $matches)) {
throw new Exception\InvalidArgumentException("Invalid memory limit '{$value}'");
}
$value = (int) $matches[1];
if ($value <= 0) {
return 0;
}
switch (strtoupper($matches[2])) {
case 'G':
$value *= 1024;
// no break
case 'M':
$value *= 1024;
// no break
case 'K':
$value *= 1024;
// no break
}
return $value;
}
}

View File

@@ -0,0 +1,282 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use MongoCollection as MongoResource;
use MongoDate;
use MongoException as MongoResourceException;
use stdClass;
use Zend\Cache\Exception;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\FlushableInterface;
class MongoDb extends AbstractAdapter implements FlushableInterface
{
/**
* Has this instance be initialized
*
* @var bool
*/
private $initialized = false;
/**
* the mongodb resource manager
*
* @var null|MongoDbResourceManager
*/
private $resourceManager;
/**
* The mongodb resource id
*
* @var null|string
*/
private $resourceId;
/**
* The namespace prefix
*
* @var string
*/
private $namespacePrefix = '';
/**
* {@inheritDoc}
*
* @throws Exception\ExtensionNotLoadedException
*/
public function __construct($options = null)
{
if (! class_exists('Mongo') || ! class_exists('MongoClient')) {
throw new Exception\ExtensionNotLoadedException(
'MongoDb extension not loaded or Mongo polyfill not included'
);
}
parent::__construct($options);
$initialized = & $this->initialized;
$this->getEventManager()->attach(
'option',
function () use (& $initialized) {
$initialized = false;
}
);
}
/**
* get mongodb resource
*
* @return MongoResource
*/
private function getMongoDbResource()
{
if (! $this->initialized) {
$options = $this->getOptions();
$this->resourceManager = $options->getResourceManager();
$this->resourceId = $options->getResourceId();
$namespace = $options->getNamespace();
$this->namespacePrefix = ($namespace === '' ? '' : $namespace . $options->getNamespaceSeparator());
$this->initialized = true;
}
return $this->resourceManager->getResource($this->resourceId);
}
/**
* {@inheritDoc}
*/
public function setOptions($options)
{
return parent::setOptions($options instanceof MongoDbOptions ? $options : new MongoDbOptions($options));
}
/**
* Get options.
*
* @return MongoDbOptions
* @see setOptions()
*/
public function getOptions()
{
return $this->options;
}
/**
* {@inheritDoc}
*
* @throws Exception\RuntimeException
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$result = $this->fetchFromCollection($normalizedKey);
$success = false;
if (null === $result) {
return;
}
if (isset($result['expires'])) {
if (! $result['expires'] instanceof MongoDate) {
throw new Exception\RuntimeException(sprintf(
"The found item _id '%s' for key '%s' is not a valid cache item"
. ": the field 'expired' isn't an instance of MongoDate, '%s' found instead",
(string) $result['_id'],
$this->namespacePrefix . $normalizedKey,
is_object($result['expires']) ? get_class($result['expires']) : gettype($result['expires'])
));
}
if ($result['expires']->sec < time()) {
$this->internalRemoveItem($normalizedKey);
return;
}
}
if (! array_key_exists('value', $result)) {
throw new Exception\RuntimeException(sprintf(
"The found item _id '%s' for key '%s' is not a valid cache item: missing the field 'value'",
(string) $result['_id'],
$this->namespacePrefix . $normalizedKey
));
}
$success = true;
return $casToken = $result['value'];
}
/**
* {@inheritDoc}
*
* @throws Exception\RuntimeException
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$mongo = $this->getMongoDbResource();
$key = $this->namespacePrefix . $normalizedKey;
$ttl = $this->getOptions()->getTTl();
$expires = null;
$cacheItem = [
'key' => $key,
'value' => $value,
];
if ($ttl > 0) {
$expiresMicro = microtime(true) + $ttl;
$expiresSecs = (int) $expiresMicro;
$cacheItem['expires'] = new MongoDate($expiresSecs, $expiresMicro - $expiresSecs);
}
try {
$mongo->remove(['key' => $key]);
$result = $mongo->insert($cacheItem);
} catch (MongoResourceException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
return null !== $result && ((double) 1) === $result['ok'];
}
/**
* {@inheritDoc}
*
* @throws Exception\RuntimeException
*/
protected function internalRemoveItem(& $normalizedKey)
{
try {
$result = $this->getMongoDbResource()->remove(['key' => $this->namespacePrefix . $normalizedKey]);
} catch (MongoResourceException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
return false !== $result
&& ((double) 1) === $result['ok']
&& $result['n'] > 0;
}
/**
* {@inheritDoc}
*/
public function flush()
{
$result = $this->getMongoDbResource()->drop();
return ((double) 1) === $result['ok'];
}
/**
* {@inheritDoc}
*/
protected function internalGetCapabilities()
{
if ($this->capabilities) {
return $this->capabilities;
}
return $this->capabilities = new Capabilities(
$this,
$this->capabilityMarker = new stdClass(),
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => false,
'resource' => false,
],
'supportedMetadata' => [
'_id',
],
'minTtl' => 1,
'staticTtl' => true,
'maxKeyLength' => 255,
'namespaceIsPrefix' => true,
]
);
}
/**
* {@inheritDoc}
*
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$result = $this->fetchFromCollection($normalizedKey);
return null !== $result ? ['_id' => $result['_id']] : false;
}
/**
* Return raw records from MongoCollection
*
* @param string $normalizedKey
*
* @return array|null
*
* @throws Exception\RuntimeException
*/
private function fetchFromCollection(& $normalizedKey)
{
try {
return $this->getMongoDbResource()->findOne(['key' => $this->namespacePrefix . $normalizedKey]);
} catch (MongoResourceException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
}
}

View File

@@ -0,0 +1,197 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
class MongoDbOptions extends AdapterOptions
{
// @codingStandardsIgnoreStart
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id'];
// @codingStandardsIgnoreEnd
/**
* The namespace separator
*
* @var string
*/
private $namespaceSeparator = ':';
/**
* The mongo DB resource manager
*
* @var null|MongoDbResourceManager
*/
private $resourceManager;
/**
* The resource id of the resource manager
*
* @var string
*/
private $resourceId = 'default';
/**
* Set namespace separator
*
* @param string $namespaceSeparator
*
* @return MongoDbOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
if ($this->namespaceSeparator !== $namespaceSeparator) {
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
}
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Set the mongodb resource manager to use
*
* @param null|MongoDbResourceManager $resourceManager
*
* @return MongoDbOptions Provides a fluent interface
*/
public function setResourceManager(MongoDbResourceManager $resourceManager = null)
{
if ($this->resourceManager !== $resourceManager) {
$this->triggerOptionEvent('resource_manager', $resourceManager);
$this->resourceManager = $resourceManager;
}
return $this;
}
/**
* Get the mongodb resource manager
*
* @return MongoDbResourceManager
*/
public function getResourceManager()
{
return $this->resourceManager ?: $this->resourceManager = new MongoDbResourceManager();
}
/**
* Get the mongodb resource id
*
* @return string
*/
public function getResourceId()
{
return $this->resourceId;
}
/**
* Set the mongodb resource id
*
* @param string $resourceId
*
* @return MongoDbOptions Provides a fluent interface
*/
public function setResourceId($resourceId)
{
$resourceId = (string) $resourceId;
if ($this->resourceId !== $resourceId) {
$this->triggerOptionEvent('resource_id', $resourceId);
$this->resourceId = $resourceId;
}
return $this;
}
/**
* Set the mongo DB server
*
* @param string $server
*
* @return MongoDbOptions Provides a fluent interface
*/
public function setServer($server)
{
$this->getResourceManager()->setServer($this->getResourceId(), $server);
return $this;
}
/**
*
*
* @param array $connectionOptions
*
* @return MongoDbOptions Provides a fluent interface
*/
public function setConnectionOptions(array $connectionOptions)
{
$this->getResourceManager()->setConnectionOptions($this->getResourceId(), $connectionOptions);
return $this;
}
/**
*
*
* @param array $driverOptions
MongoDbOptions
* @return MongoDbOptions Provides a fluent interface
*/
public function setDriverOptions(array $driverOptions)
{
$this->getResourceManager()->setDriverOptions($this->getResourceId(), $driverOptions);
return $this;
}
/**
*
*
* @param string $database
*
* @return MongoDbOptions Provides a fluent interface
*/
public function setDatabase($database)
{
$this->getResourceManager()->setDatabase($this->getResourceId(), $database);
return $this;
}
/**
*
*
* @param string $collection
*
* @return MongoDbOptions Provides a fluent interface
*/
public function setCollection($collection)
{
$this->getResourceManager()->setCollection($this->getResourceId(), $collection);
return $this;
}
}

View File

@@ -0,0 +1,204 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use MongoCollection;
use MongoException;
use Zend\Cache\Exception;
class MongoDbResourceManager
{
/**
* Registered resources
*
* @var array[]
*/
private $resources = [];
/**
* Check if a resource exists
*
* @param string $id
*
* @return bool
*/
public function hasResource($id)
{
return isset($this->resources[$id]);
}
/**
* Set a resource
*
* @param string $id
* @param array|MongoCollection $resource
*
* @return MongoDbResourceManager Provides a fluent interface
*
* @throws Exception\RuntimeException
*/
public function setResource($id, $resource)
{
if ($resource instanceof MongoCollection) {
$this->resources[$id] = [
'db' => (string) $resource->db,
'db_instance' => $resource->db,
'collection' => (string) $resource,
'collection_instance' => $resource,
];
return $this;
}
if (! is_array($resource)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects an array or MongoCollection; received %s',
__METHOD__,
(is_object($resource) ? get_class($resource) : gettype($resource))
));
}
$this->resources[$id] = $resource;
return $this;
}
/**
* Instantiate and return the MongoCollection resource
*
* @param string $id
* @return MongoCollection
* @throws Exception\RuntimeException
*/
public function getResource($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = $this->resources[$id];
if (! isset($resource['collection_instance'])) {
try {
if (! isset($resource['db_instance'])) {
if (! isset($resource['client_instance'])) {
$clientClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
$resource['client_instance'] = new $clientClass(
isset($resource['server']) ? $resource['server'] : null,
isset($resource['connection_options']) ? $resource['connection_options'] : [],
isset($resource['driver_options']) ? $resource['driver_options'] : []
);
}
$resource['db_instance'] = $resource['client_instance']->selectDB(
isset($resource['db']) ? $resource['db'] : ''
);
}
$collection = $resource['db_instance']->selectCollection(
isset($resource['collection']) ? $resource['collection'] : ''
);
$collection->ensureIndex(['key' => 1]);
$this->resources[$id]['collection_instance'] = $collection;
} catch (MongoException $e) {
throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e);
}
}
return $this->resources[$id]['collection_instance'];
}
public function setServer($id, $server)
{
$this->resources[$id]['server'] = (string)$server;
unset($this->resources[$id]['client_instance']);
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
public function getServer($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['server']) ? $this->resources[$id]['server'] : null;
}
public function setConnectionOptions($id, array $connectionOptions)
{
$this->resources[$id]['connection_options'] = $connectionOptions;
unset($this->resources[$id]['client_instance']);
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
public function getConnectionOptions($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['connection_options'])
? $this->resources[$id]['connection_options']
: [];
}
public function setDriverOptions($id, array $driverOptions)
{
$this->resources[$id]['driver_options'] = $driverOptions;
unset($this->resources[$id]['client_instance']);
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
public function getDriverOptions($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['driver_options']) ? $this->resources[$id]['driver_options'] : [];
}
public function setDatabase($id, $database)
{
$this->resources[$id]['db'] = (string)$database;
unset($this->resources[$id]['db_instance']);
unset($this->resources[$id]['collection_instance']);
}
public function getDatabase($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['db']) ? $this->resources[$id]['db'] : '';
}
public function setCollection($id, $collection)
{
$this->resources[$id]['collection'] = (string)$collection;
unset($this->resources[$id]['collection_instance']);
}
public function getCollection($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
return isset($this->resources[$id]['collection']) ? $this->resources[$id]['collection'] : '';
}
}

View File

@@ -0,0 +1,626 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Redis as RedisResource;
use RedisException as RedisResourceException;
use stdClass;
use Traversable;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Exception;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class Redis extends AbstractAdapter implements
ClearByNamespaceInterface,
ClearByPrefixInterface,
FlushableInterface,
TotalSpaceCapableInterface
{
/**
* Has this instance be initialized
*
* @var bool
*/
protected $initialized = false;
/**
* The redis resource manager
*
* @var null|RedisResourceManager
*/
protected $resourceManager;
/**
* The redis resource id
*
* @var null|string
*/
protected $resourceId;
/**
* The namespace prefix
*
* @var string
*/
protected $namespacePrefix = '';
/**
* Create new Adapter for redis storage
*
* @param null|array|Traversable|RedisOptions $options
* @see \Zend\Cache\Storage\Adapter\Abstract
*/
public function __construct($options = null)
{
if (! extension_loaded('redis')) {
throw new Exception\ExtensionNotLoadedException("Redis extension is not loaded");
}
parent::__construct($options);
// reset initialized flag on update option(s)
$initialized = & $this->initialized;
$this->getEventManager()->attach('option', function () use (& $initialized) {
$initialized = false;
});
}
/**
* Get Redis resource
*
* @return RedisResource
*/
protected function getRedisResource()
{
if (! $this->initialized) {
$options = $this->getOptions();
// get resource manager and resource id
$this->resourceManager = $options->getResourceManager();
$this->resourceId = $options->getResourceId();
// init namespace prefix
$namespace = $options->getNamespace();
if ($namespace !== '') {
$this->namespacePrefix = $namespace . $options->getNamespaceSeparator();
} else {
$this->namespacePrefix = '';
}
// update initialized flag
$this->initialized = true;
}
return $this->resourceManager->getResource($this->resourceId);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|RedisOptions $options
* @return Redis
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof RedisOptions) {
$options = new RedisOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return RedisOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new RedisOptions());
}
return $this->options;
}
/**
* Internal method to get an item.
*
* @param string &$normalizedKey Key where to store data
* @param bool &$success If the operation was successful
* @param mixed &$casToken Token
* @return mixed Data on success, false on key not found
* @throws Exception\RuntimeException
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$redis = $this->getRedisResource();
try {
$value = $redis->get($this->namespacePrefix . $normalizedKey);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
if ($value === false) {
$success = false;
return;
}
$success = true;
$casToken = $value;
return $value;
}
/**
* Internal method to get multiple items.
*
* @param array &$normalizedKeys Array of keys to be obtained
*
* @return array Associative array of keys and values
* @throws Exception\RuntimeException
*/
protected function internalGetItems(array & $normalizedKeys)
{
$redis = $this->getRedisResource();
$namespacedKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$namespacedKeys[] = $this->namespacePrefix . $normalizedKey;
}
try {
$results = $redis->mGet($namespacedKeys);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
//combine the key => value pairs and remove all missing values
return array_filter(
array_combine($normalizedKeys, $results),
function ($value) {
return $value !== false;
}
);
}
/**
* Internal method to test if an item exists.
*
* @param string &$normalizedKey Normalized key which will be checked
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalHasItem(& $normalizedKey)
{
$redis = $this->getRedisResource();
try {
return (bool) $redis->exists($this->namespacePrefix . $normalizedKey);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to store an item.
*
* @param string &$normalizedKey Key in Redis under which value will be saved
* @param mixed &$value Value to store under cache key
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
$options = $this->getOptions();
$ttl = $options->getTtl();
try {
if ($ttl) {
if ($options->getResourceManager()->getMajorVersion($options->getResourceId()) < 2) {
throw new Exception\UnsupportedMethodCallException("To use ttl you need version >= 2.0.0");
}
$success = $redis->setex($this->namespacePrefix . $normalizedKey, $ttl, $this->preSerialize($value));
} else {
$success = $redis->set($this->namespacePrefix . $normalizedKey, $this->preSerialize($value));
}
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
return $success;
}
/**
* Internal method to store multiple items.
*
* @param array &$normalizedKeyValuePairs An array of normalized key/value pairs
*
* @return array Array of not stored keys
* @throws Exception\RuntimeException
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$redis = $this->getRedisResource();
$options = $this->getOptions();
$ttl = $options->getTtl();
$namespacedKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$namespacedKeyValuePairs[$this->namespacePrefix . $normalizedKey] = $this->preSerialize($value);
}
try {
if ($ttl > 0) {
//check if ttl is supported
if ($options->getResourceManager()->getMajorVersion($options->getResourceId()) < 2) {
throw new Exception\UnsupportedMethodCallException("To use ttl you need version >= 2.0.0");
}
//mSet does not allow ttl, so use transaction
$transaction = $redis->multi();
foreach ($namespacedKeyValuePairs as $key => $value) {
$transaction->setex($key, $ttl, $value);
}
$success = $transaction->exec();
} else {
$success = $redis->mSet($namespacedKeyValuePairs);
}
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
if (! $success) {
throw new Exception\RuntimeException($redis->getLastError());
}
return [];
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
$options = $this->getOptions();
$ttl = $options->getTtl();
try {
if ($ttl) {
if ($options->getResourceManager()->getMajorVersion($options->getResourceId()) < 2) {
throw new Exception\UnsupportedMethodCallException("To use ttl you need version >= 2.0.0");
}
/**
* To ensure expected behaviour, we stick with the "setnx" method.
* This means we only set the ttl after the key/value has been successfully set.
*/
$success = $redis->setnx($this->namespacePrefix . $normalizedKey, $this->preSerialize($value));
if ($success) {
$redis->expire($this->namespacePrefix . $normalizedKey, $ttl);
}
} else {
$success = $redis->setnx($this->namespacePrefix . $normalizedKey, $this->preSerialize($value));
}
return $success;
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to touch an item.
*
* @param string &$normalizedKey Key which will be touched
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalTouchItem(& $normalizedKey)
{
$redis = $this->getRedisResource();
try {
$ttl = $this->getOptions()->getTtl();
return (bool) $redis->expire($this->namespacePrefix . $normalizedKey, $ttl);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to remove an item.
*
* @param string &$normalizedKey Key which will be removed
*
* @return bool
* @throws Exception\RuntimeException
*/
protected function internalRemoveItem(& $normalizedKey)
{
$redis = $this->getRedisResource();
try {
return (bool) $redis->delete($this->namespacePrefix . $normalizedKey);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\RuntimeException
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
try {
return $redis->incrBy($this->namespacePrefix . $normalizedKey, $value);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\RuntimeException
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$redis = $this->getRedisResource();
try {
return $redis->decrBy($this->namespacePrefix . $normalizedKey, $value);
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/**
* Flush currently set DB
*
* @return bool
* @throws Exception\RuntimeException
*/
public function flush()
{
$redis = $this->getRedisResource();
try {
return $redis->flushDB();
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
}
/* ClearByNamespaceInterface */
/**
* Remove items of given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
$redis = $this->getRedisResource();
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
$options = $this->getOptions();
$prefix = $namespace . $options->getNamespaceSeparator();
$redis->delete($redis->keys($prefix . '*'));
return true;
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$redis = $this->getRedisResource();
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator() . $prefix;
$redis->delete($redis->keys($prefix.'*'));
return true;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
$redis = $this->getRedisResource();
try {
$info = $redis->info();
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
return $info['used_memory'];
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$this->capabilityMarker = new stdClass();
$options = $this->getOptions();
$resourceMgr = $options->getResourceManager();
$serializer = $resourceMgr->getLibOption($options->getResourceId(), RedisResource::OPT_SERIALIZER);
$redisVersion = $resourceMgr->getMajorVersion($options->getResourceId());
$minTtl = version_compare($redisVersion, '2', '<') ? 0 : 1;
$supportedMetadata = $redisVersion >= 2 ? ['ttl'] : [];
$this->capabilities = new Capabilities(
$this,
$this->capabilityMarker,
[
'supportedDatatypes' => $serializer ? [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => 'array',
'object' => 'object',
'resource' => false,
] : [
'NULL' => 'string',
'boolean' => 'string',
'integer' => 'string',
'double' => 'string',
'string' => true,
'array' => false,
'object' => false,
'resource' => false,
],
'supportedMetadata' => $supportedMetadata,
'minTtl' => $minTtl,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => false,
'maxKeyLength' => 255,
'namespaceIsPrefix' => true,
]
);
}
return $this->capabilities;
}
/**
* {@inheritDoc}
*
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$redis = $this->getRedisResource();
$metadata = [];
try {
$redisVersion = $this->resourceManager->getVersion($this->resourceId);
// redis >= 2.8
// The command 'pttl' returns -2 if the item does not exist
// and -1 if the item has no associated expire
if (version_compare($redisVersion, '2.8', '>=')) {
$pttl = $redis->pttl($this->namespacePrefix . $normalizedKey);
if ($pttl <= -2) {
return false;
}
$metadata['ttl'] = ($pttl == -1) ? null : $pttl / 1000;
// redis >= 2.6, < 2.8
// The command 'pttl' returns -1 if the item does not exist or the item has no associated expire
} elseif (version_compare($redisVersion, '2.6', '>=')) {
$pttl = $redis->pttl($this->namespacePrefix . $normalizedKey);
if ($pttl <= -1) {
if (! $this->internalHasItem($normalizedKey)) {
return false;
}
$metadata['ttl'] = null;
} else {
$metadata['ttl'] = $pttl / 1000;
}
// redis >= 2, < 2.6
// The command 'pttl' is not supported but 'ttl'
// The command 'ttl' returns 0 if the item does not exist same as if the item is going to be expired
// NOTE: In case of ttl=0 we return false because the item is going to be expired in a very near future
// and then doesn't exist any more
} elseif (version_compare($redisVersion, '2', '>=')) {
$ttl = $redis->ttl($this->namespacePrefix . $normalizedKey);
if ($ttl <= -1) {
if (! $this->internalHasItem($normalizedKey)) {
return false;
}
$metadata['ttl'] = null;
} else {
$metadata['ttl'] = $ttl;
}
// redis < 2
// The commands 'pttl' and 'ttl' are not supported
// but item existence have to be checked
} elseif (! $this->internalHasItem($normalizedKey)) {
return false;
}
} catch (RedisResourceException $e) {
throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e);
}
return $metadata;
}
/**
* Pre-Serialize value before putting it to the redis extension
* The reason for this is the buggy extension version < 2.5.7
* which is producing a segfault on storing NULL as long as no serializer was configured.
* @link https://github.com/zendframework/zend-cache/issues/88
*/
protected function preSerialize($value)
{
$options = $this->getOptions();
$resourceMgr = $options->getResourceManager();
$serializer = $resourceMgr->getLibOption($options->getResourceId(), RedisResource::OPT_SERIALIZER);
if ($serializer === null) {
return (string) $value;
}
return $value;
}
}

View File

@@ -0,0 +1,273 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Exception;
class RedisOptions extends AdapterOptions
{
// @codingStandardsIgnoreStart
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id', 'server'];
// @codingStandardsIgnoreEnd
/**
* The namespace separator
* @var string
*/
protected $namespaceSeparator = ':';
/**
* The redis resource manager
*
* @var null|RedisResourceManager
*/
protected $resourceManager;
/**
* The resource id of the resource manager
*
* @var string
*/
protected $resourceId = 'default';
/**
* Set namespace.
*
* The option Redis::OPT_PREFIX will be used as the namespace.
* It can't be longer than 128 characters.
*
* @param string $namespace Prefix for each key stored in redis
* @return \Zend\Cache\Storage\Adapter\RedisOptions
*
* @see AdapterOptions::setNamespace()
* @see RedisOptions::setPrefixKey()
*/
public function setNamespace($namespace)
{
$namespace = (string) $namespace;
if (128 < strlen($namespace)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a prefix key of no longer than 128 characters',
__METHOD__
));
}
return parent::setNamespace($namespace);
}
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return RedisOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
if ($this->namespaceSeparator !== $namespaceSeparator) {
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
}
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Set the redis resource manager to use
*
* @param null|RedisResourceManager $resourceManager
* @return RedisOptions Provides a fluent interface
*/
public function setResourceManager(RedisResourceManager $resourceManager = null)
{
if ($this->resourceManager !== $resourceManager) {
$this->triggerOptionEvent('resource_manager', $resourceManager);
$this->resourceManager = $resourceManager;
}
return $this;
}
/**
* Get the redis resource manager
*
* @return RedisResourceManager
*/
public function getResourceManager()
{
if (! $this->resourceManager) {
$this->resourceManager = new RedisResourceManager();
}
return $this->resourceManager;
}
/**
* Get the redis resource id
*
* @return string
*/
public function getResourceId()
{
return $this->resourceId;
}
/**
* Set the redis resource id
*
* @param string $resourceId
* @return RedisOptions Provides a fluent interface
*/
public function setResourceId($resourceId)
{
$resourceId = (string) $resourceId;
if ($this->resourceId !== $resourceId) {
$this->triggerOptionEvent('resource_id', $resourceId);
$this->resourceId = $resourceId;
}
return $this;
}
/**
* Get the persistent id
*
* @return string
*/
public function getPersistentId()
{
return $this->getResourceManager()->getPersistentId($this->getResourceId());
}
/**
* Set the persistent id
*
* @param string $persistentId
* @return RedisOptions Provides a fluent interface
*/
public function setPersistentId($persistentId)
{
$this->triggerOptionEvent('persistent_id', $persistentId);
$this->getResourceManager()->setPersistentId($this->getResourceId(), $persistentId);
return $this;
}
/**
* Set redis options
*
* @param array $libOptions
* @return RedisOptions Provides a fluent interface
* @link http://github.com/nicolasff/phpredis#setoption
*/
public function setLibOptions(array $libOptions)
{
$this->triggerOptionEvent('lib_option', $libOptions);
$this->getResourceManager()->setLibOptions($this->getResourceId(), $libOptions);
return $this;
}
/**
* Get redis options
*
* @return array
* @link http://github.com/nicolasff/phpredis#setoption
*/
public function getLibOptions()
{
return $this->getResourceManager()->getLibOptions($this->getResourceId());
}
/**
* Set server
*
* Server can be described as follows:
* - URI: /path/to/sock.sock
* - Assoc: array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
* - List: array(<host>[, <port>, [, <timeout>]])
*
* @param string|array $server
*
* @return RedisOptions Provides a fluent interface
*/
public function setServer($server)
{
$this->getResourceManager()->setServer($this->getResourceId(), $server);
return $this;
}
/**
* Get server
*
* @return array array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
*/
public function getServer()
{
return $this->getResourceManager()->getServer($this->getResourceId());
}
/**
* Set resource database number
*
* @param int $database Database number
*
* @return RedisOptions Provides a fluent interface
*/
public function setDatabase($database)
{
$this->getResourceManager()->setDatabase($this->getResourceId(), $database);
return $this;
}
/**
* Get resource database number
*
* @return int Database number
*/
public function getDatabase()
{
return $this->getResourceManager()->getDatabase($this->getResourceId());
}
/**
* Set resource password
*
* @param string $password Password
*
* @return RedisOptions Provides a fluent interface
*/
public function setPassword($password)
{
$this->getResourceManager()->setPassword($this->getResourceId(), $password);
return $this;
}
/**
* Get resource password
*
* @return string
*/
public function getPassword()
{
return $this->getResourceManager()->getPassword($this->getResourceId());
}
}

View File

@@ -0,0 +1,671 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Redis as RedisResource;
use ReflectionClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Stdlib\ArrayUtils;
/**
* This is a resource manager for redis
*/
class RedisResourceManager
{
/**
* Registered resources
*
* @var array
*/
protected $resources = [];
/**
* Check if a resource exists
*
* @param string $id
* @return bool
*/
public function hasResource($id)
{
return isset($this->resources[$id]);
}
/**
* Get redis server version
*
* @param string $resourceId
* @return string
* @throws Exception\RuntimeException
*/
public function getVersion($resourceId)
{
// check resource id and initialize the resource
$this->getResource($resourceId);
return $this->resources[$resourceId]['version'];
}
/**
* Get redis major server version
*
* @param string $resourceId
* @return int
* @throws Exception\RuntimeException
*/
public function getMajorVersion($resourceId)
{
// check resource id and initialize the resource
$this->getResource($resourceId);
return (int) $this->resources[$resourceId]['version'];
}
/**
* Get redis server version
*
* @deprecated 2.2.2 Use getMajorVersion instead
*
* @param string $id
* @return int
* @throws Exception\RuntimeException
*/
public function getMayorVersion($id)
{
return $this->getMajorVersion($id);
}
/**
* Get redis resource database
*
* @param string $id
* @return string
*/
public function getDatabase($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
return $resource['database'];
}
/**
* Get redis resource password
*
* @param string $id
* @return string
*/
public function getPassword($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
return $resource['password'];
}
/**
* Gets a redis resource
*
* @param string $id
* @return RedisResource
* @throws Exception\RuntimeException
*/
public function getResource($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource['resource'] instanceof RedisResource) {
//in case new server was set then connect
if (! $resource['initialized']) {
$this->connect($resource);
}
if (! $resource['version']) {
$info = $resource['resource']->info();
$resource['version'] = $info['redis_version'];
}
return $resource['resource'];
}
$redis = new RedisResource();
$resource['resource'] = $redis;
$this->connect($resource);
$this->normalizeLibOptions($resource['lib_options']);
foreach ($resource['lib_options'] as $k => $v) {
$redis->setOption($k, $v);
}
$info = $redis->info();
$resource['version'] = $info['redis_version'];
$this->resources[$id]['resource'] = $redis;
return $redis;
}
/**
* Get server
* @param string $id
* @throws Exception\RuntimeException
* @return array array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
*/
public function getServer($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
return $resource['server'];
}
/**
* Normalize one server into the following format:
* array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
*
* @param string|array $server
*
* @throws Exception\InvalidArgumentException
*/
protected function normalizeServer(&$server)
{
$host = null;
$port = null;
$timeout = 0;
// convert a single server into an array
if ($server instanceof Traversable) {
$server = ArrayUtils::iteratorToArray($server);
}
if (is_array($server)) {
// array(<host>[, <port>[, <timeout>]])
if (isset($server[0])) {
$host = (string) $server[0];
$port = isset($server[1]) ? (int) $server[1] : $port;
$timeout = isset($server[2]) ? (int) $server[2] : $timeout;
}
// array('host' => <host>[, 'port' => <port>, ['timeout' => <timeout>]])
if (! isset($server[0]) && isset($server['host'])) {
$host = (string) $server['host'];
$port = isset($server['port']) ? (int) $server['port'] : $port;
$timeout = isset($server['timeout']) ? (int) $server['timeout'] : $timeout;
}
} else {
// parse server from URI host{:?port}
$server = trim($server);
if (strpos($server, '/') !== 0) {
//non unix domain socket connection
$server = parse_url($server);
} else {
$server = ['host' => $server];
}
if (! $server) {
throw new Exception\InvalidArgumentException("Invalid server given");
}
$host = $server['host'];
$port = isset($server['port']) ? (int) $server['port'] : $port;
$timeout = isset($server['timeout']) ? (int) $server['timeout'] : $timeout;
}
if (! $host) {
throw new Exception\InvalidArgumentException('Missing required server host');
}
$server = [
'host' => $host,
'port' => $port,
'timeout' => $timeout,
];
}
/**
* Extract password to be used on connection
*
* @param mixed $resource
* @param mixed $serverUri
*
* @return string|null
*/
protected function extractPassword($resource, $serverUri)
{
if (! empty($resource['password'])) {
return $resource['password'];
}
if (! is_string($serverUri)) {
return;
}
// parse server from URI host{:?port}
$server = trim($serverUri);
if (strpos($server, '/') === 0) {
return;
}
//non unix domain socket connection
$server = parse_url($server);
return isset($server['pass']) ? $server['pass'] : null;
}
/**
* Connects to redis server
*
*
* @param array & $resource
*
* @return null
* @throws Exception\RuntimeException
*/
protected function connect(array & $resource)
{
$server = $resource['server'];
$redis = $resource['resource'];
if ($resource['persistent_id'] !== '') {
//connect or reuse persistent connection
$success = $redis->pconnect(
$server['host'],
$server['port'],
$server['timeout'],
$resource['persistent_id']
);
} elseif ($server['port']) {
$success = $redis->connect($server['host'], $server['port'], $server['timeout']);
} elseif ($server['timeout']) {
//connect through unix domain socket
$success = $redis->connect($server['host'], $server['timeout']);
} else {
$success = $redis->connect($server['host']);
}
if (! $success) {
throw new Exception\RuntimeException('Could not establish connection with Redis instance');
}
$resource['initialized'] = true;
if ($resource['password']) {
$redis->auth($resource['password']);
}
$redis->select($resource['database']);
}
/**
* Set a resource
*
* @param string $id
* @param array|Traversable|RedisResource $resource
* @return RedisResourceManager Fluent interface
*/
public function setResource($id, $resource)
{
$id = (string) $id;
//TODO: how to get back redis connection info from resource?
$defaults = [
'persistent_id' => '',
'lib_options' => [],
'server' => [],
'password' => '',
'database' => 0,
'resource' => null,
'initialized' => false,
'version' => 0,
];
if (! $resource instanceof RedisResource) {
if ($resource instanceof Traversable) {
$resource = ArrayUtils::iteratorToArray($resource);
} elseif (! is_array($resource)) {
throw new Exception\InvalidArgumentException(
'Resource must be an instance of an array or Traversable'
);
}
$resource = array_merge($defaults, $resource);
// normalize and validate params
$this->normalizePersistentId($resource['persistent_id']);
// #6495 note: order is important here, as `normalizeServer` applies destructive
// transformations on $resource['server']
$resource['password'] = $this->extractPassword($resource, $resource['server']);
$this->normalizeServer($resource['server']);
} else {
//there are two ways of determining if redis is already initialized
//with connect function:
//1) pinging server
//2) checking undocumented property socket which is available only
//after successful connect
$resource = array_merge(
$defaults,
[
'resource' => $resource,
'initialized' => isset($resource->socket),
]
);
}
$this->resources[$id] = $resource;
return $this;
}
/**
* Remove a resource
*
* @param string $id
* @return RedisResourceManager Fluent interface
*/
public function removeResource($id)
{
unset($this->resources[$id]);
return $this;
}
/**
* Set the persistent id
*
* @param string $id
* @param string $persistentId
* @return RedisResourceManager Fluent interface
* @throws Exception\RuntimeException
*/
public function setPersistentId($id, $persistentId)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'persistent_id' => $persistentId
]);
}
$resource = & $this->resources[$id];
if ($resource['resource'] instanceof RedisResource && $resource['initialized']) {
throw new Exception\RuntimeException(
"Can't change persistent id of resource {$id} after initialization"
);
}
$this->normalizePersistentId($persistentId);
$resource['persistent_id'] = $persistentId;
return $this;
}
/**
* Get the persistent id
*
* @param string $id
* @return string
* @throws Exception\RuntimeException
*/
public function getPersistentId($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
return $resource['persistent_id'];
}
/**
* Normalize the persistent id
*
* @param string $persistentId
*/
protected function normalizePersistentId(& $persistentId)
{
$persistentId = (string) $persistentId;
}
/**
* Set Redis options
*
* @param string $id
* @param array $libOptions
* @return RedisResourceManager Fluent interface
*/
public function setLibOptions($id, array $libOptions)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'lib_options' => $libOptions
]);
}
$resource = & $this->resources[$id];
$resource['lib_options'] = $libOptions;
if (! $resource['resource'] instanceof RedisResource) {
return $this;
}
$this->normalizeLibOptions($libOptions);
$redis = & $resource['resource'];
if (method_exists($redis, 'setOptions')) {
$redis->setOptions($libOptions);
} else {
foreach ($libOptions as $key => $value) {
$redis->setOption($key, $value);
}
}
return $this;
}
/**
* Get Redis options
*
* @param string $id
* @return array
* @throws Exception\RuntimeException
*/
public function getLibOptions($id)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$resource = & $this->resources[$id];
if ($resource['resource'] instanceof RedisResource) {
$libOptions = [];
$reflection = new ReflectionClass('Redis');
$constants = $reflection->getConstants();
foreach ($constants as $constName => $constValue) {
if (strpos($constName, 'OPT_') === 0) {
$libOptions[$constValue] = $resource['resource']->getOption($constValue);
}
}
return $libOptions;
}
return $resource['lib_options'];
}
/**
* Set one Redis option
*
* @param string $id
* @param string|int $key
* @param mixed $value
* @return RedisResourceManager Fluent interface
*/
public function setLibOption($id, $key, $value)
{
return $this->setLibOptions($id, [$key => $value]);
}
/**
* Get one Redis option
*
* @param string $id
* @param string|int $key
* @return mixed
* @throws Exception\RuntimeException
*/
public function getLibOption($id, $key)
{
if (! $this->hasResource($id)) {
throw new Exception\RuntimeException("No resource with id '{$id}'");
}
$this->normalizeLibOptionKey($key);
$resource = & $this->resources[$id];
if ($resource['resource'] instanceof RedisResource) {
return $resource['resource']->getOption($key);
}
return isset($resource['lib_options'][$key]) ? $resource['lib_options'][$key] : null;
}
/**
* Normalize Redis options
*
* @param array|Traversable $libOptions
* @throws Exception\InvalidArgumentException
*/
protected function normalizeLibOptions(& $libOptions)
{
if (! is_array($libOptions) && ! ($libOptions instanceof Traversable)) {
throw new Exception\InvalidArgumentException(
"Lib-Options must be an array or an instance of Traversable"
);
}
$result = [];
foreach ($libOptions as $key => $value) {
$this->normalizeLibOptionKey($key);
$result[$key] = $value;
}
$libOptions = $result;
}
/**
* Convert option name into it's constant value
*
* @param string|int $key
* @throws Exception\InvalidArgumentException
*/
protected function normalizeLibOptionKey(& $key)
{
// convert option name into it's constant value
if (is_string($key)) {
$const = 'Redis::OPT_' . str_replace([' ', '-'], '_', strtoupper($key));
if (! defined($const)) {
throw new Exception\InvalidArgumentException("Unknown redis option '{$key}' ({$const})");
}
$key = constant($const);
} else {
$key = (int) $key;
}
}
/**
* Set server
*
* Server can be described as follows:
* - URI: /path/to/sock.sock
* - Assoc: array('host' => <host>[, 'port' => <port>[, 'timeout' => <timeout>]])
* - List: array(<host>[, <port>, [, <timeout>]])
*
* @param string $id
* @param string|array $server
* @return RedisResourceManager
*/
public function setServer($id, $server)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'server' => $server
]);
}
$this->normalizeServer($server);
$resource = & $this->resources[$id];
$resource['password'] = $this->extractPassword($resource, $server);
if ($resource['resource'] instanceof RedisResource) {
$resourceParams = ['server' => $server];
if (! empty($resource['password'])) {
$resourceParams['password'] = $resource['password'];
}
$this->setResource($id, $resourceParams);
} else {
$resource['server'] = $server;
}
return $this;
}
/**
* Set redis password
*
* @param string $id
* @param string $password
* @return RedisResource
*/
public function setPassword($id, $password)
{
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'password' => $password,
]);
}
$resource = & $this->resources[$id];
$resource['password'] = $password;
$resource['initialized'] = false;
return $this;
}
/**
* Set redis database number
*
* @param string $id
* @param int $database
* @return RedisResourceManager
*/
public function setDatabase($id, $database)
{
$database = (int) $database;
if (! $this->hasResource($id)) {
return $this->setResource($id, [
'database' => $database,
]);
}
$resource = & $this->resources[$id];
if ($resource['resource'] instanceof RedisResource && $resource['initialized']) {
$resource['resource']->select($database);
}
$resource['database'] = $database;
return $this;
}
}

View File

@@ -0,0 +1,545 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use stdClass;
use Zend\Cache\Exception;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\IterableInterface;
use Zend\Session\Container as SessionContainer;
class Session extends AbstractAdapter implements
ClearByPrefixInterface,
FlushableInterface,
IterableInterface
{
/**
* Set options.
*
* @param array|\Traversable|SessionOptions $options
* @return Memory
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof SessionOptions) {
$options = new SessionOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return SessionOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new SessionOptions());
}
return $this->options;
}
/**
* Get the session container
*
* @return SessionContainer
*/
protected function getSessionContainer()
{
$sessionContainer = $this->getOptions()->getSessionContainer();
if (! $sessionContainer) {
throw new Exception\RuntimeException("No session container configured");
}
return $sessionContainer;
}
/* IterableInterface */
/**
* Get the storage iterator
*
* @return KeyListIterator
*/
public function getIterator()
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if ($cntr->offsetExists($ns)) {
$keys = array_keys($cntr->offsetGet($ns));
} else {
$keys = [];
}
return new KeyListIterator($this, $keys);
}
/* FlushableInterface */
/**
* Flush the whole session container
*
* @return bool
*/
public function flush()
{
$this->getSessionContainer()->exchangeArray([]);
return true;
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
return true;
}
$data = $cntr->offsetGet($ns);
foreach ($data as $key => & $item) {
if (strpos($key, $prefix) === 0) {
unset($data[$key]);
}
}
$cntr->offsetSet($ns, $data);
return true;
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
$success = false;
return;
}
$data = $cntr->offsetGet($ns);
$success = array_key_exists($normalizedKey, $data);
if (! $success) {
return;
}
$casToken = $value = $data[$normalizedKey];
return $value;
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
return [];
}
$data = $cntr->offsetGet($ns);
$result = [];
foreach ($normalizedKeys as $normalizedKey) {
if (array_key_exists($normalizedKey, $data)) {
$result[$normalizedKey] = $data[$normalizedKey];
}
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
*/
protected function internalHasItem(& $normalizedKey)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
return false;
}
$data = $cntr->offsetGet($ns);
return array_key_exists($normalizedKey, $data);
}
/**
* Internal method to test multiple items.
*
* @param array $normalizedKeys
* @return array Array of found keys
*/
protected function internalHasItems(array & $normalizedKeys)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
return [];
}
$data = $cntr->offsetGet($ns);
$result = [];
foreach ($normalizedKeys as $normalizedKey) {
if (array_key_exists($normalizedKey, $data)) {
$result[] = $normalizedKey;
}
}
return $result;
}
/**
* Get metadata of an item.
*
* @param string $normalizedKey
* @return array|bool Metadata on success, false on failure
* @throws Exception\ExceptionInterface
*
* @triggers getMetadata.pre(PreEvent)
* @triggers getMetadata.post(PostEvent)
* @triggers getMetadata.exception(ExceptionEvent)
*/
protected function internalGetMetadata(& $normalizedKey)
{
return $this->internalHasItem($normalizedKey) ? [] : false;
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
$data = $cntr->offsetExists($ns) ? $cntr->offsetGet($ns) : [];
$data[$normalizedKey] = $value;
$cntr->offsetSet($ns, $data);
return true;
}
/**
* Internal method to store multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if ($cntr->offsetExists($ns)) {
$data = array_merge($cntr->offsetGet($ns), $normalizedKeyValuePairs);
} else {
$data = $normalizedKeyValuePairs;
}
$cntr->offsetSet($ns, $data);
return [];
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if ($cntr->offsetExists($ns)) {
$data = $cntr->offsetGet($ns);
if (array_key_exists($normalizedKey, $data)) {
return false;
}
$data[$normalizedKey] = $value;
} else {
$data = [$normalizedKey => $value];
}
$cntr->offsetSet($ns, $data);
return true;
}
/**
* Internal method to add multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalAddItems(array & $normalizedKeyValuePairs)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
$result = [];
if ($cntr->offsetExists($ns)) {
$data = $cntr->offsetGet($ns);
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
if (array_key_exists($normalizedKey, $data)) {
$result[] = $normalizedKey;
} else {
$data[$normalizedKey] = $value;
}
}
} else {
$data = $normalizedKeyValuePairs;
}
$cntr->offsetSet($ns, $data);
return $result;
}
/**
* Internal method to replace an existing item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItem(& $normalizedKey, & $value)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
return false;
}
$data = $cntr->offsetGet($ns);
if (! array_key_exists($normalizedKey, $data)) {
return false;
}
$data[$normalizedKey] = $value;
$cntr->offsetSet($ns, $data);
return true;
}
/**
* Internal method to replace multiple existing items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItems(array & $normalizedKeyValuePairs)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
return array_keys($normalizedKeyValuePairs);
}
$data = $cntr->offsetGet($ns);
$result = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
if (! array_key_exists($normalizedKey, $data)) {
$result[] = $normalizedKey;
} else {
$data[$normalizedKey] = $value;
}
}
$cntr->offsetSet($ns, $data);
return $result;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if (! $cntr->offsetExists($ns)) {
return false;
}
$data = $cntr->offsetGet($ns);
if (! array_key_exists($normalizedKey, $data)) {
return false;
}
unset($data[$normalizedKey]);
if (! $data) {
$cntr->offsetUnset($ns);
} else {
$cntr->offsetSet($ns, $data);
}
return true;
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if ($cntr->offsetExists($ns)) {
$data = $cntr->offsetGet($ns);
} else {
$data = [];
}
if (array_key_exists($normalizedKey, $data)) {
$data[$normalizedKey] += $value;
$newValue = $data[$normalizedKey];
} else {
// initial value
$newValue = $value;
$data[$normalizedKey] = $newValue;
}
$cntr->offsetSet($ns, $data);
return $newValue;
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$cntr = $this->getSessionContainer();
$ns = $this->getOptions()->getNamespace();
if ($cntr->offsetExists($ns)) {
$data = $cntr->offsetGet($ns);
} else {
$data = [];
}
if (array_key_exists($normalizedKey, $data)) {
$data[$normalizedKey] -= $value;
$newValue = $data[$normalizedKey];
} else {
// initial value
$newValue = -$value;
$data[$normalizedKey] = $newValue;
}
$cntr->offsetSet($ns, $data);
return $newValue;
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$this->capabilityMarker = new stdClass();
$this->capabilities = new Capabilities(
$this,
$this->capabilityMarker,
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => 'array',
'object' => 'object',
'resource' => false,
],
'supportedMetadata' => [],
'minTtl' => 0,
'maxKeyLength' => 0,
'namespaceIsPrefix' => false,
'namespaceSeparator' => '',
]
);
}
return $this->capabilities;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Session\Container as SessionContainer;
/**
* These are options specific to the APC adapter
*/
class SessionOptions extends AdapterOptions
{
/**
* The session container
*
* @var null|SessionContainer
*/
protected $sessionContainer = null;
/**
* Set the session container
*
* @param null|SessionContainer $sessionContainer
* @return SessionOptions Provides a fluent interface
*/
public function setSessionContainer(SessionContainer $sessionContainer = null)
{
if ($this->sessionContainer != $sessionContainer) {
$this->triggerOptionEvent('session_container', $sessionContainer);
$this->sessionContainer = $sessionContainer;
}
return $this;
}
/**
* Get the session container
*
* @return null|SessionContainer
*/
public function getSessionContainer()
{
return $this->sessionContainer;
}
}

View File

@@ -0,0 +1,532 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use stdClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class WinCache extends AbstractAdapter implements
AvailableSpaceCapableInterface,
FlushableInterface,
TotalSpaceCapableInterface
{
/**
* Constructor
*
* @param array|Traversable|WinCacheOptions $options
* @throws Exception\ExceptionInterface
*/
public function __construct($options = null)
{
if (! extension_loaded('wincache')) {
throw new Exception\ExtensionNotLoadedException("WinCache extension is not loaded");
}
$enabled = ini_get('wincache.ucenabled');
if (PHP_SAPI == 'cli') {
$enabled = $enabled && (bool) ini_get('wincache.enablecli');
}
if (! $enabled) {
throw new Exception\ExtensionNotLoadedException(
"WinCache is disabled - see 'wincache.ucenabled' and 'wincache.enablecli'"
);
}
parent::__construct($options);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|WinCacheOptions $options
* @return WinCache
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof WinCacheOptions) {
$options = new WinCacheOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return WinCacheOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new WinCacheOptions());
}
return $this->options;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
$mem = wincache_ucache_meminfo();
return $mem['memory_total'];
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
$mem = wincache_ucache_meminfo();
return $mem['memory_free'];
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
return wincache_ucache_clear();
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$result = wincache_ucache_get($internalKey, $success);
if ($success) {
$casToken = $result;
} else {
$result = null;
}
return $result;
}
/**
* Internal method to get multiple items.
*
* @param array $normalizedKeys
* @return array Associative array of keys and values
* @throws Exception\ExceptionInterface
*/
protected function internalGetItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return wincache_ucache_get($normalizedKeys);
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$fetch = wincache_ucache_get($internalKeys);
// remove namespace prefix
$prefixL = strlen($prefix);
$result = [];
foreach ($fetch as $internalKey => & $value) {
$result[substr($internalKey, $prefixL)] = & $value;
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
return wincache_ucache_exists($prefix . $normalizedKey);
}
/**
* Get metadata of an item.
*
* @param string $normalizedKey
* @return array|bool Metadata on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$info = wincache_ucache_info(true, $internalKey);
if (isset($info['ucache_entries'][1])) {
$metadata = $info['ucache_entries'][1];
$this->normalizeMetadata($metadata);
return $metadata;
}
return false;
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
if (! wincache_ucache_set($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"wincache_ucache_set('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to store multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalSetItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return wincache_ucache_set($normalizedKeyValuePairs, null, $options->getTtl());
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => & $value) {
$internalKey = $prefix . $normalizedKey;
$internalKeyValuePairs[$internalKey] = & $value;
}
$result = wincache_ucache_set($internalKeyValuePairs, null, $options->getTtl());
// remove key prefic
$prefixL = strlen($prefix);
foreach ($result as & $key) {
$key = substr($key, $prefixL);
}
return $result;
}
/**
* Add an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalAddItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
if (! wincache_ucache_add($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"wincache_ucache_add('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to add multiple items.
*
* @param array $normalizedKeyValuePairs
* @return array Array of not stored keys
* @throws Exception\ExceptionInterface
*/
protected function internalAddItems(array & $normalizedKeyValuePairs)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
return wincache_ucache_add($normalizedKeyValuePairs, null, $options->getTtl());
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeyValuePairs = [];
foreach ($normalizedKeyValuePairs as $normalizedKey => $value) {
$internalKey = $prefix . $normalizedKey;
$internalKeyValuePairs[$internalKey] = $value;
}
$result = wincache_ucache_add($internalKeyValuePairs, null, $options->getTtl());
// remove key prefic
$prefixL = strlen($prefix);
foreach ($result as & $key) {
$key = substr($key, $prefixL);
}
return $result;
}
/**
* Internal method to replace an existing item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalReplaceItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
if (! wincache_ucache_exists($internalKey)) {
return false;
}
$ttl = $options->getTtl();
if (! wincache_ucache_set($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"wincache_ucache_set('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
return wincache_ucache_delete($internalKey);
}
/**
* Internal method to remove multiple items.
*
* @param array $normalizedKeys
* @return array Array of not removed keys
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItems(array & $normalizedKeys)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
if ($namespace === '') {
$result = wincache_ucache_delete($normalizedKeys);
return ($result === false) ? $normalizedKeys : $result;
}
$prefix = $namespace . $options->getNamespaceSeparator();
$internalKeys = [];
foreach ($normalizedKeys as $normalizedKey) {
$internalKeys[] = $prefix . $normalizedKey;
}
$result = wincache_ucache_delete($internalKeys);
if ($result === false) {
return $normalizedKeys;
} elseif ($result) {
// remove key prefix
$prefixL = strlen($prefix);
foreach ($result as & $key) {
$key = substr($key, $prefixL);
}
}
return $result;
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
return wincache_ucache_inc($internalKey, (int) $value);
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
return wincache_ucache_dec($internalKey, (int) $value);
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$marker = new stdClass();
$capabilities = new Capabilities(
$this,
$marker,
[
'supportedDatatypes' => [
'NULL' => true,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => 'object',
'resource' => false,
],
'supportedMetadata' => [
'internal_key', 'ttl', 'hits', 'size'
],
'minTtl' => 1,
'maxTtl' => 0,
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => false,
'namespaceIsPrefix' => true,
'namespaceSeparator' => $this->getOptions()->getNamespaceSeparator(),
]
);
// update namespace separator on change option
$this->getEventManager()->attach('option', function ($event) use ($capabilities, $marker) {
$params = $event->getParams();
if (isset($params['namespace_separator'])) {
$capabilities->setNamespaceSeparator($marker, $params['namespace_separator']);
}
});
$this->capabilities = $capabilities;
$this->capabilityMarker = $marker;
}
return $this->capabilities;
}
/* internal */
/**
* Normalize metadata to work with WinCache
*
* @param array $metadata
* @return void
*/
protected function normalizeMetadata(array & $metadata)
{
$metadata['internal_key'] = $metadata['key_name'];
$metadata['hits'] = $metadata['hitcount'];
$metadata['ttl'] = $metadata['ttl_seconds'];
$metadata['size'] = $metadata['value_size'];
unset(
$metadata['key_name'],
$metadata['hitcount'],
$metadata['ttl_seconds'],
$metadata['value_size']
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
/**
* These are options specific to the APC adapter
*/
class WinCacheOptions extends AdapterOptions
{
/**
* Namespace separator
*
* @var string
*/
protected $namespaceSeparator = ':';
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return WinCacheOptions
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
}

View File

@@ -0,0 +1,530 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use stdClass;
use Traversable;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\Capabilities;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\ClearByPrefixInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\IterableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class XCache extends AbstractAdapter implements
AvailableSpaceCapableInterface,
ClearByNamespaceInterface,
ClearByPrefixInterface,
FlushableInterface,
IterableInterface,
TotalSpaceCapableInterface
{
/**
* Backup HTTP authentication properties of $_SERVER array
*
* @var array
*/
protected $backupAuth = [];
/**
* Total space in bytes
*
* @var int|float
*/
protected $totalSpace;
/**
* Constructor
*
* @param null|array|Traversable|XCacheOptions $options
* @throws Exception\ExceptionInterface
*/
public function __construct($options = null)
{
if (! extension_loaded('xcache')) {
throw new Exception\ExtensionNotLoadedException('Missing ext/xcache');
}
if (PHP_SAPI == 'cli' && version_compare(phpversion('xcache'), '3.1.0') < 0) {
throw new Exception\ExtensionNotLoadedException(
"ext/xcache isn't available on SAPI 'cli' for versions less than 3.1.0"
);
}
if (ini_get('xcache.var_size') <= 0) {
throw new Exception\ExtensionNotLoadedException(
"ext/xcache is disabled - see 'xcache.var_size'"
);
}
parent::__construct($options);
}
/* options */
/**
* Set options.
*
* @param array|Traversable|XCacheOptions $options
* @return XCache
* @see getOptions()
*/
public function setOptions($options)
{
if (! $options instanceof XCacheOptions) {
$options = new XCacheOptions($options);
}
return parent::setOptions($options);
}
/**
* Get options.
*
* @return XCacheOptions
* @see setOptions()
*/
public function getOptions()
{
if (! $this->options) {
$this->setOptions(new XCacheOptions());
}
return $this->options;
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int|float
*/
public function getTotalSpace()
{
if ($this->totalSpace === null) {
$this->totalSpace = 0;
$this->initAdminAuth();
$cnt = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $cnt; $i++) {
$info = xcache_info(XC_TYPE_VAR, $i);
$this->totalSpace += $info['size'];
}
$this->resetAdminAuth();
}
return $this->totalSpace;
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace()
{
$availableSpace = 0;
$this->initAdminAuth();
$cnt = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $cnt; $i++) {
$info = xcache_info(XC_TYPE_VAR, $i);
$availableSpace += $info['avail'];
}
$this->resetAdminAuth();
return $availableSpace;
}
/* ClearByNamespaceInterface */
/**
* Remove items by given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
$options = $this->getOptions();
$prefix = $namespace . $options->getNamespaceSeparator();
xcache_unset_by_prefix($prefix);
return true;
}
/* ClearByPrefixInterface */
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix)
{
$prefix = (string) $prefix;
if ($prefix === '') {
throw new Exception\InvalidArgumentException('No prefix given');
}
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator() . $prefix;
xcache_unset_by_prefix($prefix);
return true;
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
$this->initAdminAuth();
$cnt = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $cnt; $i++) {
xcache_clear_cache(XC_TYPE_VAR, $i);
}
$this->resetAdminAuth();
return true;
}
/* IterableInterface */
/**
* Get the storage iterator
*
* @return KeyListIterator
*/
public function getIterator()
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$keys = [];
$this->initAdminAuth();
if ($namespace === '') {
$cnt = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $cnt; $i++) {
$list = xcache_list(XC_TYPE_VAR, $i);
foreach ($list['cache_list'] as & $item) {
$keys[] = $item['name'];
}
}
} else {
$prefix = $namespace . $options->getNamespaceSeparator();
$prefixL = strlen($prefix);
$cnt = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $cnt; $i++) {
$list = xcache_list(XC_TYPE_VAR, $i);
foreach ($list['cache_list'] as & $item) {
$keys[] = substr($item['name'], $prefixL);
}
}
}
$this->resetAdminAuth();
return new KeyListIterator($this, $keys);
}
/* reading */
/**
* Internal method to get an item.
*
* @param string $normalizedKey
* @param bool $success
* @param mixed $casToken
* @return mixed Data on success, null on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$result = xcache_get($internalKey);
$success = ($result !== null);
if ($success) {
$casToken = $result;
}
return $result;
}
/**
* Internal method to test if an item exists.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalHasItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
return xcache_isset($prefix . $normalizedKey);
}
/**
* Get metadata of an item.
*
* @param string $normalizedKey
* @return array|bool Metadata on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalGetMetadata(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
if (xcache_isset($internalKey)) {
$this->initAdminAuth();
$cnt = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $cnt; $i++) {
$list = xcache_list(XC_TYPE_VAR, $i);
foreach ($list['cache_list'] as & $metadata) {
if ($metadata['name'] === $internalKey) {
$this->normalizeMetadata($metadata);
return $metadata;
}
}
}
$this->resetAdminAuth();
}
return false;
}
/* writing */
/**
* Internal method to store an item.
*
* @param string $normalizedKey
* @param mixed $value
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalSetItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
if (is_object($value) || is_resource($value)) {
throw new Exception\InvalidArgumentException(sprintf(
"Cannot store data of type %s",
gettype($value)
));
}
if (! xcache_set($internalKey, $value, $ttl)) {
$type = is_object($value) ? get_class($value) : gettype($value);
throw new Exception\RuntimeException(
"xcache_set('{$internalKey}', <{$type}>, {$ttl}) failed"
);
}
return true;
}
/**
* Internal method to remove an item.
*
* @param string $normalizedKey
* @return bool
* @throws Exception\ExceptionInterface
*/
protected function internalRemoveItem(& $normalizedKey)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
return xcache_unset($internalKey);
}
/**
* Internal method to increment an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalIncrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
$value = (int) $value;
return xcache_inc($internalKey, $value, $ttl);
}
/**
* Internal method to decrement an item.
*
* @param string $normalizedKey
* @param int $value
* @return int|bool The new value on success, false on failure
* @throws Exception\ExceptionInterface
*/
protected function internalDecrementItem(& $normalizedKey, & $value)
{
$options = $this->getOptions();
$namespace = $options->getNamespace();
$prefix = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
$internalKey = $prefix . $normalizedKey;
$ttl = $options->getTtl();
$value = (int) $value;
return xcache_dec($internalKey, $value, $ttl);
}
/* status */
/**
* Internal method to get capabilities of this adapter
*
* @return Capabilities
*/
protected function internalGetCapabilities()
{
if ($this->capabilities === null) {
$marker = new stdClass();
$capabilities = new Capabilities(
$this,
$marker,
[
'supportedDatatypes' => [
'NULL' => false,
'boolean' => true,
'integer' => true,
'double' => true,
'string' => true,
'array' => true,
'object' => false,
'resource' => false,
],
'supportedMetadata' => [
'internal_key',
'size', 'refcount', 'hits',
'ctime', 'atime', 'hvalue',
],
'minTtl' => 1,
'maxTtl' => (int)ini_get('xcache.var_maxttl'),
'staticTtl' => true,
'ttlPrecision' => 1,
'useRequestTime' => true,
'maxKeyLength' => 5182,
'namespaceIsPrefix' => true,
'namespaceSeparator' => $this->getOptions()->getNamespaceSeparator(),
]
);
// update namespace separator on change option
$this->getEventManager()->attach('option', function ($event) use ($capabilities, $marker) {
$params = $event->getParams();
if (isset($params['namespace_separator'])) {
$capabilities->setNamespaceSeparator($marker, $params['namespace_separator']);
}
});
$this->capabilities = $capabilities;
$this->capabilityMarker = $marker;
}
return $this->capabilities;
}
/* internal */
/**
* Init authentication before calling admin functions
*
* @return void
*/
protected function initAdminAuth()
{
$options = $this->getOptions();
if ($options->getAdminAuth()) {
$adminUser = $options->getAdminUser();
$adminPass = $options->getAdminPass();
// backup HTTP authentication properties
if (isset($_SERVER['PHP_AUTH_USER'])) {
$this->backupAuth['PHP_AUTH_USER'] = $_SERVER['PHP_AUTH_USER'];
}
if (isset($_SERVER['PHP_AUTH_PW'])) {
$this->backupAuth['PHP_AUTH_PW'] = $_SERVER['PHP_AUTH_PW'];
}
// set authentication
$_SERVER['PHP_AUTH_USER'] = $adminUser;
$_SERVER['PHP_AUTH_PW'] = $adminPass;
}
}
/**
* Reset authentication after calling admin functions
*
* @return void
*/
protected function resetAdminAuth()
{
unset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
$_SERVER = $this->backupAuth + $_SERVER;
$this->backupAuth = [];
}
/**
* Normalize metadata to work with XCache
*
* @param array $metadata
*/
protected function normalizeMetadata(array & $metadata)
{
$metadata['internal_key'] = &$metadata['name'];
unset($metadata['name']);
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
/**
* These are options specific to the XCache adapter
*/
class XCacheOptions extends AdapterOptions
{
/**
* Namespace separator
*
* @var string
*/
protected $namespaceSeparator = ':';
/**
* Handle admin authentication
*
* @var bool
*/
protected $adminAuth = false;
/**
* Username to call admin functions
*
* @var null|string
*/
protected $adminUser;
/**
* Password to call admin functions
*
* @var null|string
*/
protected $adminPass;
/**
* Set namespace separator
*
* @param string $namespaceSeparator
* @return XCacheOptions Provides a fluent interface
*/
public function setNamespaceSeparator($namespaceSeparator)
{
$namespaceSeparator = (string) $namespaceSeparator;
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
return $this;
}
/**
* Get namespace separator
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Set username to call admin functions
*
* @param null|string $adminUser
* @return XCacheOptions Provides a fluent interface
*/
public function setAdminUser($adminUser)
{
$adminUser = ($adminUser === null) ? null : (string) $adminUser;
if ($this->adminUser !== $adminUser) {
$this->triggerOptionEvent('admin_user', $adminUser);
$this->adminUser = $adminUser;
}
return $this;
}
/**
* Get username to call admin functions
*
* @return string
*/
public function getAdminUser()
{
return $this->adminUser;
}
/**
* Enable/Disable admin authentication handling
*
* @param bool $adminAuth
* @return XCacheOptions Provides a fluent interface
*/
public function setAdminAuth($adminAuth)
{
$adminAuth = (bool) $adminAuth;
if ($this->adminAuth !== $adminAuth) {
$this->triggerOptionEvent('admin_auth', $adminAuth);
$this->adminAuth = $adminAuth;
}
return $this;
}
/**
* Get admin authentication enabled
*
* @return bool
*/
public function getAdminAuth()
{
return $this->adminAuth;
}
/**
* Set password to call admin functions
*
* @param null|string $adminPass
* @return XCacheOptions Provides a fluent interface
*/
public function setAdminPass($adminPass)
{
$adminPass = ($adminPass === null) ? null : (string) $adminPass;
if ($this->adminPass !== $adminPass) {
$this->triggerOptionEvent('admin_pass', $adminPass);
$this->adminPass = $adminPass;
}
return $this;
}
/**
* Get password to call admin functions
*
* @return string
*/
public function getAdminPass()
{
return $this->adminPass;
}
}

View File

@@ -0,0 +1,186 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Exception;
use Zend\Cache\Storage\AvailableSpaceCapableInterface;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
use Zend\Stdlib\ErrorHandler;
class ZendServerDisk extends AbstractZendServer implements
AvailableSpaceCapableInterface,
ClearByNamespaceInterface,
FlushableInterface,
TotalSpaceCapableInterface
{
/**
* Buffered total space in bytes
*
* @var null|int|float
*/
protected $totalSpace;
/**
* Constructor
*
* @param null|array|\Traversable|AdapterOptions $options
* @throws Exception\ExtensionNotLoadedException
*/
public function __construct($options = [])
{
if (! function_exists('zend_disk_cache_store')) {
throw new Exception\ExtensionNotLoadedException("Missing 'zend_disk_cache_*' functions");
} elseif (PHP_SAPI == 'cli') {
throw new Exception\ExtensionNotLoadedException("Zend server data cache isn't available on cli");
}
parent::__construct($options);
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
return zend_disk_cache_clear();
}
/* ClearByNamespaceInterface */
/**
* Remove items of given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
return zend_disk_cache_clear($namespace);
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @throws Exception\RuntimeException
* @return int|float
*/
public function getTotalSpace()
{
if ($this->totalSpace === null) {
$path = ini_get('zend_datacache.disk.save_path');
ErrorHandler::start();
$total = disk_total_space($path);
$error = ErrorHandler::stop();
if ($total === false) {
throw new Exception\RuntimeException("Can't detect total space of '{$path}'", 0, $error);
}
$this->totalSpace = $total;
}
return $this->totalSpace;
}
/* AvailableSpaceCapableInterface */
/**
* Get available space in bytes
*
* @throws Exception\RuntimeException
* @return int|float
*/
public function getAvailableSpace()
{
$path = ini_get('zend_datacache.disk.save_path');
ErrorHandler::start();
$avail = disk_free_space($path);
$error = ErrorHandler::stop();
if ($avail === false) {
throw new Exception\RuntimeException("Can't detect free space of '{$path}'", 0, $error);
}
return $avail;
}
/* internal */
/**
* Store data into Zend Data Disk Cache
*
* @param string $internalKey
* @param mixed $value
* @param int $ttl
* @return void
* @throws Exception\RuntimeException
*/
protected function zdcStore($internalKey, $value, $ttl)
{
if (! zend_disk_cache_store($internalKey, $value, $ttl)) {
$valueType = gettype($value);
throw new Exception\RuntimeException(
"zend_disk_cache_store($internalKey, <{$valueType}>, {$ttl}) failed"
);
}
}
/**
* Fetch a single item from Zend Data Disk Cache
*
* @param string $internalKey
* @return mixed The stored value or FALSE if item wasn't found
* @throws Exception\RuntimeException
*/
protected function zdcFetch($internalKey)
{
return zend_disk_cache_fetch((string) $internalKey);
}
/**
* Fetch multiple items from Zend Data Disk Cache
*
* @param array $internalKeys
* @return array All found items
* @throws Exception\RuntimeException
*/
protected function zdcFetchMulti(array $internalKeys)
{
$items = zend_disk_cache_fetch($internalKeys);
if ($items === false) {
throw new Exception\RuntimeException("zend_disk_cache_fetch(<array>) failed");
}
return $items;
}
/**
* Delete data from Zend Data Disk Cache
*
* @param string $internalKey
* @return bool
* @throws Exception\RuntimeException
*/
protected function zdcDelete($internalKey)
{
return zend_disk_cache_delete($internalKey);
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Adapter;
use Zend\Cache\Exception;
use Zend\Cache\Storage\ClearByNamespaceInterface;
use Zend\Cache\Storage\FlushableInterface;
use Zend\Cache\Storage\TotalSpaceCapableInterface;
class ZendServerShm extends AbstractZendServer implements
ClearByNamespaceInterface,
FlushableInterface,
TotalSpaceCapableInterface
{
/**
* Constructor
*
* @param null|array|\Traversable|AdapterOptions $options
* @throws Exception\ExtensionNotLoadedException
*/
public function __construct($options = [])
{
if (! function_exists('zend_shm_cache_store')) {
throw new Exception\ExtensionNotLoadedException("Missing 'zend_shm_cache_*' functions");
} elseif (PHP_SAPI == 'cli') {
throw new Exception\ExtensionNotLoadedException("Zend server data cache isn't available on cli");
}
parent::__construct($options);
}
/* FlushableInterface */
/**
* Flush the whole storage
*
* @return bool
*/
public function flush()
{
return zend_shm_cache_clear();
}
/* ClearByNamespaceInterface */
/**
* Remove items of given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace)
{
$namespace = (string) $namespace;
if ($namespace === '') {
throw new Exception\InvalidArgumentException('No namespace given');
}
return zend_shm_cache_clear($namespace);
}
/* TotalSpaceCapableInterface */
/**
* Get total space in bytes
*
* @return int
*/
public function getTotalSpace()
{
return (int) ini_get('zend_datacache.shm.memory_cache_size') * 1048576;
}
/* internal */
/**
* Store data into Zend Data SHM Cache
*
* @param string $internalKey
* @param mixed $value
* @param int $ttl
* @return void
* @throws Exception\RuntimeException
*/
protected function zdcStore($internalKey, $value, $ttl)
{
if (! zend_shm_cache_store($internalKey, $value, $ttl)) {
$valueType = gettype($value);
throw new Exception\RuntimeException(
"zend_shm_cache_store($internalKey, <{$valueType}>, {$ttl}) failed"
);
}
}
/**
* Fetch a single item from Zend Data SHM Cache
*
* @param string $internalKey
* @return mixed The stored value or FALSE if item wasn't found
* @throws Exception\RuntimeException
*/
protected function zdcFetch($internalKey)
{
return zend_shm_cache_fetch((string) $internalKey);
}
/**
* Fetch multiple items from Zend Data SHM Cache
*
* @param array $internalKeys
* @return array All found items
* @throws Exception\RuntimeException
*/
protected function zdcFetchMulti(array $internalKeys)
{
$items = zend_shm_cache_fetch($internalKeys);
if ($items === false) {
throw new Exception\RuntimeException("zend_shm_cache_fetch(<array>) failed");
}
return $items;
}
/**
* Delete data from Zend Data SHM Cache
*
* @param string $internalKey
* @return bool
* @throws Exception\RuntimeException
*/
protected function zdcDelete($internalKey)
{
return zend_shm_cache_delete($internalKey);
}
}

View File

@@ -0,0 +1,177 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
use Zend\Cache\Exception\RuntimeException;
use Zend\ServiceManager\AbstractPluginManager;
use Zend\ServiceManager\Exception\InvalidServiceException;
use Zend\ServiceManager\Factory\InvokableFactory;
/**
* Plugin manager implementation for cache storage adapters
*
* Enforces that adapters retrieved are instances of
* StorageInterface. Additionally, it registers a number of default
* adapters available.
*/
class AdapterPluginManager extends AbstractPluginManager
{
protected $aliases = [
'apc' => Adapter\Apc::class,
'Apc' => Adapter\Apc::class,
'APC' => Adapter\Apc::class,
'apcu' => Adapter\Apcu::class,
'ApcU' => Adapter\Apcu::class,
'Apcu' => Adapter\Apcu::class,
'APCu' => Adapter\Apcu::class,
'black_hole' => Adapter\BlackHole::class,
'blackhole' => Adapter\BlackHole::class,
'blackHole' => Adapter\BlackHole::class,
'BlackHole' => Adapter\BlackHole::class,
'dba' => Adapter\Dba::class,
'Dba' => Adapter\Dba::class,
'DBA' => Adapter\Dba::class,
'ext_mongo_db' => Adapter\ExtMongoDb::class,
'extmongodb' => Adapter\ExtMongoDb::class,
'ExtMongoDb' => Adapter\ExtMongoDb::class,
'ExtMongoDB' => Adapter\ExtMongoDb::class,
'extMongoDb' => Adapter\ExtMongoDb::class,
'extMongoDB' => Adapter\ExtMongoDb::class,
'filesystem' => Adapter\Filesystem::class,
'Filesystem' => Adapter\Filesystem::class,
'memcache' => Adapter\Memcache::class,
'Memcache' => Adapter\Memcache::class,
'memcached' => Adapter\Memcached::class,
'Memcached' => Adapter\Memcached::class,
'memory' => Adapter\Memory::class,
'Memory' => Adapter\Memory::class,
'mongo_db' => Adapter\MongoDb::class,
'mongodb' => Adapter\MongoDb::class,
'MongoDb' => Adapter\MongoDb::class,
'MongoDB' => Adapter\MongoDb::class,
'mongoDb' => Adapter\MongoDb::class,
'mongoDB' => Adapter\MongoDb::class,
'redis' => Adapter\Redis::class,
'Redis' => Adapter\Redis::class,
'session' => Adapter\Session::class,
'Session' => Adapter\Session::class,
'xcache' => Adapter\XCache::class,
'xCache' => Adapter\XCache::class,
'Xcache' => Adapter\XCache::class,
'XCache' => Adapter\XCache::class,
'win_cache' => Adapter\WinCache::class,
'wincache' => Adapter\WinCache::class,
'winCache' => Adapter\WinCache::class,
'WinCache' => Adapter\WinCache::class,
'zend_server_disk' => Adapter\ZendServerDisk::class,
'zendserverdisk' => Adapter\ZendServerDisk::class,
'zendServerDisk' => Adapter\ZendServerDisk::class,
'ZendServerDisk' => Adapter\ZendServerDisk::class,
'zend_server_shm' => Adapter\ZendServerShm::class,
'zendservershm' => Adapter\ZendServerShm::class,
'zendServerShm' => Adapter\ZendServerShm::class,
'zendServerSHM' => Adapter\ZendServerShm::class,
'ZendServerShm' => Adapter\ZendServerShm::class,
'ZendServerSHM' => Adapter\ZendServerShm::class,
];
protected $factories = [
Adapter\Apc::class => InvokableFactory::class,
Adapter\Apcu::class => InvokableFactory::class,
Adapter\BlackHole::class => InvokableFactory::class,
Adapter\Dba::class => InvokableFactory::class,
Adapter\ExtMongoDb::class => InvokableFactory::class,
Adapter\Filesystem::class => InvokableFactory::class,
Adapter\Memcache::class => InvokableFactory::class,
Adapter\Memcached::class => InvokableFactory::class,
Adapter\Memory::class => InvokableFactory::class,
Adapter\MongoDb::class => InvokableFactory::class,
Adapter\Redis::class => InvokableFactory::class,
Adapter\Session::class => InvokableFactory::class,
Adapter\WinCache::class => InvokableFactory::class,
Adapter\XCache::class => InvokableFactory::class,
Adapter\ZendServerDisk::class => InvokableFactory::class,
Adapter\ZendServerShm::class => InvokableFactory::class,
// v2 normalized FQCNs
'zendcachestorageadapterapc' => InvokableFactory::class,
'zendcachestorageadapterapcu' => InvokableFactory::class,
'zendcachestorageadapterblackhole' => InvokableFactory::class,
'zendcachestorageadapterdba' => InvokableFactory::class,
'zendcachestorageadapterextmongodb' => InvokableFactory::class,
'zendcachestorageadapterfilesystem' => InvokableFactory::class,
'zendcachestorageadaptermemcache' => InvokableFactory::class,
'zendcachestorageadaptermemcached' => InvokableFactory::class,
'zendcachestorageadaptermemory' => InvokableFactory::class,
'zendcachestorageadaptermongodb' => InvokableFactory::class,
'zendcachestorageadapterredis' => InvokableFactory::class,
'zendcachestorageadaptersession' => InvokableFactory::class,
'zendcachestorageadapterwincache' => InvokableFactory::class,
'zendcachestorageadapterxcache' => InvokableFactory::class,
'zendcachestorageadapterzendserverdisk' => InvokableFactory::class,
'zendcachestorageadapterzendservershm' => InvokableFactory::class,
];
/**
* Do not share by default (v3)
*
* @var array
*/
protected $sharedByDefault = false;
/**
* Don't share by default (v2)
*
* @var boolean
*/
protected $shareByDefault = false;
/**
* @var string
*/
protected $instanceOf = StorageInterface::class;
/**
* Validate the plugin is of the expected type (v3).
*
* Validates against `$instanceOf`.
*
* @param mixed $instance
* @throws InvalidServiceException
*/
public function validate($instance)
{
if (! $instance instanceof $this->instanceOf) {
throw new InvalidServiceException(sprintf(
'%s can only create instances of %s; %s is invalid',
get_class($this),
$this->instanceOf,
(is_object($instance) ? get_class($instance) : gettype($instance))
));
}
}
/**
* Validate the plugin is of the expected type (v2).
*
* Proxies to `validate()`.
*
* @param mixed $instance
* @throws InvalidServiceException
*/
public function validatePlugin($instance)
{
try {
$this->validate($instance);
} catch (InvalidServiceException $e) {
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
}
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
interface AvailableSpaceCapableInterface
{
/**
* Get available space in bytes
*
* @return int|float
*/
public function getAvailableSpace();
}

View File

@@ -0,0 +1,581 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
use ArrayObject;
use stdClass;
use Zend\Cache\Exception;
use Zend\EventManager\EventsCapableInterface;
class Capabilities
{
/**
* The storage instance
*
* @var StorageInterface
*/
protected $storage;
/**
* A marker to set/change capabilities
*
* @var stdClass
*/
protected $marker;
/**
* Base capabilities
*
* @var null|Capabilities
*/
protected $baseCapabilities;
/**
* "lock-on-expire" support in seconds.
*
* 0 = Expired items will never be retrieved
* >0 = Time in seconds an expired item could be retrieved
* -1 = Expired items could be retrieved forever
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $lockOnExpire;
/**
* Max. key length
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $maxKeyLength;
/**
* Min. TTL (0 means items never expire)
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $minTtl;
/**
* Max. TTL (0 means infinite)
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $maxTtl;
/**
* Namespace is prefix
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $namespaceIsPrefix;
/**
* Namespace separator
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|string
*/
protected $namespaceSeparator;
/**
* Static ttl
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $staticTtl;
/**
* Supported datatypes
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|array
*/
protected $supportedDatatypes;
/**
* Supported metadata
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|array
*/
protected $supportedMetadata;
/**
* TTL precision
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $ttlPrecision;
/**
* Use request time
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $useRequestTime;
/**
* Constructor
*
* @param StorageInterface $storage
* @param stdClass $marker
* @param array $capabilities
* @param null|Capabilities $baseCapabilities
*/
public function __construct(
StorageInterface $storage,
stdClass $marker,
array $capabilities = [],
Capabilities $baseCapabilities = null
) {
$this->storage = $storage;
$this->marker = $marker;
$this->baseCapabilities = $baseCapabilities;
foreach ($capabilities as $name => $value) {
$this->setCapability($marker, $name, $value);
}
}
/**
* Get the storage adapter
*
* @return StorageInterface
*/
public function getAdapter()
{
return $this->storage;
}
/**
* Get supported datatypes
*
* @return array
*/
public function getSupportedDatatypes()
{
return $this->getCapability('supportedDatatypes', [
'NULL' => false,
'boolean' => false,
'integer' => false,
'double' => false,
'string' => true,
'array' => false,
'object' => false,
'resource' => false,
]);
}
/**
* Set supported datatypes
*
* @param stdClass $marker
* @param array $datatypes
* @throws Exception\InvalidArgumentException
* @return Capabilities Fluent interface
*/
public function setSupportedDatatypes(stdClass $marker, array $datatypes)
{
$allTypes = [
'array',
'boolean',
'double',
'integer',
'NULL',
'object',
'resource',
'string',
];
// check/normalize datatype values
foreach ($datatypes as $type => &$toType) {
if (! in_array($type, $allTypes)) {
throw new Exception\InvalidArgumentException("Unknown datatype '{$type}'");
}
if (is_string($toType)) {
$toType = strtolower($toType);
if (! in_array($toType, $allTypes)) {
throw new Exception\InvalidArgumentException("Unknown datatype '{$toType}'");
}
} else {
$toType = (bool) $toType;
}
}
// add missing datatypes as not supported
$missingTypes = array_diff($allTypes, array_keys($datatypes));
foreach ($missingTypes as $type) {
$datatypes[$type] = false;
}
return $this->setCapability($marker, 'supportedDatatypes', $datatypes);
}
/**
* Get supported metadata
*
* @return array
*/
public function getSupportedMetadata()
{
return $this->getCapability('supportedMetadata', []);
}
/**
* Set supported metadata
*
* @param stdClass $marker
* @param string[] $metadata
* @throws Exception\InvalidArgumentException
* @return Capabilities Fluent interface
*/
public function setSupportedMetadata(stdClass $marker, array $metadata)
{
foreach ($metadata as $name) {
if (! is_string($name)) {
throw new Exception\InvalidArgumentException('$metadata must be an array of strings');
}
}
return $this->setCapability($marker, 'supportedMetadata', $metadata);
}
/**
* Get minimum supported time-to-live
*
* @return int 0 means items never expire
*/
public function getMinTtl()
{
return $this->getCapability('minTtl', 0);
}
/**
* Set minimum supported time-to-live
*
* @param stdClass $marker
* @param int $minTtl
* @throws Exception\InvalidArgumentException
* @return Capabilities Fluent interface
*/
public function setMinTtl(stdClass $marker, $minTtl)
{
$minTtl = (int) $minTtl;
if ($minTtl < 0) {
throw new Exception\InvalidArgumentException('$minTtl must be greater or equal 0');
}
return $this->setCapability($marker, 'minTtl', $minTtl);
}
/**
* Get maximum supported time-to-live
*
* @return int 0 means infinite
*/
public function getMaxTtl()
{
return $this->getCapability('maxTtl', 0);
}
/**
* Set maximum supported time-to-live
*
* @param stdClass $marker
* @param int $maxTtl
* @throws Exception\InvalidArgumentException
* @return Capabilities Fluent interface
*/
public function setMaxTtl(stdClass $marker, $maxTtl)
{
$maxTtl = (int) $maxTtl;
if ($maxTtl < 0) {
throw new Exception\InvalidArgumentException('$maxTtl must be greater or equal 0');
}
return $this->setCapability($marker, 'maxTtl', $maxTtl);
}
/**
* Is the time-to-live handled static (on write)
* or dynamic (on read)
*
* @return bool
*/
public function getStaticTtl()
{
return $this->getCapability('staticTtl', false);
}
/**
* Set if the time-to-live handled static (on write) or dynamic (on read)
*
* @param stdClass $marker
* @param bool $flag
* @return Capabilities Fluent interface
*/
public function setStaticTtl(stdClass $marker, $flag)
{
return $this->setCapability($marker, 'staticTtl', (bool) $flag);
}
/**
* Get time-to-live precision
*
* @return float
*/
public function getTtlPrecision()
{
return $this->getCapability('ttlPrecision', 1);
}
/**
* Set time-to-live precision
*
* @param stdClass $marker
* @param float $ttlPrecision
* @throws Exception\InvalidArgumentException
* @return Capabilities Fluent interface
*/
public function setTtlPrecision(stdClass $marker, $ttlPrecision)
{
$ttlPrecision = (float) $ttlPrecision;
if ($ttlPrecision <= 0) {
throw new Exception\InvalidArgumentException('$ttlPrecision must be greater than 0');
}
return $this->setCapability($marker, 'ttlPrecision', $ttlPrecision);
}
/**
* Get use request time
*
* @return bool
*/
public function getUseRequestTime()
{
return $this->getCapability('useRequestTime', false);
}
/**
* Set use request time
*
* @param stdClass $marker
* @param bool $flag
* @return Capabilities Fluent interface
*/
public function setUseRequestTime(stdClass $marker, $flag)
{
return $this->setCapability($marker, 'useRequestTime', (bool) $flag);
}
/**
* Get if expired items are readable
*
* @return bool
* @deprecated This capability has been deprecated and will be removed in the future.
* Please use getStaticTtl() instead
*/
public function getExpiredRead()
{
trigger_error(
'This capability has been deprecated and will be removed in the future. Please use static_ttl instead',
E_USER_DEPRECATED
);
return ! $this->getCapability('staticTtl', true);
}
/**
* Set if expired items are readable
*
* @param stdClass $marker
* @param bool $flag
* @return Capabilities Fluent interface
* @deprecated This capability has been deprecated and will be removed in the future.
* Please use setStaticTtl() instead
*/
public function setExpiredRead(stdClass $marker, $flag)
{
trigger_error(
'This capability has been deprecated and will be removed in the future. Please use static_ttl instead',
E_USER_DEPRECATED
);
return $this->setCapability($marker, 'staticTtl', (bool) $flag);
}
/**
* Get "lock-on-expire" support in seconds.
*
* @return int 0 = Expired items will never be retrieved
* >0 = Time in seconds an expired item could be retrieved
* -1 = Expired items could be retrieved forever
*/
public function getLockOnExpire()
{
return $this->getCapability('lockOnExpire', 0);
}
/**
* Set "lock-on-expire" support in seconds.
*
* @param stdClass $marker
* @param int $timeout
* @return Capabilities Fluent interface
*/
public function setLockOnExpire(stdClass $marker, $timeout)
{
return $this->setCapability($marker, 'lockOnExpire', (int) $timeout);
}
/**
* Get maximum key length
*
* @return int -1 means unknown, 0 means infinite
*/
public function getMaxKeyLength()
{
return $this->getCapability('maxKeyLength', -1);
}
/**
* Set maximum key length
*
* @param stdClass $marker
* @param int $maxKeyLength
* @throws Exception\InvalidArgumentException
* @return Capabilities Fluent interface
*/
public function setMaxKeyLength(stdClass $marker, $maxKeyLength)
{
$maxKeyLength = (int) $maxKeyLength;
if ($maxKeyLength < -1) {
throw new Exception\InvalidArgumentException('$maxKeyLength must be greater or equal than -1');
}
return $this->setCapability($marker, 'maxKeyLength', $maxKeyLength);
}
/**
* Get if namespace support is implemented as prefix
*
* @return bool
*/
public function getNamespaceIsPrefix()
{
return $this->getCapability('namespaceIsPrefix', true);
}
/**
* Set if namespace support is implemented as prefix
*
* @param stdClass $marker
* @param bool $flag
* @return Capabilities Fluent interface
*/
public function setNamespaceIsPrefix(stdClass $marker, $flag)
{
return $this->setCapability($marker, 'namespaceIsPrefix', (bool) $flag);
}
/**
* Get namespace separator if namespace is implemented as prefix
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->getCapability('namespaceSeparator', '');
}
/**
* Set the namespace separator if namespace is implemented as prefix
*
* @param stdClass $marker
* @param string $separator
* @return Capabilities Fluent interface
*/
public function setNamespaceSeparator(stdClass $marker, $separator)
{
return $this->setCapability($marker, 'namespaceSeparator', (string) $separator);
}
/**
* Get a capability
*
* @param string $property
* @param mixed $default
* @return mixed
*/
protected function getCapability($property, $default = null)
{
if ($this->$property !== null) {
return $this->$property;
} elseif ($this->baseCapabilities) {
$getMethod = 'get' . $property;
return $this->baseCapabilities->$getMethod();
}
return $default;
}
/**
* Change a capability
*
* @param stdClass $marker
* @param string $property
* @param mixed $value
* @return Capabilities Fluent interface
* @throws Exception\InvalidArgumentException
*/
protected function setCapability(stdClass $marker, $property, $value)
{
if ($this->marker !== $marker) {
throw new Exception\InvalidArgumentException('Invalid marker');
}
if ($this->$property !== $value) {
$this->$property = $value;
// trigger event
if ($this->storage instanceof EventsCapableInterface) {
$this->storage->getEventManager()->trigger('capability', $this->storage, new ArrayObject([
$property => $value
]));
}
}
return $this;
}
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
interface ClearByNamespaceInterface
{
/**
* Remove items of given namespace
*
* @param string $namespace
* @return bool
*/
public function clearByNamespace($namespace);
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
interface ClearByPrefixInterface
{
/**
* Remove items matching given prefix
*
* @param string $prefix
* @return bool
*/
public function clearByPrefix($prefix);
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
interface ClearExpiredInterface
{
/**
* Remove expired items
*
* @return bool
*/
public function clearExpired();
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
use ArrayObject;
use Zend\EventManager\Event as BaseEvent;
class Event extends BaseEvent
{
/**
* Constructor
*
* Accept a storage adapter and its parameters.
*
* @param string $name Event name
* @param StorageInterface $storage
* @param ArrayObject $params
*/
public function __construct($name, StorageInterface $storage, ArrayObject $params)
{
parent::__construct($name, $storage, $params);
}
/**
* Set the event target/context
*
* @param StorageInterface $target
* @return Event
* @see Zend\EventManager\Event::setTarget()
*/
public function setTarget($target)
{
return $this->setStorage($target);
}
/**
* Alias of setTarget
*
* @param StorageInterface $storage
* @return Event
* @see Zend\EventManager\Event::setTarget()
*/
public function setStorage(StorageInterface $storage)
{
$this->target = $storage;
return $this;
}
/**
* Alias of getTarget
*
* @return StorageInterface
*/
public function getStorage()
{
return $this->getTarget();
}
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
use ArrayObject;
use Exception;
class ExceptionEvent extends PostEvent
{
/**
* The exception to be thrown
*
* @var Exception
*/
protected $exception;
/**
* Throw the exception or use the result
*
* @var bool
*/
protected $throwException = true;
/**
* Constructor
*
* Accept a target and its parameters.
*
* @param string $name
* @param StorageInterface $storage
* @param ArrayObject $params
* @param mixed $result
* @param Exception $exception
*/
public function __construct($name, StorageInterface $storage, ArrayObject $params, & $result, Exception $exception)
{
parent::__construct($name, $storage, $params, $result);
$this->setException($exception);
}
/**
* Set the exception to be thrown
*
* @param Exception $exception
* @return ExceptionEvent
*/
public function setException(Exception $exception)
{
$this->exception = $exception;
return $this;
}
/**
* Get the exception to be thrown
*
* @return Exception
*/
public function getException()
{
return $this->exception;
}
/**
* Throw the exception or use the result
*
* @param bool $flag
* @return ExceptionEvent
*/
public function setThrowException($flag)
{
$this->throwException = (bool) $flag;
return $this;
}
/**
* Throw the exception or use the result
*
* @return bool
*/
public function getThrowException()
{
return $this->throwException;
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
interface FlushableInterface
{
/**
* Flush the whole storage
*
* @return bool
*/
public function flush();
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
use IteratorAggregate;
/**
*
* @method IteratorInterface getIterator() Get the storage iterator
*/
interface IterableInterface extends IteratorAggregate
{
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
use Iterator;
interface IteratorInterface extends Iterator
{
const CURRENT_AS_SELF = 0;
const CURRENT_AS_KEY = 1;
const CURRENT_AS_VALUE = 2;
const CURRENT_AS_METADATA = 3;
/**
* Get storage instance
*
* @return StorageInterface
*/
public function getStorage();
/**
* Get iterator mode
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode();
/**
* Set iterator mode
*
* @param int $mode Value of IteratorInterface::CURRENT_AS_*
* @return IteratorInterface Fluent interface
*/
public function setMode($mode);
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage;
interface OptimizableInterface
{
/**
* Optimize the storage
*
* @return bool
*/
public function optimize();
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Plugin;
use Zend\EventManager\AbstractListenerAggregate;
abstract class AbstractPlugin extends AbstractListenerAggregate implements PluginInterface
{
/**
* @var PluginOptions
*/
protected $options;
/**
* Set pattern options
*
* @param PluginOptions $options
* @return AbstractPlugin Provides a fluent interface
*/
public function setOptions(PluginOptions $options)
{
$this->options = $options;
return $this;
}
/**
* Get all pattern options
*
* @return PluginOptions
*/
public function getOptions()
{
if (null === $this->options) {
$this->setOptions(new PluginOptions());
}
return $this->options;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Cache\Storage\Plugin;
use Zend\Cache\Storage\ClearExpiredInterface;
use Zend\Cache\Storage\PostEvent;
use Zend\EventManager\EventManagerInterface;
class ClearExpiredByFactor extends AbstractPlugin
{
/**
* {@inheritDoc}
*/
public function attach(EventManagerInterface $events, $priority = 1)
{
$callback = [$this, 'clearExpiredByFactor'];
$this->listeners[] = $events->attach('setItem.post', $callback, $priority);
$this->listeners[] = $events->attach('setItems.post', $callback, $priority);
$this->listeners[] = $events->attach('addItem.post', $callback, $priority);
$this->listeners[] = $events->attach('addItems.post', $callback, $priority);
}
/**
* Clear expired items by factor after writing new item(s)
*
* @param PostEvent $event
* @return void
*/
public function clearExpiredByFactor(PostEvent $event)
{
$storage = $event->getStorage();
if (! ($storage instanceof ClearExpiredInterface)) {
return;
}
$factor = $this->getOptions()->getClearingFactor();
if ($factor && mt_rand(1, $factor) == 1) {
$storage->clearExpired();
}
}
}

Some files were not shown because too many files have changed in this diff Show More