├── .composer-auth.json
├── .gitignore
├── .styleci.yml
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── composer.json
├── phpunit.xml.dist
├── src
├── AbstractRedisStore.php
├── Api
│ ├── CountableStore.php
│ ├── InvalidKeyException.php
│ ├── KeyValueStore.php
│ ├── NoSuchKeyException.php
│ ├── ReadException.php
│ ├── SerializationFailedException.php
│ ├── SortableStore.php
│ ├── UnserializationFailedException.php
│ ├── UnsupportedValueException.php
│ └── WriteException.php
├── ArrayStore.php
├── DbalStore.php
├── Decorator
│ ├── AbstractDecorator.php
│ ├── CachingDecorator.php
│ ├── CountableDecorator.php
│ └── SortableDecorator.php
├── JsonFileStore.php
├── MongoDbStore.php
├── NullStore.php
├── PhpRedisStore.php
├── PredisStore.php
├── RiakStore.php
├── SerializingArrayStore.php
└── Util
│ ├── KeyUtil.php
│ └── Serializer.php
└── tests
├── AbstractCountableStoreTest.php
├── AbstractKeyValueStoreTest.php
├── AbstractMongoDbStoreTest.php
├── AbstractSortableCountableStoreTest.php
├── ArrayStoreTest.php
├── DbalStoreTest.php
├── Decorator
├── AbstractDecoratorTest.php
├── CachingDecoratorTest.php
├── CountableDecoratorTest.php
└── SortableDecoratorTest.php
├── Fixtures
├── NotSerializable.php
├── PrivateProperties.php
├── TestClearableCache.php
├── TestClearableFlushableCache.php
├── TestException.php
├── TestFlushableCache.php
└── file
├── JsonFileStoreTest.php
├── NonSerializingBinaryMongoDbStoreTest.php
├── NonSerializingMongoDbStoreTest.php
├── NullStoreTest.php
├── PhpRedisStoreTest.php
├── PredisStoreTest.php
├── RiakStoreTest.php
├── SerializingArrayStoreTest.php
├── SerializingBinaryMongoDbStoreTest.php
├── SerializingMongoDbStoreTest.php
└── Util
└── SerializerTest.php
/.composer-auth.json:
--------------------------------------------------------------------------------
1 | {
2 | "github-oauth": {
3 | "github.com": "PLEASE DO NOT USE THIS TOKEN IN YOUR OWN PROJECTS/FORKS",
4 | "github.com": "This token is reserved for testing the webmozart/* repositories",
5 | "github.com": "a9debbffdd953ee9b3b82dbc3b807cde2086bb86"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: symfony
2 |
3 | enabled:
4 | - ordered_use
5 | - strict
6 |
7 | disabled:
8 | - empty_return
9 | - phpdoc_annotation_without_dot # This is still buggy: https://github.com/symfony/symfony/pull/19198
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | services:
4 | - redis-server
5 | - riak
6 | - mongodb
7 |
8 | branches:
9 | only:
10 | - master
11 |
12 | sudo: false
13 |
14 | cache:
15 | directories:
16 | - $HOME/.composer/cache/files
17 |
18 | matrix:
19 | include:
20 | - php: 5.3
21 | - php: 5.4
22 | - php: 5.5
23 | - php: 5.6
24 | - php: hhvm
25 | - php: nightly
26 | - php: 7.0
27 | env: COVERAGE=yes
28 | - php: 7.0
29 | env: COMPOSER_FLAGS='--prefer-lowest --prefer-stable'
30 | allow_failures:
31 | - php: hhvm
32 | - php: nightly
33 | fast_finish: true
34 |
35 |
36 | before_install:
37 | - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
38 | - if [[ $TRAVIS_PHP_VERSION != hhvm && $TRAVIS_PHP_VERSION != 5.3 ]]; then pecl install -f mongodb; echo "extension=mongodb.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
39 | - if [[ $TRAVIS_PHP_VERSION != hhvm && $COVERAGE != yes ]]; then phpenv config-rm xdebug.ini; fi;
40 | - if [[ $TRAVIS_REPO_SLUG = webmozart/key-value-store ]]; then cp .composer-auth.json ~/.composer/auth.json; fi;
41 | - composer self-update
42 |
43 | install:
44 | # mongodb/mongodb is not compatible with HHVM/5.3
45 | - if [[ $TRAVIS_PHP_VERSION = hhvm || $TRAVIS_PHP_VERSION = 5.3 ]]; then composer remove --dev mongodb/mongodb; fi
46 | - composer update $COMPOSER_FLAGS --prefer-dist --no-interaction
47 |
48 | script: if [[ $COVERAGE = yes ]]; then vendor/bin/phpunit --verbose --coverage-clover=coverage.clover; else vendor/bin/phpunit --verbose; fi
49 |
50 | after_script: if [[ $COVERAGE = yes ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi
51 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 |
4 | * 1.0.0-beta8 (2016-08-09)
5 |
6 | * added `MongoDbStore`
7 | * added flag `ArrayStore::SERIALIZE`
8 | * deprecated `SerializingArrayStore`
9 |
10 | * 1.0.0-beta7 (2016-01-14)
11 |
12 | * added `JsonFileStore::NO_SERIALIZE_STRINGS` and `JsonFileStore::NO_SERIALIZE_ARRAYS`
13 | * disabled serialization of `null` values in `JsonFileStore`
14 | * removed code from `JsonFileStore` and relied on webmozart/json instead
15 | * added `JsonFileStore::ESCAPE_GT_LT`
16 | * added `JsonFileStore::ESCAPE_AMPERSAND`
17 | * added `JsonFileStore::ESCAPE_SINGLE_QUOTE`
18 | * added `JsonFileStore::ESCAPE_DOUBLE_QUOTE`
19 | * added `JsonFileStore::NO_ESCAPE_SLASH`
20 | * added `JsonFileStore::NO_ESCAPE_UNICODE`
21 | * added `JsonFileStore::PRETTY_PRINT`
22 | * added `JsonFileStore::TERMINATE_WITH_LINE_FEED`
23 |
24 | * 1.0.0-beta6 (2015-10-02)
25 |
26 | * added `SerializingArrayStore`
27 | * added `DbalStore`
28 |
29 | * 1.0.0-beta5 (2015-08-11)
30 |
31 | * added `AbstractDecorator`
32 | * added `AbstractRedisStore`
33 | * added `CountableStore`
34 | * added `SortableStore`
35 | * renamed `CachedStore` to `CachingDecorator`
36 | * added `CountableDecorator`
37 | * added `SortableDecorator`
38 | * implemented `CountableStore` and `SortableStore` in `ArrayStore`,
39 | `JsonFileStore` and `NullStore`
40 | * made `KeyUtil` final
41 | * made `Serializer` final
42 |
43 | * 1.0.0-beta4 (2015-05-28)
44 |
45 | * added `KeyValueStore::keys()`
46 | * renamed `KeyValueStore::has()` to `exists()`
47 | * added `KeyValueStore::getOrFail()`
48 | * added `KeyValueStore::getMultiple()`
49 | * added `KeyValueStore::getMultipleOrFail()`
50 |
51 | * 1.0.0-beta3 (2015-04-13)
52 |
53 | * replaced `Assert` by webmozart/assert
54 |
55 | * 1.0.0-beta2 (2015-01-21)
56 |
57 | * added `PhpRedisStore`
58 | * added `CachedStore`
59 | * removed optional argument `$cache` from `JsonFileStore::__construct()`
60 | * removed implementations that don't make sense for a key-value store:
61 | * `MemcacheStore` (not persistent)
62 | * `MemcachedStore` (not persistent)
63 | * `SharedMemoryStore` (not persistent)
64 | * added `Serializer` utility
65 | * `KeyValueStore::get()` now throws an exception if the value cannot be unserialized
66 |
67 | * 1.0.0-beta (2015-01-12)
68 |
69 | * first release
70 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Bernhard Schussek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Webmozart Key-Value-Store
2 | =========================
3 |
4 | [](https://travis-ci.org/webmozart/key-value-store)
5 | [](https://ci.appveyor.com/project/webmozart/key-value-store/branch/master)
6 | [](https://scrutinizer-ci.com/g/webmozart/key-value-store/?branch=master)
7 | [](https://packagist.org/packages/webmozart/key-value-store)
8 | [](https://packagist.org/packages/webmozart/key-value-store)
9 | [](https://www.versioneye.com/php/webmozart:key-value-store/1.0.0)
10 |
11 | Latest release: [1.0.0](https://packagist.org/packages/webmozart/key-value-store#1.0.0)
12 |
13 | A key-value store API with implementations for different backends.
14 |
15 | [API Documentation]
16 |
17 | All contained key-value stores implement the interface [`KeyValueStore`]. The
18 | following stores are currently supported:
19 |
20 | * [`ArrayStore`]
21 | * [`DbalStore`]
22 | * [`JsonFileStore`]
23 | * [`MongoDbStore`]
24 | * [`NullStore`]
25 | * [`PhpRedisStore`]
26 | * [`PredisStore`]
27 | * [`RiakStore`]
28 |
29 | The interface [`CountableStore`] is supported by the following classes:
30 |
31 | * [`ArrayStore`]
32 | * [`JsonFileStore`]
33 | * [`NullStore`]
34 | * [`CountableDecorator`]
35 |
36 | The interface [`SortableStore`] is supported by the following classes:
37 |
38 | * [`ArrayStore`]
39 | * [`JsonFileStore`]
40 | * [`NullStore`]
41 | * [`SortableDecorator`]
42 |
43 | The decorator [`CachingDecorator`] exists for caching another store instance
44 | in a Doctrine cache.
45 |
46 | FAQ
47 | ---
48 |
49 | **Why not use [Doctrine Cache]?**
50 |
51 | Caching is **not** key-value storage. When you use a cache, you accept that keys
52 | may disappear for various reasons:
53 |
54 | * Keys may expire.
55 | * Keys may be overwritten when the cache is full.
56 | * Keys may be lost after shutdowns.
57 | * ...
58 |
59 | In another word, caches are *volatile*. This is not a problem, since the cached
60 | data is usually stored safely somewhere else. The point of a cache is to provide
61 | high-performance access to frequently needed data.
62 |
63 | Key-value stores, on the other hand, are *persistent*. When you write a key to a
64 | key-value store, you expect it to be there until you delete it. It would be a
65 | disaster if data would silently disappear from a key-value store (or any other
66 | kind of database).
67 |
68 | Hence the two libraries fulfill two very different purposes, even if their
69 | interfaces and implementations are often similar.
70 |
71 | The [`CachingDecorator`] actually uses a Doctrine Cache object to cache the data
72 | of a persistent [`KeyValueStore`].
73 |
74 | Authors
75 | -------
76 |
77 | * [Bernhard Schussek] a.k.a. [@webmozart]
78 | * [The Community Contributors]
79 |
80 | Installation
81 | ------------
82 |
83 | Use [Composer] to install the package:
84 |
85 | ```
86 | $ composer require webmozart/key-value-store
87 | ```
88 |
89 | Contribute
90 | ----------
91 |
92 | Contributions to the package are always welcome!
93 |
94 | * Report any bugs or issues you find on the [issue tracker].
95 | * You can grab the source code at the package's [Git repository].
96 |
97 | Support
98 | -------
99 |
100 | If you are having problems, send a mail to bschussek@gmail.com or shout out to
101 | [@webmozart] on Twitter.
102 |
103 | License
104 | -------
105 |
106 | All contents of this package are licensed under the [MIT license].
107 |
108 | [Composer]: https://getcomposer.org
109 | [Bernhard Schussek]: http://webmozarts.com
110 | [The Community Contributors]: https://github.com/webmozart/key-value-store/graphs/contributors
111 | [issue tracker]: https://github.com/webmozart/key-value-store/issues
112 | [Git repository]: https://github.com/webmozart/key-value-store
113 | [@webmozart]: https://twitter.com/webmozart
114 | [MIT license]: LICENSE
115 | [Doctrine Cache]: https://github.com/doctrine/cache
116 | [API Documentation]: https://webmozart.github.io/key-value-store/api
117 | [`KeyValueStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.Api.KeyValueStore.html
118 | [`CountableStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.Api.CountableStore.html
119 | [`SortableStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.Api.SortableStore.html
120 | [`ArrayStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.ArrayStore.html
121 | [`DbalStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.DbalStore.html
122 | [`JsonFileStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.JsonFileStore.html
123 | [`MongoDbStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.MongoDbStore.html
124 | [`NullStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.NullStore.html
125 | [`PhpRedisStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.PhpRedisStore.html
126 | [`PredisStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.PredisStore.html
127 | [`RiakStore`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.RiakStore.html
128 | [`CachingDecorator`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.Decorator.CachingDecorator.html
129 | [`CountableDecorator`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.Decorator.CountableDecorator.html
130 | [`SortableDecorator`]: https://webmozart.github.io/key-value-store/api/latest/class-Webmozart.KeyValueStore.Decorator.SortableDecorator.html
131 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | build: false
2 | platform: x86
3 | clone_folder: c:\projects\webmozart\key-value-store
4 |
5 | services:
6 | - mongodb
7 |
8 | branches:
9 | only:
10 | - master
11 |
12 | cache:
13 | - c:\php -> appveyor.yml
14 |
15 | init:
16 | - SET PATH=c:\php;%PATH%
17 | - SET COMPOSER_NO_INTERACTION=1
18 | - SET PHP=1
19 |
20 | install:
21 | - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
22 | - cd c:\php
23 | - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-7.0.0-nts-Win32-VC14-x86.zip
24 | - IF %PHP%==1 7z x php-7.0.0-nts-Win32-VC14-x86.zip -y >nul
25 | - IF %PHP%==1 del /Q *.zip
26 | - IF %PHP%==1 cd ext
27 | - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/mongodb/1.1.8/php_mongodb-1.1.8-7.0-nts-vc14-x86.zip
28 | - IF %PHP%==1 7z x php_mongodb-1.1.8-7.0-nts-vc14-x86.zip php_mongodb.dll -y >nul
29 | - IF %PHP%==1 del /Q *.zip
30 | - IF %PHP%==1 cd ..
31 | - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
32 | - IF %PHP%==1 copy /Y php.ini-development php.ini
33 | - IF %PHP%==1 echo max_execution_time=1200 >> php.ini
34 | - IF %PHP%==1 echo date.timezone="UTC" >> php.ini
35 | - IF %PHP%==1 echo extension_dir=ext >> php.ini
36 | - IF %PHP%==1 echo extension=php_curl.dll >> php.ini
37 | - IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini
38 | - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
39 | - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini
40 | - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini
41 | - IF %PHP%==1 echo extension=php_mongodb.dll >> php.ini
42 | - appveyor DownloadFile https://getcomposer.org/composer.phar
43 | - cd c:\projects\webmozart\key-value-store
44 | - mkdir %APPDATA%\Composer
45 | - IF %APPVEYOR_REPO_NAME%==webmozart/key-value-store copy /Y .composer-auth.json %APPDATA%\Composer\auth.json
46 | - composer update --prefer-dist --no-progress --ansi
47 |
48 | test_script:
49 | - cd c:\projects\webmozart\key-value-store
50 | - vendor\bin\phpunit.bat --verbose
51 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webmozart/key-value-store",
3 | "description": "A key-value store API with implementations for different backends.",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Bernhard Schussek",
8 | "email": "bschussek@gmail.com"
9 | }
10 | ],
11 | "require": {
12 | "php": "^5.3.3|^7.0",
13 | "webmozart/assert": "^1.0"
14 | },
15 | "require-dev": {
16 | "webmozart/json": "^1.1.1",
17 | "symfony/filesystem": "^2.5",
18 | "predis/predis": "^1.0",
19 | "basho/riak": "^1.4",
20 | "doctrine/cache": "^1.4",
21 | "phpunit/phpunit": "^4.6",
22 | "doctrine/dbal" : "^2.4",
23 | "sebastian/version": "^1.0.1",
24 | "mongodb/mongodb": "^1.0"
25 | },
26 | "suggest": {
27 | "webmozart/json": "to enable the JsonFileStore",
28 | "predis/predis": "to enable the PredisStore",
29 | "basho/riak": "to enable the RiakStore",
30 | "doctrine/cache": "to enable the CachedStore",
31 | "doctrine/dbal": "to enable the DbalStore",
32 | "mongodb/mongodb": "to enable the MongoDbStore"
33 | },
34 | "autoload": {
35 | "psr-4": {
36 | "Webmozart\\KeyValueStore\\": "src/"
37 | }
38 | },
39 | "autoload-dev": {
40 | "psr-4": {
41 | "Webmozart\\KeyValueStore\\Tests\\": "tests/"
42 | }
43 | },
44 | "extra": {
45 | "branch-alias": {
46 | "dev-master": "1.1-dev"
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ./tests/
7 |
8 |
9 |
10 |
11 |
12 |
13 | ./src/
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/AbstractRedisStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Exception;
15 | use Webmozart\KeyValueStore\Api\KeyValueStore;
16 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
17 | use Webmozart\KeyValueStore\Api\ReadException;
18 | use Webmozart\KeyValueStore\Api\WriteException;
19 | use Webmozart\KeyValueStore\Util\KeyUtil;
20 | use Webmozart\KeyValueStore\Util\Serializer;
21 |
22 | /**
23 | * An abstract Redis key-value store to support multiple Redis clients without code duplication.
24 | *
25 | * @since 1.0
26 | *
27 | * @author Bernhard Schussek
28 | * @author Titouan Galopin
29 | */
30 | abstract class AbstractRedisStore implements KeyValueStore
31 | {
32 | /**
33 | * Redis client.
34 | *
35 | * @var object
36 | */
37 | protected $client;
38 |
39 | /**
40 | * Return the value corresponding to "not found"
41 | * for the internal client.
42 | *
43 | * @return mixed
44 | */
45 | abstract protected function clientNotFoundValue();
46 |
47 | /**
48 | * Call the internal client method to fetch a key.
49 | * Don't have to catch the exceptions.
50 | *
51 | * @param string $key The key to fetch
52 | *
53 | * @return mixed The raw value
54 | */
55 | abstract protected function clientGet($key);
56 |
57 | /**
58 | * Call the internal client method to fetch multiple keys.
59 | * Don't have to catch the exceptions.
60 | *
61 | * @param array $keys The keys to fetch
62 | *
63 | * @return array The raw values
64 | */
65 | abstract protected function clientGetMultiple(array $keys);
66 |
67 | /**
68 | * Call the internal client method to set a value associated to a key.
69 | * Don't have to catch the exceptions.
70 | *
71 | * @param string $key
72 | * @param mixed $value
73 | */
74 | abstract protected function clientSet($key, $value);
75 |
76 | /**
77 | * Call the internal client method to remove a key.
78 | * Don't have to catch the exceptions.
79 | *
80 | * @param string $key
81 | *
82 | * @return bool true if the removal worked, false otherwise
83 | */
84 | abstract protected function clientRemove($key);
85 |
86 | /**
87 | * Call the internal client method to check if a key exists.
88 | * Don't have to catch the exceptions.
89 | *
90 | * @param string $key
91 | *
92 | * @return bool
93 | */
94 | abstract protected function clientExists($key);
95 |
96 | /**
97 | * Call the internal client method to clear all the keys.
98 | * Don't have to catch the exceptions.
99 | */
100 | abstract protected function clientClear();
101 |
102 | /**
103 | * Call the internal client method to fetch all the keys.
104 | * Don't have to catch the exceptions.
105 | *
106 | * @return array The keys
107 | */
108 | abstract protected function clientKeys();
109 |
110 | /**
111 | * {@inheritdoc}
112 | */
113 | public function getMultiple(array $keys, $default = null)
114 | {
115 | KeyUtil::validateMultiple($keys);
116 |
117 | // Normalize indices of the array
118 | $keys = array_values($keys);
119 | $values = array();
120 |
121 | try {
122 | $serializedValues = $this->clientGetMultiple($keys);
123 | } catch (Exception $e) {
124 | throw ReadException::forException($e);
125 | }
126 |
127 | foreach ($serializedValues as $i => $serializedValue) {
128 | $values[$keys[$i]] = $this->clientNotFoundValue() === $serializedValue
129 | ? $default
130 | : Serializer::unserialize($serializedValue);
131 | }
132 |
133 | return $values;
134 | }
135 |
136 | /**
137 | * {@inheritdoc}
138 | */
139 | public function getMultipleOrFail(array $keys)
140 | {
141 | KeyUtil::validateMultiple($keys);
142 |
143 | // Normalize indices of the array
144 | $keys = array_values($keys);
145 | $values = array();
146 | $notFoundKeys = array();
147 |
148 | try {
149 | $serializedValues = $this->clientGetMultiple($keys);
150 | } catch (Exception $e) {
151 | throw ReadException::forException($e);
152 | }
153 |
154 | foreach ($serializedValues as $i => $serializedValue) {
155 | if ($this->clientNotFoundValue() === $serializedValue) {
156 | $notFoundKeys[] = $keys[$i];
157 | } elseif (0 === count($notFoundKeys)) {
158 | $values[$keys[$i]] = Serializer::unserialize($serializedValue);
159 | }
160 | }
161 |
162 | if (0 !== count($notFoundKeys)) {
163 | throw NoSuchKeyException::forKeys($notFoundKeys);
164 | }
165 |
166 | return $values;
167 | }
168 |
169 | /**
170 | * {@inheritdoc}
171 | */
172 | public function get($key, $default = null)
173 | {
174 | KeyUtil::validate($key);
175 |
176 | try {
177 | $serialized = $this->clientGet($key);
178 | } catch (Exception $e) {
179 | throw ReadException::forException($e);
180 | }
181 |
182 | if ($this->clientNotFoundValue() === $serialized) {
183 | return $default;
184 | }
185 |
186 | return Serializer::unserialize($serialized);
187 | }
188 |
189 | /**
190 | * {@inheritdoc}
191 | */
192 | public function getOrFail($key)
193 | {
194 | KeyUtil::validate($key);
195 |
196 | try {
197 | $serialized = $this->clientGet($key);
198 | } catch (Exception $e) {
199 | throw ReadException::forException($e);
200 | }
201 |
202 | if ($this->clientNotFoundValue() === $serialized) {
203 | throw NoSuchKeyException::forKey($key);
204 | }
205 |
206 | return Serializer::unserialize($serialized);
207 | }
208 |
209 | /**
210 | * {@inheritdoc}
211 | */
212 | public function set($key, $value)
213 | {
214 | KeyUtil::validate($key);
215 |
216 | $serialized = Serializer::serialize($value);
217 |
218 | try {
219 | $this->clientSet($key, $serialized);
220 | } catch (Exception $e) {
221 | throw WriteException::forException($e);
222 | }
223 | }
224 |
225 | /**
226 | * {@inheritdoc}
227 | */
228 | public function remove($key)
229 | {
230 | KeyUtil::validate($key);
231 |
232 | try {
233 | return (bool) $this->clientRemove($key);
234 | } catch (Exception $e) {
235 | throw WriteException::forException($e);
236 | }
237 | }
238 |
239 | /**
240 | * {@inheritdoc}
241 | */
242 | public function exists($key)
243 | {
244 | KeyUtil::validate($key);
245 |
246 | try {
247 | return $this->clientExists($key);
248 | } catch (Exception $e) {
249 | throw ReadException::forException($e);
250 | }
251 | }
252 |
253 | /**
254 | * {@inheritdoc}
255 | */
256 | public function clear()
257 | {
258 | try {
259 | $this->clientClear();
260 | } catch (Exception $e) {
261 | throw WriteException::forException($e);
262 | }
263 | }
264 |
265 | /**
266 | * {@inheritdoc}
267 | */
268 | public function keys()
269 | {
270 | try {
271 | return $this->clientKeys();
272 | } catch (Exception $e) {
273 | throw ReadException::forException($e);
274 | }
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/src/Api/CountableStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | /**
15 | * A countable key-value store.
16 | *
17 | * In addition of the properties of a classical store, a countable store
18 | * has the ability to count its keys.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | * @author Titouan Galopin
24 | */
25 | interface CountableStore extends KeyValueStore
26 | {
27 | /**
28 | * Count the number of keys in the store.
29 | *
30 | * @throws ReadException If the store cannot be read.
31 | */
32 | public function count();
33 | }
34 |
--------------------------------------------------------------------------------
/src/Api/InvalidKeyException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | use Exception;
15 | use RuntimeException;
16 |
17 | /**
18 | * Thrown when a key is invalid.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class InvalidKeyException extends RuntimeException
25 | {
26 | /**
27 | * Creates an exception for an invalid key.
28 | *
29 | * @param mixed $key The invalid key.
30 | * @param Exception|null $cause The exception that caused this exception.
31 | *
32 | * @return static The created exception.
33 | */
34 | public static function forKey($key, Exception $cause = null)
35 | {
36 | return new static(sprintf(
37 | 'Expected a key of type integer or string. Got: %s',
38 | is_object($key) ? get_class($key) : gettype($key)
39 | ), 0, $cause);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Api/KeyValueStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | /**
15 | * A key-value store.
16 | *
17 | * KeyUtil-value stores support storing values for integer or string keys chosen
18 | * by the user. Any serializable value can be stored, although an implementation
19 | * of this interface may further restrict the range of accepted values. See the
20 | * documentation of the implementation for more information.
21 | *
22 | * @since 1.0
23 | *
24 | * @author Bernhard Schussek
25 | */
26 | interface KeyValueStore
27 | {
28 | /**
29 | * Sets the value for a key in the store.
30 | *
31 | * The key-value store accepts any serializable value. If a value is not
32 | * serializable, a {@link SerializationFailedException} is thrown.
33 | * Additionally, implementations may put further restrictions on their
34 | * accepted values. If an unsupported value is passed, an
35 | * {@link UnsupportedValueException} is thrown. Check the documentation of
36 | * the implementation to learn more about its supported values.
37 | *
38 | * Any integer or string value is accepted as key. If any other type is
39 | * passed for the key, an {@link InvalidKeyException} is thrown. You should
40 | * make sure that you only pass valid keys to the store.
41 | *
42 | * If the backend of the store cannot be written, a {@link WriteException}
43 | * is thrown. You should always handle this exception in your code:
44 | *
45 | * ```php
46 | * try {
47 | * $store->set($key, $value);
48 | * } catch (WriteException $e) {
49 | * // write failed
50 | * }
51 | * ```
52 | *
53 | * @param int|string $key The key to set.
54 | * @param mixed $value The value to set for the key.
55 | *
56 | * @throws WriteException If the store cannot be written.
57 | * @throws InvalidKeyException If the key is not a string or integer.
58 | * @throws SerializationFailedException If the value cannot be serialized.
59 | * @throws UnsupportedValueException If the value is not supported by the
60 | * implementation.
61 | */
62 | public function set($key, $value);
63 |
64 | /**
65 | * Returns the value of a key in the store.
66 | *
67 | * If a key does not exist in the store, the default value passed in the
68 | * second parameter is returned.
69 | *
70 | * Any integer or string value is accepted as key. If any other type is
71 | * passed for the key, an {@link InvalidKeyException} is thrown. You should
72 | * make sure that you only pass valid keys to the store.
73 | *
74 | * If the backend of the store cannot be read, a {@link ReadException}
75 | * is thrown. You should always handle this exception in your code:
76 | *
77 | * ```php
78 | * try {
79 | * $value = $store->get($key);
80 | * } catch (ReadException $e) {
81 | * // read failed
82 | * }
83 | * ```
84 | *
85 | * @param int|string $key The key to get.
86 | * @param mixed $default The default value to return if the key does
87 | * not exist.
88 | *
89 | * @return mixed The value of the key or the default value if the key does
90 | * not exist.
91 | *
92 | * @throws ReadException If the store cannot be read.
93 | * @throws InvalidKeyException If the key is not a string or integer.
94 | * @throws UnserializationFailedException If the stored value cannot be
95 | * unserialized.
96 | */
97 | public function get($key, $default = null);
98 |
99 | /**
100 | * Returns the value of a key in the store.
101 | *
102 | * If the key does not exist in the store, an exception is thrown.
103 | *
104 | * Any integer or string value is accepted as key. If any other type is
105 | * passed for the key, an {@link InvalidKeyException} is thrown. You should
106 | * make sure that you only pass valid keys to the store.
107 | *
108 | * If the backend of the store cannot be read, a {@link ReadException}
109 | * is thrown. You should always handle this exception in your code:
110 | *
111 | * ```php
112 | * try {
113 | * $value = $store->getOrFail($key);
114 | * } catch (ReadException $e) {
115 | * // read failed
116 | * }
117 | * ```
118 | *
119 | * @param int|string $key The key to get.
120 | *
121 | * @return mixed The value of the key.
122 | *
123 | * @throws ReadException If the store cannot be read.
124 | * @throws NoSuchKeyException If the key was not found.
125 | * @throws InvalidKeyException If the key is not a string or integer.
126 | * @throws UnserializationFailedException If the stored value cannot be
127 | * unserialized.
128 | */
129 | public function getOrFail($key);
130 |
131 | /**
132 | * Returns the values of multiple keys in the store.
133 | *
134 | * The passed default value is returned for keys that don't exist.
135 | *
136 | * Any integer or string value is accepted as key. If any other type is
137 | * passed for the key, an {@link InvalidKeyException} is thrown. You should
138 | * make sure that you only pass valid keys to the store.
139 | *
140 | * If the backend of the store cannot be read, a {@link ReadException}
141 | * is thrown. You should always handle this exception in your code:
142 | *
143 | * ```php
144 | * try {
145 | * $value = $store->getMultiple(array($key1, $key2));
146 | * } catch (ReadException $e) {
147 | * // read failed
148 | * }
149 | * ```
150 | *
151 | * @param array $keys The keys to get. The keys must be strings or integers.
152 | * @param mixed $default The default value to return for keys that are not
153 | * found.
154 | *
155 | * @return array The values of the passed keys, indexed by the keys.
156 | *
157 | * @throws ReadException If the store cannot be read.
158 | * @throws InvalidKeyException If a key is not a string or integer.
159 | * @throws UnserializationFailedException If a stored value cannot be
160 | * unserialized.
161 | */
162 | public function getMultiple(array $keys, $default = null);
163 |
164 | /**
165 | * Returns the values of multiple keys in the store.
166 | *
167 | * If a key does not exist in the store, an exception is thrown.
168 | *
169 | * Any integer or string value is accepted as key. If any other type is
170 | * passed for the key, an {@link InvalidKeyException} is thrown. You should
171 | * make sure that you only pass valid keys to the store.
172 | *
173 | * If the backend of the store cannot be read, a {@link ReadException}
174 | * is thrown. You should always handle this exception in your code:
175 | *
176 | * ```php
177 | * try {
178 | * $value = $store->getMultipleOrFail(array($key1, $key2));
179 | * } catch (ReadException $e) {
180 | * // read failed
181 | * }
182 | * ```
183 | *
184 | * @param array $keys The keys to get. The keys must be strings or integers.
185 | *
186 | * @return array The values of the passed keys, indexed by the keys.
187 | *
188 | * @throws ReadException If the store cannot be read.
189 | * @throws NoSuchKeyException If a key was not found.
190 | * @throws InvalidKeyException If a key is not a string or integer.
191 | * @throws UnserializationFailedException If a stored value cannot be
192 | * unserialized.
193 | */
194 | public function getMultipleOrFail(array $keys);
195 |
196 | /**
197 | * Removes a key from the store.
198 | *
199 | * If the store does not contain the key, this method returns `false`.
200 | *
201 | * Any integer or string value is accepted as key. If any other type is
202 | * passed for the key, an {@link InvalidKeyException} is thrown. You should
203 | * make sure that you only pass valid keys to the store.
204 | *
205 | * If the backend of the store cannot be written, a {@link WriteException}
206 | * is thrown. You should always handle this exception in your code:
207 | *
208 | * ```php
209 | * try {
210 | * $store->remove($key);
211 | * } catch (WriteException $e) {
212 | * // write failed
213 | * }
214 | * ```
215 | *
216 | * @param int|string $key The key to remove.
217 | *
218 | * @return bool Returns `true` if a key was removed from the store.
219 | *
220 | * @throws WriteException If the store cannot be written.
221 | * @throws InvalidKeyException If the key is not a string or integer.
222 | */
223 | public function remove($key);
224 |
225 | /**
226 | * Returns whether a key exists.
227 | *
228 | * Any integer or string value is accepted as key. If any other type is
229 | * passed for the key, an {@link InvalidKeyException} is thrown. You should
230 | * make sure that you only pass valid keys to the store.
231 | *
232 | * If the backend of the store cannot be read, a {@link ReadException}
233 | * is thrown. You should always handle this exception in your code:
234 | *
235 | * ```php
236 | * try {
237 | * if ($store->exists($key)) {
238 | * // ...
239 | * }
240 | * } catch (ReadException $e) {
241 | * // read failed
242 | * }
243 | * ```
244 | *
245 | * @param int|string $key The key to test.
246 | *
247 | * @return bool Whether the key exists in the store.
248 | *
249 | * @throws ReadException If the store cannot be read.
250 | * @throws InvalidKeyException If the key is not a string or integer.
251 | */
252 | public function exists($key);
253 |
254 | /**
255 | * Removes all keys from the store.
256 | *
257 | * If the backend of the store cannot be written, a {@link WriteException}
258 | * is thrown. You should always handle this exception in your code:
259 | *
260 | * ```php
261 | * try {
262 | * $store->clear();
263 | * } catch (WriteException $e) {
264 | * // write failed
265 | * }
266 | * ```
267 | *
268 | * @throws WriteException If the store cannot be written.
269 | */
270 | public function clear();
271 |
272 | /**
273 | * Returns all keys currently stored in the store.
274 | *
275 | * If the backend of the store cannot be read, a {@link ReadException}
276 | * is thrown. You should always handle this exception in your code:
277 | *
278 | * ```php
279 | * try {
280 | * foreach ($store->keys() as $key) {
281 | * // ...
282 | * }
283 | * } catch (ReadException $e) {
284 | * // read failed
285 | * }
286 | * ```
287 | *
288 | * @return array The keys stored in the store. Each key is either a string
289 | * or an integer. The order of the keys is undefined.
290 | *
291 | * @throws ReadException If the store cannot be read.
292 | */
293 | public function keys();
294 | }
295 |
--------------------------------------------------------------------------------
/src/Api/NoSuchKeyException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | use Exception;
15 | use RuntimeException;
16 |
17 | /**
18 | * Thrown when a key was not found in the store.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class NoSuchKeyException extends RuntimeException
25 | {
26 | /**
27 | * Creates an exception for a key that was not found.
28 | *
29 | * @param string|int $key The key that was not found.
30 | * @param Exception|null $cause The exception that caused this exception.
31 | *
32 | * @return static The created exception.
33 | */
34 | public static function forKey($key, Exception $cause = null)
35 | {
36 | return new static(sprintf(
37 | 'The key "%s" does not exist.',
38 | $key
39 | ), 0, $cause);
40 | }
41 |
42 | /**
43 | * Creates an exception for multiple keys that were not found.
44 | *
45 | * @param array[] $keys The keys that were not found.
46 | * @param Exception|null $cause The exception that caused this exception.
47 | *
48 | * @return static The created exception.
49 | */
50 | public static function forKeys(array $keys, Exception $cause = null)
51 | {
52 | return new static(sprintf(
53 | 'The keys "%s" does not exist.',
54 | implode('", "', $keys)
55 | ), 0, $cause);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Api/ReadException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | use Exception;
15 | use RuntimeException;
16 |
17 | /**
18 | * Thrown when a key-value store cannot be read.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class ReadException extends RuntimeException
25 | {
26 | /**
27 | * Creates a new exception..
28 | *
29 | * @param Exception $exception The exception that caused this exception.
30 | *
31 | * @return static The new exception.
32 | */
33 | public static function forException(Exception $exception)
34 | {
35 | return new static(sprintf(
36 | 'Could not read key-value store: %s',
37 | $exception->getMessage()
38 | ), $exception->getCode(), $exception);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Api/SerializationFailedException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | use Exception;
15 | use RuntimeException;
16 |
17 | /**
18 | * Thrown when a value cannot be serialized.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class SerializationFailedException extends RuntimeException
25 | {
26 | /**
27 | * Creates a new exception for the given value.
28 | *
29 | * @param mixed $value The value that could not be serialized.
30 | * @param string $reason The reason why the value could not be
31 | * unserialized.
32 | * @param int $code The exception code.
33 | * @param Exception $cause The exception that caused this exception.
34 | *
35 | * @return static The new exception.
36 | */
37 | public static function forValue($value, $reason = '', $code = 0, Exception $cause = null)
38 | {
39 | return self::forType(is_object($value) ? get_class($value) : gettype($value), $reason, $code, $cause);
40 | }
41 |
42 | /**
43 | * Creates a new exception for the given value type.
44 | *
45 | * @param string $type The type that could not be serialized.
46 | * @param string $reason The reason why the value could not be
47 | * unserialized.
48 | * @param int $code The exception code.
49 | * @param Exception $cause The exception that caused this exception.
50 | *
51 | * @return static The new exception.
52 | */
53 | public static function forType($type, $reason = '', $code = 0, Exception $cause = null)
54 | {
55 | return new static(sprintf(
56 | 'Could not serialize value of type %s%s',
57 | $type,
58 | $reason ? ': '.$reason : '.'
59 | ), $code, $cause);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Api/SortableStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | /**
15 | * A sortable key-value store.
16 | *
17 | * In addition of the properties of a classical store, a sortable store
18 | * has the ability to sort its values by its keys.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | * @author Titouan Galopin
24 | */
25 | interface SortableStore extends KeyValueStore
26 | {
27 | /**
28 | * Sort the store by its keys.
29 | *
30 | * The store values will be arranged from lowest to highest when this
31 | * function has completed.
32 | *
33 | * This method accepts an optional second parameter that may be used
34 | * to modify the sorting behavior using the standard sort flags of PHP.
35 | *
36 | * @see http://php.net/manual/en/function.sort.php
37 | *
38 | * @param int $flags Sorting type flags (from the standard PHP sort flags).
39 | *
40 | * @throws ReadException If the store cannot be read.
41 | * @throws WriteException If the store cannot be written.
42 | */
43 | public function sort($flags = SORT_REGULAR);
44 | }
45 |
--------------------------------------------------------------------------------
/src/Api/UnserializationFailedException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | use Exception;
15 | use RuntimeException;
16 |
17 | /**
18 | * Thrown when a value cannot be unserialized.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class UnserializationFailedException extends RuntimeException
25 | {
26 | /**
27 | * Creates a new exception for the given value.
28 | *
29 | * @param mixed $value The value that could not be unserialized.
30 | * @param string $reason The reason why the value could not be
31 | * unserialized.
32 | * @param int $code The exception code.
33 | * @param Exception $cause The exception that caused this exception.
34 | *
35 | * @return static The new exception.
36 | */
37 | public static function forValue($value, $reason = '', $code = 0, Exception $cause = null)
38 | {
39 | return self::forType(is_object($value) ? get_class($value) : gettype($value), $reason, $code, $cause);
40 | }
41 |
42 | /**
43 | * Creates a new exception for the given value type.
44 | *
45 | * @param string $type The type that could not be unserialized.
46 | * @param string $reason The reason why the value could not be
47 | * unserialized.
48 | * @param int $code The exception code.
49 | * @param Exception $cause The exception that caused this exception.
50 | *
51 | * @return static The new exception.
52 | */
53 | public static function forType($type, $reason = '', $code = 0, Exception $cause = null)
54 | {
55 | return new static(sprintf(
56 | 'Could not unserialize value of type %s%s',
57 | $type,
58 | $reason ? ': '.$reason : '.'
59 | ), $code, $cause);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Api/UnsupportedValueException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | use Exception;
15 | use RuntimeException;
16 |
17 | /**
18 | * Thrown when an unsupported value is stored in a key-value store.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class UnsupportedValueException extends RuntimeException
25 | {
26 | /**
27 | * Creates a new exception for the given value type.
28 | *
29 | * @param string $type The name of the unsupported type.
30 | * @param KeyValueStore $store The store that does not support the type.
31 | * @param int $code The exception code.
32 | * @param Exception|null $cause The exception that caused this exception.
33 | *
34 | * @return static The new exception.
35 | */
36 | public static function forType($type, KeyValueStore $store, $code = 0, Exception $cause = null)
37 | {
38 | return new static(sprintf(
39 | 'Values of type %s are not supported by %s.',
40 | $type,
41 | get_class($store)
42 | ), $code, $cause);
43 | }
44 |
45 | /**
46 | * Creates a new exception for the given value.
47 | *
48 | * @param string $value The unsupported value.
49 | * @param KeyValueStore $store The store that does not support the type.
50 | * @param int $code The exception code.
51 | * @param Exception|null $cause The exception that caused this exception.
52 | *
53 | * @return static The new exception.
54 | */
55 | public static function forValue($value, KeyValueStore $store, $code = 0, Exception $cause = null)
56 | {
57 | return new static(sprintf(
58 | 'Values of type %s are not supported by %s.',
59 | gettype($value),
60 | get_class($store)
61 | ), $code, $cause);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Api/WriteException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Api;
13 |
14 | use Exception;
15 | use RuntimeException;
16 |
17 | /**
18 | * Thrown when a key-value store cannot be written.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class WriteException extends RuntimeException
25 | {
26 | /**
27 | * Creates a new exception..
28 | *
29 | * @param Exception $exception The exception that caused this exception.
30 | *
31 | * @return static The new exception.
32 | */
33 | public static function forException(Exception $exception)
34 | {
35 | return new static(sprintf(
36 | 'Could not write key-value store: %s',
37 | $exception->getMessage()
38 | ), $exception->getCode(), $exception);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/ArrayStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Webmozart\KeyValueStore\Api\CountableStore;
15 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
16 | use Webmozart\KeyValueStore\Api\SortableStore;
17 | use Webmozart\KeyValueStore\Util\KeyUtil;
18 |
19 | /**
20 | * A key-value store backed by a PHP array.
21 | *
22 | * The contents of the store are lost when the store is released from memory.
23 | *
24 | * @since 1.0
25 | *
26 | * @author Bernhard Schussek
27 | */
28 | class ArrayStore implements SortableStore, CountableStore
29 | {
30 | /**
31 | * Flag: Enable serialization.
32 | */
33 | const SERIALIZE = 1;
34 |
35 | /**
36 | * @var array
37 | */
38 | private $array = array();
39 |
40 | /**
41 | * @var callable
42 | */
43 | private $serialize;
44 |
45 | /**
46 | * @var callable
47 | */
48 | private $unserialize;
49 |
50 | /**
51 | * Creates a new store.
52 | *
53 | * @param array $array The values to set initially in the store.
54 | */
55 | public function __construct(array $array = array(), $flags = 0)
56 | {
57 | if ($flags & self::SERIALIZE) {
58 | $this->serialize = array(
59 | 'Webmozart\KeyValueStore\Util\Serializer',
60 | 'serialize',
61 | );
62 | $this->unserialize = array(
63 | 'Webmozart\KeyValueStore\Util\Serializer',
64 | 'unserialize',
65 | );
66 | } else {
67 | $this->serialize = function ($value) {
68 | return $value;
69 | };
70 | $this->unserialize = function ($value) {
71 | return $value;
72 | };
73 | }
74 |
75 | foreach ($array as $key => $value) {
76 | $this->set($key, $value);
77 | }
78 | }
79 |
80 | /**
81 | * {@inheritdoc}
82 | */
83 | public function set($key, $value)
84 | {
85 | KeyUtil::validate($key);
86 |
87 | $this->array[$key] = call_user_func($this->serialize, $value);
88 | }
89 |
90 | /**
91 | * {@inheritdoc}
92 | */
93 | public function get($key, $default = null)
94 | {
95 | KeyUtil::validate($key);
96 |
97 | if (!array_key_exists($key, $this->array)) {
98 | return $default;
99 | }
100 |
101 | return call_user_func($this->unserialize, $this->array[$key]);
102 | }
103 |
104 | /**
105 | * {@inheritdoc}
106 | */
107 | public function getOrFail($key)
108 | {
109 | KeyUtil::validate($key);
110 |
111 | if (!array_key_exists($key, $this->array)) {
112 | throw NoSuchKeyException::forKey($key);
113 | }
114 |
115 | return call_user_func($this->unserialize, $this->array[$key]);
116 | }
117 |
118 | /**
119 | * {@inheritdoc}
120 | */
121 | public function getMultiple(array $keys, $default = null)
122 | {
123 | KeyUtil::validateMultiple($keys);
124 |
125 | $values = array();
126 |
127 | foreach ($keys as $key) {
128 | $values[$key] = array_key_exists($key, $this->array)
129 | ? call_user_func($this->unserialize, $this->array[$key])
130 | : $default;
131 | }
132 |
133 | return $values;
134 | }
135 |
136 | /**
137 | * {@inheritdoc}
138 | */
139 | public function getMultipleOrFail(array $keys)
140 | {
141 | KeyUtil::validateMultiple($keys);
142 |
143 | $notFoundKeys = array_diff($keys, array_keys($this->array));
144 |
145 | if (count($notFoundKeys) > 0) {
146 | throw NoSuchKeyException::forKeys($notFoundKeys);
147 | }
148 |
149 | return $this->getMultiple($keys);
150 | }
151 |
152 | /**
153 | * {@inheritdoc}
154 | */
155 | public function remove($key)
156 | {
157 | KeyUtil::validate($key);
158 |
159 | $removed = array_key_exists($key, $this->array);
160 |
161 | unset($this->array[$key]);
162 |
163 | return $removed;
164 | }
165 |
166 | /**
167 | * {@inheritdoc}
168 | */
169 | public function exists($key)
170 | {
171 | KeyUtil::validate($key);
172 |
173 | return array_key_exists($key, $this->array);
174 | }
175 |
176 | /**
177 | * {@inheritdoc}
178 | */
179 | public function clear()
180 | {
181 | $this->array = array();
182 | }
183 |
184 | /**
185 | * {@inheritdoc}
186 | */
187 | public function keys()
188 | {
189 | return array_keys($this->array);
190 | }
191 |
192 | /**
193 | * Returns the contents of the store as array.
194 | *
195 | * @return array The keys and values in the store.
196 | */
197 | public function toArray()
198 | {
199 | return array_map($this->unserialize, $this->array);
200 | }
201 |
202 | /**
203 | * {@inheritdoc}
204 | */
205 | public function sort($flags = SORT_REGULAR)
206 | {
207 | ksort($this->array, $flags);
208 | }
209 |
210 | /**
211 | * {@inheritdoc}
212 | */
213 | public function count()
214 | {
215 | return count($this->array);
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/src/DbalStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Doctrine\DBAL\Schema\Schema;
16 | use Doctrine\DBAL\Schema\Table;
17 | use Exception;
18 | use PDO;
19 | use Webmozart\Assert\Assert;
20 | use Webmozart\KeyValueStore\Api\KeyValueStore;
21 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
22 | use Webmozart\KeyValueStore\Api\ReadException;
23 | use Webmozart\KeyValueStore\Api\WriteException;
24 | use Webmozart\KeyValueStore\Util\KeyUtil;
25 | use Webmozart\KeyValueStore\Util\Serializer;
26 |
27 | /**
28 | * A key-value store backed by Doctrine DBAL.
29 | *
30 | * @since 1.0
31 | *
32 | * @author Bernhard Schussek
33 | * @author Michiel Boeckaert
34 | */
35 | class DbalStore implements KeyValueStore
36 | {
37 | private $connection;
38 | private $tableName;
39 |
40 | /**
41 | * @param Connection $connection A doctrine connection instance
42 | * @param string $tableName The name of the database table
43 | */
44 | public function __construct(Connection $connection, $tableName = 'store')
45 | {
46 | Assert::stringNotEmpty($tableName, 'The table must be a string. Got: %s');
47 |
48 | $this->connection = $connection;
49 | $this->tableName = $tableName;
50 | }
51 |
52 | /**
53 | * {@inheritdoc}
54 | */
55 | public function set($key, $value)
56 | {
57 | KeyUtil::validate($key);
58 |
59 | try {
60 | $existing = $this->exists($key);
61 | } catch (Exception $e) {
62 | throw WriteException::forException($e);
63 | }
64 |
65 | if (false === $existing) {
66 | $this->doInsert($key, $value);
67 | } else {
68 | $this->doUpdate($key, $value);
69 | }
70 | }
71 |
72 | /**
73 | * {@inheritdoc}
74 | */
75 | public function get($key, $default = null)
76 | {
77 | KeyUtil::validate($key);
78 |
79 | $dbResult = $this->getDbRow($key);
80 |
81 | if (null === $dbResult) {
82 | return $default;
83 | }
84 |
85 | return Serializer::unserialize($dbResult['meta_value']);
86 | }
87 |
88 | /**
89 | * {@inheritdoc}
90 | */
91 | public function getOrFail($key)
92 | {
93 | KeyUtil::validate($key);
94 |
95 | $dbResult = $this->getDbRow($key);
96 |
97 | if (null === $dbResult) {
98 | throw NoSuchKeyException::forKey($key);
99 | }
100 |
101 | return Serializer::unserialize($dbResult['meta_value']);
102 | }
103 |
104 | /**
105 | * {@inheritdoc}
106 | */
107 | public function getMultiple(array $keys, $default = null)
108 | {
109 | KeyUtil::validateMultiple($keys);
110 |
111 | // Normalize indices of the array
112 | $keys = array_values($keys);
113 | $data = $this->doGetMultiple($keys);
114 |
115 | $results = array();
116 | $resolved = array();
117 | foreach ($data as $row) {
118 | $results[$row['meta_key']] = Serializer::unserialize($row['meta_value']);
119 | $resolved[$row['meta_key']] = $row['meta_key'];
120 | }
121 |
122 | $notResolvedArr = array_diff($keys, $resolved);
123 | foreach ($notResolvedArr as $notResolved) {
124 | $results[$notResolved] = $default;
125 | }
126 |
127 | return $results;
128 | }
129 |
130 | /**
131 | * {@inheritdoc}
132 | */
133 | public function getMultipleOrFail(array $keys)
134 | {
135 | KeyUtil::validateMultiple($keys);
136 |
137 | // Normalize indices of the array
138 | $keys = array_values($keys);
139 |
140 | $data = $this->doGetMultiple($keys);
141 |
142 | $results = array();
143 | $resolved = array();
144 | foreach ($data as $row) {
145 | $results[$row['meta_key']] = Serializer::unserialize($row['meta_value']);
146 | $resolved[] = $row['meta_key'];
147 | }
148 |
149 | $notResolvedArr = array_diff($keys, $resolved);
150 |
151 | if (!empty($notResolvedArr)) {
152 | throw NoSuchKeyException::forKeys($notResolvedArr);
153 | }
154 |
155 | return $results;
156 | }
157 |
158 | /**
159 | * {@inheritdoc}
160 | */
161 | public function remove($key)
162 | {
163 | KeyUtil::validate($key);
164 |
165 | try {
166 | $result = $this->connection->delete($this->tableName, array('meta_key' => $key));
167 | } catch (Exception $e) {
168 | throw WriteException::forException($e);
169 | }
170 |
171 | return $result === 1;
172 | }
173 |
174 | /**
175 | * {@inheritdoc}
176 | */
177 | public function exists($key)
178 | {
179 | KeyUtil::validate($key);
180 |
181 | try {
182 | $result = $this->connection->fetchAssoc('SELECT * FROM '.$this->tableName.' WHERE meta_key = ?', array($key));
183 | } catch (Exception $e) {
184 | throw ReadException::forException($e);
185 | }
186 |
187 | return $result ? true : false;
188 | }
189 |
190 | /**
191 | * {@inheritdoc}
192 | */
193 | public function clear()
194 | {
195 | try {
196 | $stmt = $this->connection->query('DELETE FROM '.$this->tableName);
197 | $stmt->execute();
198 | } catch (Exception $e) {
199 | throw WriteException::forException($e);
200 | }
201 | }
202 |
203 | /**
204 | * {@inheritdoc}
205 | */
206 | public function keys()
207 | {
208 | try {
209 | $stmt = $this->connection->query('SELECT meta_key FROM '.$this->tableName);
210 | $result = $stmt->fetchAll(PDO::FETCH_COLUMN);
211 | } catch (Exception $e) {
212 | throw ReadException::forException($e);
213 | }
214 |
215 | return $result;
216 | }
217 |
218 | /**
219 | * The name for our DBAL database table.
220 | *
221 | * @return string
222 | */
223 | public function getTableName()
224 | {
225 | return $this->tableName;
226 | }
227 |
228 | /**
229 | * Object Representation of the table used in this class.
230 | *
231 | * @return Table
232 | */
233 | public function getTableForCreate()
234 | {
235 | $schema = new Schema();
236 |
237 | $table = $schema->createTable($this->getTableName());
238 |
239 | $table->addColumn('id', 'integer', array('autoincrement' => true));
240 | $table->addColumn('meta_key', 'string', array('length' => 255));
241 | $table->addColumn('meta_value', 'object');
242 | $table->setPrimaryKey(array('id'));
243 | $table->addUniqueIndex(array('meta_key'));
244 |
245 | return $table;
246 | }
247 |
248 | private function doInsert($key, $value)
249 | {
250 | $serialized = Serializer::serialize($value);
251 |
252 | try {
253 | $this->connection->insert($this->tableName, array(
254 | 'meta_key' => $key,
255 | 'meta_value' => $serialized,
256 | ));
257 | } catch (Exception $e) {
258 | throw WriteException::forException($e);
259 | }
260 | }
261 |
262 | private function doUpdate($key, $value)
263 | {
264 | $serialized = Serializer::serialize($value);
265 |
266 | try {
267 | $this->connection->update($this->tableName, array(
268 | 'meta_value' => $serialized,
269 | ), array(
270 | 'meta_key' => $key,
271 | ));
272 | } catch (Exception $e) {
273 | throw WriteException::forException($e);
274 | }
275 | }
276 |
277 | private function doGetMultiple(array $keys)
278 | {
279 | try {
280 | $stmt = $this->connection->executeQuery('SELECT * FROM '.$this->tableName.' WHERE meta_key IN (?)',
281 | array($keys),
282 | array(Connection::PARAM_STR_ARRAY)
283 | );
284 | $data = $stmt->fetchAll();
285 | } catch (Exception $e) {
286 | throw ReadException::forException($e);
287 | }
288 |
289 | return is_array($data) ? $data : array();
290 | }
291 |
292 | private function getDbRow($key)
293 | {
294 | try {
295 | $dbResult = $this->connection->fetchAssoc('SELECT meta_value, meta_key FROM '.$this->tableName.' WHERE meta_key = ?', array($key));
296 | } catch (Exception $e) {
297 | throw ReadException::forException($e);
298 | }
299 |
300 | if (empty($dbResult)) {
301 | return null;
302 | }
303 |
304 | return $dbResult;
305 | }
306 | }
307 |
--------------------------------------------------------------------------------
/src/Decorator/AbstractDecorator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Decorator;
13 |
14 | use Webmozart\KeyValueStore\Api\KeyValueStore;
15 |
16 | /**
17 | * A delegating decorator delegate each call of a KeyValueStore method
18 | * to the internal store.
19 | *
20 | * It is used by decorators that need to override only a few specific
21 | * methods (such as SortableDecorator or CountableDecorator).
22 | *
23 | * @since 1.0
24 | *
25 | * @author Bernhard Schussek
26 | * @author Titouan Galopin
27 | */
28 | class AbstractDecorator implements KeyValueStore
29 | {
30 | /**
31 | * @var KeyValueStore
32 | */
33 | protected $store;
34 |
35 | /**
36 | * Creates the store.
37 | *
38 | * @param KeyValueStore $store The store to sort.
39 | */
40 | public function __construct(KeyValueStore $store)
41 | {
42 | $this->store = $store;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function set($key, $value)
49 | {
50 | $this->store->set($key, $value);
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function get($key, $default = null)
57 | {
58 | return $this->store->get($key, $default);
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | */
64 | public function getOrFail($key)
65 | {
66 | return $this->store->getOrFail($key);
67 | }
68 |
69 | /**
70 | * {@inheritdoc}
71 | */
72 | public function getMultiple(array $keys, $default = null)
73 | {
74 | return $this->store->getMultiple($keys, $default);
75 | }
76 |
77 | /**
78 | * {@inheritdoc}
79 | */
80 | public function getMultipleOrFail(array $keys)
81 | {
82 | return $this->store->getMultipleOrFail($keys);
83 | }
84 |
85 | /**
86 | * {@inheritdoc}
87 | */
88 | public function remove($key)
89 | {
90 | $this->store->remove($key);
91 | }
92 |
93 | /**
94 | * {@inheritdoc}
95 | */
96 | public function exists($key)
97 | {
98 | return $this->store->exists($key);
99 | }
100 |
101 | /**
102 | * {@inheritdoc}
103 | */
104 | public function clear()
105 | {
106 | $this->store->clear();
107 | }
108 |
109 | /**
110 | * {@inheritdoc}
111 | */
112 | public function keys()
113 | {
114 | return $this->store->keys();
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/Decorator/CachingDecorator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Decorator;
13 |
14 | use Doctrine\Common\Cache\Cache;
15 | use Doctrine\Common\Cache\ClearableCache;
16 | use Doctrine\Common\Cache\FlushableCache;
17 | use InvalidArgumentException;
18 | use Webmozart\KeyValueStore\Api\KeyValueStore;
19 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
20 |
21 | /**
22 | * A caching decorator implementing a cache layer for any store.
23 | *
24 | * @since 1.0
25 | *
26 | * @author Bernhard Schussek
27 | */
28 | class CachingDecorator extends AbstractDecorator
29 | {
30 | /**
31 | * @var Cache
32 | */
33 | private $cache;
34 |
35 | private $ttl;
36 |
37 | /**
38 | * Creates the store.
39 | *
40 | * @param KeyValueStore $store The cached store.
41 | * @param Cache $cache The cache.
42 | * @param int $ttl The time-to-live for cache entries. If set to
43 | * 0, cache entries never expire.
44 | *
45 | * @throws InvalidArgumentException If the provided cache is not supported
46 | */
47 | public function __construct(KeyValueStore $store, Cache $cache, $ttl = 0)
48 | {
49 | if (!$cache instanceof ClearableCache && !$cache instanceof FlushableCache) {
50 | throw new InvalidArgumentException(sprintf(
51 | 'The cache must either implement ClearableCache or '.
52 | 'FlushableCache. Got: %s',
53 | get_class($cache)
54 | ));
55 | }
56 |
57 | parent::__construct($store);
58 |
59 | $this->cache = $cache;
60 | $this->ttl = $ttl;
61 | }
62 |
63 | /**
64 | * {@inheritdoc}
65 | */
66 | public function set($key, $value)
67 | {
68 | $this->store->set($key, $value);
69 | $this->cache->save($key, $value, $this->ttl);
70 | }
71 |
72 | /**
73 | * {@inheritdoc}
74 | */
75 | public function get($key, $default = null)
76 | {
77 | if ($this->cache->contains($key)) {
78 | return $this->cache->fetch($key);
79 | }
80 |
81 | try {
82 | $value = $this->store->getOrFail($key);
83 | } catch (NoSuchKeyException $e) {
84 | return $default;
85 | }
86 |
87 | $this->cache->save($key, $value, $this->ttl);
88 |
89 | return $value;
90 | }
91 |
92 | /**
93 | * {@inheritdoc}
94 | */
95 | public function getOrFail($key)
96 | {
97 | if ($this->cache->contains($key)) {
98 | return $this->cache->fetch($key);
99 | }
100 |
101 | $value = $this->store->getOrFail($key);
102 |
103 | $this->cache->save($key, $value, $this->ttl);
104 |
105 | return $value;
106 | }
107 |
108 | /**
109 | * {@inheritdoc}
110 | */
111 | public function getMultiple(array $keys, $default = null)
112 | {
113 | $values = array();
114 |
115 | // Read cached values from the cache
116 | foreach ($keys as $i => $key) {
117 | if ($this->cache->contains($key)) {
118 | $values[$key] = $this->cache->fetch($key);
119 | unset($keys[$i]);
120 | }
121 | }
122 |
123 | // Don't write cache, as we can't differentiate between existing and
124 | // non-existing keys
125 | return array_replace($values, $this->store->getMultiple($keys, $default));
126 | }
127 |
128 | /**
129 | * {@inheritdoc}
130 | */
131 | public function getMultipleOrFail(array $keys)
132 | {
133 | $values = array();
134 |
135 | // Read cached values from the cache
136 | foreach ($keys as $i => $key) {
137 | if ($this->cache->contains($key)) {
138 | $values[$key] = $this->cache->fetch($key);
139 | unset($keys[$i]);
140 | }
141 | }
142 |
143 | $values = array_replace($values, $this->store->getMultipleOrFail($keys));
144 |
145 | // Write newly fetched values to the cache
146 | foreach ($keys as $key) {
147 | $this->cache->save($key, $values[$key], $this->ttl);
148 | }
149 |
150 | return $values;
151 | }
152 |
153 | /**
154 | * {@inheritdoc}
155 | */
156 | public function remove($key)
157 | {
158 | $this->store->remove($key);
159 | $this->cache->delete($key);
160 | }
161 |
162 | /**
163 | * {@inheritdoc}
164 | */
165 | public function exists($key)
166 | {
167 | if ($this->cache->contains($key)) {
168 | return true;
169 | }
170 |
171 | return $this->store->exists($key);
172 | }
173 |
174 | /**
175 | * {@inheritdoc}
176 | */
177 | public function clear()
178 | {
179 | $this->store->clear();
180 |
181 | if ($this->cache instanceof ClearableCache) {
182 | $this->cache->deleteAll();
183 | } else {
184 | $this->cache->flushAll();
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/src/Decorator/CountableDecorator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Decorator;
13 |
14 | use Webmozart\KeyValueStore\Api\CountableStore;
15 |
16 | /**
17 | * A countable decorator implementing a count system for any store.
18 | *
19 | * @since 1.0
20 | *
21 | * @author Bernhard Schussek
22 | * @author Titouan Galopin
23 | */
24 | class CountableDecorator extends AbstractDecorator implements CountableStore
25 | {
26 | /**
27 | * In-memory cache invalidated on store modification.
28 | *
29 | * @var int
30 | */
31 | private $cache;
32 |
33 | /**
34 | * Is the cache fresh enough to be served?
35 | *
36 | * @var bool
37 | */
38 | private $cacheIsFresh = false;
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | public function set($key, $value)
44 | {
45 | $this->cacheIsFresh = false;
46 | $this->store->set($key, $value);
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | */
52 | public function remove($key)
53 | {
54 | $this->cacheIsFresh = false;
55 | $this->store->remove($key);
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | public function clear()
62 | {
63 | $this->cacheIsFresh = false;
64 | $this->store->clear();
65 | }
66 |
67 | /**
68 | * {@inheritdoc}
69 | */
70 | public function count()
71 | {
72 | if (!$this->cacheIsFresh) {
73 | $this->cache = count($this->store->keys());
74 | $this->cacheIsFresh = true;
75 | }
76 |
77 | return $this->cache;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Decorator/SortableDecorator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Decorator;
13 |
14 | use Webmozart\KeyValueStore\Api\SortableStore;
15 |
16 | /**
17 | * A sortable decorator implementing a sort system for any store.
18 | *
19 | * @since 1.0
20 | *
21 | * @author Bernhard Schussek
22 | * @author Titouan Galopin
23 | */
24 | class SortableDecorator extends AbstractDecorator implements SortableStore
25 | {
26 | /**
27 | * @var int
28 | */
29 | private $flags;
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function sort($flags = SORT_REGULAR)
35 | {
36 | $this->flags = $flags;
37 | }
38 |
39 | /**
40 | * {@inheritdoc}
41 | */
42 | public function set($key, $value)
43 | {
44 | $this->flags = null;
45 | $this->store->set($key, $value);
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | public function keys()
52 | {
53 | $keys = $this->store->keys();
54 |
55 | if (null !== $this->flags) {
56 | sort($keys, $this->flags);
57 | }
58 |
59 | return $keys;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/JsonFileStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use stdClass;
15 | use Webmozart\Assert\Assert;
16 | use Webmozart\Json\DecodingFailedException;
17 | use Webmozart\Json\EncodingFailedException;
18 | use Webmozart\Json\FileNotFoundException;
19 | use Webmozart\Json\IOException;
20 | use Webmozart\Json\JsonDecoder;
21 | use Webmozart\Json\JsonEncoder;
22 | use Webmozart\KeyValueStore\Api\CountableStore;
23 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
24 | use Webmozart\KeyValueStore\Api\ReadException;
25 | use Webmozart\KeyValueStore\Api\SortableStore;
26 | use Webmozart\KeyValueStore\Api\UnsupportedValueException;
27 | use Webmozart\KeyValueStore\Api\WriteException;
28 | use Webmozart\KeyValueStore\Util\KeyUtil;
29 | use Webmozart\KeyValueStore\Util\Serializer;
30 |
31 | /**
32 | * A key-value store backed by a JSON file.
33 | *
34 | * @since 1.0
35 | *
36 | * @author Bernhard Schussek
37 | */
38 | class JsonFileStore implements SortableStore, CountableStore
39 | {
40 | /**
41 | * Flag: Disable serialization of strings.
42 | */
43 | const NO_SERIALIZE_STRINGS = 1;
44 |
45 | /**
46 | * Flag: Disable serialization of arrays.
47 | */
48 | const NO_SERIALIZE_ARRAYS = 2;
49 |
50 | /**
51 | * Flag: Escape ">" and "<".
52 | */
53 | const ESCAPE_GT_LT = 4;
54 |
55 | /**
56 | * Flag: Escape "&".
57 | */
58 | const ESCAPE_AMPERSAND = 8;
59 |
60 | /**
61 | * Flag: Escape single quotes.
62 | */
63 | const ESCAPE_SINGLE_QUOTE = 16;
64 |
65 | /**
66 | * Flag: Escape double quotes.
67 | */
68 | const ESCAPE_DOUBLE_QUOTE = 32;
69 |
70 | /**
71 | * Flag: Don't escape forward slashes.
72 | */
73 | const NO_ESCAPE_SLASH = 64;
74 |
75 | /**
76 | * Flag: Don't escape Unicode characters.
77 | */
78 | const NO_ESCAPE_UNICODE = 128;
79 |
80 | /**
81 | * Flag: Format the JSON nicely.
82 | */
83 | const PRETTY_PRINT = 256;
84 |
85 | /**
86 | * Flag: Terminate the JSON with a line feed.
87 | */
88 | const TERMINATE_WITH_LINE_FEED = 512;
89 |
90 | /**
91 | * This seems to be the biggest float supported by json_encode()/json_decode().
92 | */
93 | const MAX_FLOAT = 1.0E+14;
94 |
95 | /**
96 | * @var string
97 | */
98 | private $path;
99 |
100 | /**
101 | * @var int
102 | */
103 | private $flags;
104 |
105 | /**
106 | * @var JsonEncoder
107 | */
108 | private $encoder;
109 |
110 | /**
111 | * @var JsonDecoder
112 | */
113 | private $decoder;
114 |
115 | public function __construct($path, $flags = 0)
116 | {
117 | Assert::string($path, 'The path must be a string. Got: %s');
118 | Assert::notEmpty($path, 'The path must not be empty.');
119 | Assert::integer($flags, 'The flags must be an integer. Got: %s');
120 |
121 | $this->path = $path;
122 | $this->flags = $flags;
123 |
124 | $this->encoder = new JsonEncoder();
125 | $this->encoder->setEscapeGtLt($this->flags & self::ESCAPE_GT_LT);
126 | $this->encoder->setEscapeAmpersand($this->flags & self::ESCAPE_AMPERSAND);
127 | $this->encoder->setEscapeSingleQuote($this->flags & self::ESCAPE_SINGLE_QUOTE);
128 | $this->encoder->setEscapeDoubleQuote($this->flags & self::ESCAPE_DOUBLE_QUOTE);
129 | $this->encoder->setEscapeSlash(!($this->flags & self::NO_ESCAPE_SLASH));
130 | $this->encoder->setEscapeUnicode(!($this->flags & self::NO_ESCAPE_UNICODE));
131 | $this->encoder->setPrettyPrinting($this->flags & self::PRETTY_PRINT);
132 | $this->encoder->setTerminateWithLineFeed($this->flags & self::TERMINATE_WITH_LINE_FEED);
133 |
134 | $this->decoder = new JsonDecoder();
135 | $this->decoder->setObjectDecoding(JsonDecoder::ASSOC_ARRAY);
136 | }
137 |
138 | /**
139 | * {@inheritdoc}
140 | */
141 | public function set($key, $value)
142 | {
143 | KeyUtil::validate($key);
144 |
145 | if (is_float($value) && $value > self::MAX_FLOAT) {
146 | throw new UnsupportedValueException('The JSON file store cannot handle floats larger than 1.0E+14.');
147 | }
148 |
149 | $data = $this->load();
150 | $data[$key] = $this->serializeValue($value);
151 |
152 | $this->save($data);
153 | }
154 |
155 | /**
156 | * {@inheritdoc}
157 | */
158 | public function get($key, $default = null)
159 | {
160 | KeyUtil::validate($key);
161 |
162 | $data = $this->load();
163 |
164 | if (!array_key_exists($key, $data)) {
165 | return $default;
166 | }
167 |
168 | return $this->unserializeValue($data[$key]);
169 | }
170 |
171 | /**
172 | * {@inheritdoc}
173 | */
174 | public function getOrFail($key)
175 | {
176 | KeyUtil::validate($key);
177 |
178 | $data = $this->load();
179 |
180 | if (!array_key_exists($key, $data)) {
181 | throw NoSuchKeyException::forKey($key);
182 | }
183 |
184 | return $this->unserializeValue($data[$key]);
185 | }
186 |
187 | /**
188 | * {@inheritdoc}
189 | */
190 | public function getMultiple(array $keys, $default = null)
191 | {
192 | $values = array();
193 | $data = $this->load();
194 |
195 | foreach ($keys as $key) {
196 | KeyUtil::validate($key);
197 |
198 | if (array_key_exists($key, $data)) {
199 | $value = $this->unserializeValue($data[$key]);
200 | } else {
201 | $value = $default;
202 | }
203 |
204 | $values[$key] = $value;
205 | }
206 |
207 | return $values;
208 | }
209 |
210 | /**
211 | * {@inheritdoc}
212 | */
213 | public function getMultipleOrFail(array $keys)
214 | {
215 | $values = array();
216 | $data = $this->load();
217 |
218 | foreach ($keys as $key) {
219 | KeyUtil::validate($key);
220 |
221 | if (!array_key_exists($key, $data)) {
222 | throw NoSuchKeyException::forKey($key);
223 | }
224 |
225 | $values[$key] = $this->unserializeValue($data[$key]);
226 | }
227 |
228 | return $values;
229 | }
230 |
231 | /**
232 | * {@inheritdoc}
233 | */
234 | public function remove($key)
235 | {
236 | KeyUtil::validate($key);
237 |
238 | $data = $this->load();
239 |
240 | if (!array_key_exists($key, $data)) {
241 | return false;
242 | }
243 |
244 | unset($data[$key]);
245 |
246 | $this->save($data);
247 |
248 | return true;
249 | }
250 |
251 | /**
252 | * {@inheritdoc}
253 | */
254 | public function exists($key)
255 | {
256 | KeyUtil::validate($key);
257 |
258 | $data = $this->load();
259 |
260 | return array_key_exists($key, $data);
261 | }
262 |
263 | /**
264 | * {@inheritdoc}
265 | */
266 | public function clear()
267 | {
268 | $this->save(new stdClass());
269 | }
270 |
271 | /**
272 | * {@inheritdoc}
273 | */
274 | public function keys()
275 | {
276 | return array_keys($this->load());
277 | }
278 |
279 | /**
280 | * {@inheritdoc}
281 | */
282 | public function sort($flags = SORT_REGULAR)
283 | {
284 | $data = $this->load();
285 |
286 | ksort($data, $flags);
287 |
288 | $this->save($data);
289 | }
290 |
291 | /**
292 | * {@inheritdoc}
293 | */
294 | public function count()
295 | {
296 | $data = $this->load();
297 |
298 | return count($data);
299 | }
300 |
301 | private function load()
302 | {
303 | try {
304 | return $this->decoder->decodeFile($this->path);
305 | } catch (FileNotFoundException $e) {
306 | return array();
307 | } catch (DecodingFailedException $e) {
308 | throw new ReadException($e->getMessage(), 0, $e);
309 | } catch (IOException $e) {
310 | throw new ReadException($e->getMessage(), 0, $e);
311 | }
312 | }
313 |
314 | private function save($data)
315 | {
316 | try {
317 | $this->encoder->encodeFile($data, $this->path);
318 | } catch (EncodingFailedException $e) {
319 | if (JSON_ERROR_UTF8 === $e->getCode()) {
320 | throw UnsupportedValueException::forType('binary', $this);
321 | }
322 |
323 | throw new WriteException($e->getMessage(), 0, $e);
324 | } catch (IOException $e) {
325 | throw new WriteException($e->getMessage(), 0, $e);
326 | }
327 | }
328 |
329 | private function serializeValue($value)
330 | {
331 | // Serialize if we have a string and string serialization is enabled...
332 | $serializeValue = (is_string($value) && !($this->flags & self::NO_SERIALIZE_STRINGS))
333 | // or we have an array and array serialization is enabled...
334 | || (is_array($value) && !($this->flags & self::NO_SERIALIZE_ARRAYS))
335 | // or we have any other non-scalar, non-null value
336 | || (null !== $value && !is_scalar($value) && !is_array($value));
337 |
338 | if ($serializeValue) {
339 | return Serializer::serialize($value);
340 | }
341 |
342 | // If we have an array and array serialization is disabled, serialize
343 | // its entries if necessary
344 | if (is_array($value)) {
345 | return array_map(array($this, 'serializeValue'), $value);
346 | }
347 |
348 | return $value;
349 | }
350 |
351 | private function unserializeValue($value)
352 | {
353 | // Unserialize value if it is a string...
354 | $unserializeValue = is_string($value) && (
355 | // and string serialization is enabled
356 | !($this->flags & self::NO_SERIALIZE_STRINGS)
357 | // or the string contains a serialized object
358 | || 'O:' === ($prefix = substr($value, 0, 2))
359 | // or the string contains a serialized array when array
360 | // serialization is enabled
361 | || ('a:' === $prefix && !($this->flags & self::NO_SERIALIZE_ARRAYS))
362 | );
363 |
364 | if ($unserializeValue) {
365 | return Serializer::unserialize($value);
366 | }
367 |
368 | if (is_array($value)) {
369 | return array_map(array($this, 'unserializeValue'), $value);
370 | }
371 |
372 | return $value;
373 | }
374 | }
375 |
--------------------------------------------------------------------------------
/src/MongoDbStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Closure;
15 | use Exception;
16 | use MongoDB\BSON\Binary;
17 | use MongoDB\Collection;
18 | use MongoDB\Driver\Exception\UnexpectedValueException;
19 | use Webmozart\KeyValueStore\Api\KeyValueStore;
20 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
21 | use Webmozart\KeyValueStore\Api\ReadException;
22 | use Webmozart\KeyValueStore\Api\UnserializationFailedException;
23 | use Webmozart\KeyValueStore\Api\UnsupportedValueException;
24 | use Webmozart\KeyValueStore\Api\WriteException;
25 | use Webmozart\KeyValueStore\Util\KeyUtil;
26 | use Webmozart\KeyValueStore\Util\Serializer;
27 |
28 | /**
29 | * A key-value-store backed by MongoDB.
30 | *
31 | * @since 1.0
32 | *
33 | * @author Bernhard Schussek
34 | */
35 | class MongoDbStore implements KeyValueStore
36 | {
37 | /**
38 | * Flag: Disable serialization.
39 | */
40 | const NO_SERIALIZE = 1;
41 |
42 | /**
43 | * Flag: Support storage of binary data.
44 | */
45 | const SUPPORT_BINARY = 2;
46 |
47 | private static $typeMap = array(
48 | 'root' => 'array',
49 | 'document' => 'array',
50 | 'array' => 'array',
51 | );
52 |
53 | /**
54 | * @var Collection
55 | */
56 | private $collection;
57 |
58 | /**
59 | * @var Closure
60 | */
61 | private $serialize;
62 |
63 | /**
64 | * @var Closure
65 | */
66 | private $unserialize;
67 |
68 | public function __construct(Collection $collection, $flags = 0)
69 | {
70 | $this->collection = $collection;
71 |
72 | if ($flags & self::NO_SERIALIZE) {
73 | if ($flags & self::SUPPORT_BINARY) {
74 | $this->serialize = function ($unserialized) {
75 | if (!is_string($unserialized)) {
76 | throw UnsupportedValueException::forValue($unserialized, $this);
77 | }
78 |
79 | return new Binary($unserialized, Binary::TYPE_GENERIC);
80 | };
81 | $this->unserialize = function (Binary $serialized) {
82 | return $serialized->getData();
83 | };
84 | } else {
85 | $this->serialize = function ($unserialized) {
86 | if (!is_scalar($unserialized) && !is_array($unserialized) && null !== $unserialized) {
87 | throw UnsupportedValueException::forValue($unserialized, $this);
88 | }
89 |
90 | return $unserialized;
91 | };
92 | $this->unserialize = function ($serialized) {
93 | return $serialized;
94 | };
95 | }
96 | } else {
97 | if ($flags & self::SUPPORT_BINARY) {
98 | $this->serialize = function ($unserialized) {
99 | return new Binary(
100 | Serializer::serialize($unserialized),
101 | Binary::TYPE_GENERIC
102 | );
103 | };
104 | $this->unserialize = function (Binary $serialized) {
105 | return Serializer::unserialize($serialized->getData());
106 | };
107 | } else {
108 | $this->serialize = function ($unserialized) {
109 | return Serializer::serialize($unserialized);
110 | };
111 | $this->unserialize = function ($serialized) {
112 | return Serializer::unserialize($serialized);
113 | };
114 | }
115 | }
116 | }
117 |
118 | /**
119 | * {@inheritdoc}
120 | */
121 | public function set($key, $value)
122 | {
123 | KeyUtil::validate($key);
124 |
125 | $serialized = $this->serialize->__invoke($value);
126 |
127 | try {
128 | $this->collection->replaceOne(
129 | array('_id' => $key),
130 | array('_id' => $key, 'value' => $serialized),
131 | array('upsert' => true)
132 | );
133 | } catch (UnexpectedValueException $e) {
134 | throw UnsupportedValueException::forType('binary', $this, 0, $e);
135 | } catch (Exception $e) {
136 | throw WriteException::forException($e);
137 | }
138 | }
139 |
140 | /**
141 | * {@inheritdoc}
142 | */
143 | public function get($key, $default = null)
144 | {
145 | KeyUtil::validate($key);
146 |
147 | try {
148 | $document = $this->collection->findOne(
149 | array('_id' => $key),
150 | array('typeMap' => self::$typeMap)
151 | );
152 | } catch (Exception $e) {
153 | throw ReadException::forException($e);
154 | }
155 |
156 | if (null === $document) {
157 | return $default;
158 | }
159 |
160 | return $this->unserialize->__invoke($document['value']);
161 | }
162 |
163 | /**
164 | * {@inheritdoc}
165 | */
166 | public function getOrFail($key)
167 | {
168 | KeyUtil::validate($key);
169 |
170 | try {
171 | $document = $this->collection->findOne(
172 | array('_id' => $key),
173 | array('typeMap' => self::$typeMap)
174 | );
175 | } catch (Exception $e) {
176 | throw ReadException::forException($e);
177 | }
178 |
179 | if (null === $document) {
180 | throw NoSuchKeyException::forKey($key);
181 | }
182 |
183 | return $this->unserialize->__invoke($document['value']);
184 | }
185 |
186 | /**
187 | * {@inheritdoc}
188 | */
189 | public function getMultiple(array $keys, $default = null)
190 | {
191 | KeyUtil::validateMultiple($keys);
192 |
193 | $values = array_fill_keys($keys, $default);
194 |
195 | try {
196 | $cursor = $this->collection->find(
197 | array('_id' => array('$in' => array_values($keys))),
198 | array('typeMap' => self::$typeMap)
199 | );
200 |
201 | foreach ($cursor as $document) {
202 | $values[$document['_id']] = $this->unserialize->__invoke($document['value']);
203 | }
204 | } catch (UnserializationFailedException $e) {
205 | throw $e;
206 | } catch (Exception $e) {
207 | throw ReadException::forException($e);
208 | }
209 |
210 | return $values;
211 | }
212 |
213 | /**
214 | * {@inheritdoc}
215 | */
216 | public function getMultipleOrFail(array $keys)
217 | {
218 | KeyUtil::validateMultiple($keys);
219 |
220 | $values = array();
221 |
222 | try {
223 | $cursor = $this->collection->find(
224 | array('_id' => array('$in' => array_values($keys))),
225 | array('typeMap' => self::$typeMap)
226 | );
227 |
228 | foreach ($cursor as $document) {
229 | $values[$document['_id']] = $this->unserialize->__invoke($document['value']);
230 | }
231 | } catch (UnserializationFailedException $e) {
232 | throw $e;
233 | } catch (Exception $e) {
234 | throw ReadException::forException($e);
235 | }
236 |
237 | $notFoundKeys = array_diff($keys, array_keys($values));
238 |
239 | if (count($notFoundKeys) > 0) {
240 | throw NoSuchKeyException::forKeys($notFoundKeys);
241 | }
242 |
243 | return $values;
244 | }
245 |
246 | /**
247 | * {@inheritdoc}
248 | */
249 | public function remove($key)
250 | {
251 | KeyUtil::validate($key);
252 |
253 | try {
254 | $result = $this->collection->deleteOne(array('_id' => $key));
255 | $deletedCount = $result->getDeletedCount();
256 | } catch (Exception $e) {
257 | throw WriteException::forException($e);
258 | }
259 |
260 | return $deletedCount > 0;
261 | }
262 |
263 | /**
264 | * {@inheritdoc}
265 | */
266 | public function exists($key)
267 | {
268 | KeyUtil::validate($key);
269 |
270 | try {
271 | $count = $this->collection->count(array('_id' => $key));
272 | } catch (Exception $e) {
273 | throw ReadException::forException($e);
274 | }
275 |
276 | return $count > 0;
277 | }
278 |
279 | /**
280 | * {@inheritdoc}
281 | */
282 | public function clear()
283 | {
284 | try {
285 | $this->collection->drop();
286 | } catch (Exception $e) {
287 | throw WriteException::forException($e);
288 | }
289 | }
290 |
291 | /**
292 | * {@inheritdoc}
293 | */
294 | public function keys()
295 | {
296 | try {
297 | $cursor = $this->collection->find(array(), array(
298 | 'projection' => array('_id' => 1),
299 | ));
300 |
301 | $keys = array();
302 |
303 | foreach ($cursor as $document) {
304 | $keys[] = $document['_id'];
305 | }
306 | } catch (Exception $e) {
307 | throw ReadException::forException($e);
308 | }
309 |
310 | return $keys;
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/src/NullStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Webmozart\KeyValueStore\Api\CountableStore;
15 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
16 | use Webmozart\KeyValueStore\Api\SortableStore;
17 |
18 | /**
19 | * A key-value store that does nothing.
20 | *
21 | * @since 1.0
22 | *
23 | * @author Bernhard Schussek
24 | */
25 | class NullStore implements SortableStore, CountableStore
26 | {
27 | /**
28 | * {@inheritdoc}
29 | */
30 | public function set($key, $value)
31 | {
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function get($key, $default = null)
38 | {
39 | return $default;
40 | }
41 |
42 | /**
43 | * {@inheritdoc}
44 | */
45 | public function getOrFail($key)
46 | {
47 | throw NoSuchKeyException::forKey($key);
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function getMultiple(array $keys, $default = null)
54 | {
55 | return array_fill_keys($keys, $default);
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | public function getMultipleOrFail(array $keys)
62 | {
63 | throw NoSuchKeyException::forKeys($keys);
64 | }
65 |
66 | /**
67 | * {@inheritdoc}
68 | */
69 | public function remove($key)
70 | {
71 | return false;
72 | }
73 |
74 | /**
75 | * {@inheritdoc}
76 | */
77 | public function exists($key)
78 | {
79 | return false;
80 | }
81 |
82 | /**
83 | * {@inheritdoc}
84 | */
85 | public function clear()
86 | {
87 | }
88 |
89 | /**
90 | * {@inheritdoc}
91 | */
92 | public function keys()
93 | {
94 | return array();
95 | }
96 |
97 | /**
98 | * {@inheritdoc}
99 | */
100 | public function sort($flags = SORT_REGULAR)
101 | {
102 | }
103 |
104 | /**
105 | * {@inheritdoc}
106 | */
107 | public function count()
108 | {
109 | return 0;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/PhpRedisStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Redis;
15 |
16 | /**
17 | * A key-value store that uses the PhpRedis extension to connect to a Redis instance.
18 | *
19 | * @since 1.0
20 | *
21 | * @author Bernhard Schussek
22 | * @author Philipp Wahala
23 | * @author Titouan Galopin
24 | *
25 | * @link https://github.com/phpredis/phpredis
26 | */
27 | class PhpRedisStore extends AbstractRedisStore
28 | {
29 | /**
30 | * Creates a store backed by a PhpRedis client.
31 | *
32 | * If no client is passed, a new one is created using the default server
33 | * "127.0.0.1" and the default port 6379.
34 | *
35 | * @param Redis|null $client The client used to connect to Redis.
36 | */
37 | public function __construct(Redis $client = null)
38 | {
39 | if (null === $client) {
40 | $client = new Redis();
41 | $client->connect('127.0.0.1', 6379);
42 | }
43 |
44 | $this->client = $client;
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | protected function clientNotFoundValue()
51 | {
52 | return false;
53 | }
54 |
55 | /**
56 | * {@inheritdoc}
57 | */
58 | protected function clientGet($key)
59 | {
60 | return $this->client->get($key);
61 | }
62 |
63 | /**
64 | * {@inheritdoc}
65 | */
66 | protected function clientGetMultiple(array $keys)
67 | {
68 | return $this->client->getMultiple($keys);
69 | }
70 |
71 | /**
72 | * {@inheritdoc}
73 | */
74 | protected function clientSet($key, $value)
75 | {
76 | $this->client->set($key, $value);
77 | }
78 |
79 | /**
80 | * {@inheritdoc}
81 | */
82 | protected function clientRemove($key)
83 | {
84 | return (bool) $this->client->del($key);
85 | }
86 |
87 | /**
88 | * {@inheritdoc}
89 | */
90 | protected function clientExists($key)
91 | {
92 | return (bool) $this->client->exists($key);
93 | }
94 |
95 | /**
96 | * {@inheritdoc}
97 | */
98 | protected function clientClear()
99 | {
100 | $this->client->flushdb();
101 | }
102 |
103 | /**
104 | * {@inheritdoc}
105 | */
106 | protected function clientKeys()
107 | {
108 | return $this->client->keys('*');
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/PredisStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Predis\Client;
15 | use Predis\ClientInterface;
16 |
17 | /**
18 | * A key-value store that uses Predis to connect to a Redis instance.
19 | *
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | * @author Titouan Galopin
24 | */
25 | class PredisStore extends AbstractRedisStore
26 | {
27 | /**
28 | * Creates a store backed by a Predis client.
29 | *
30 | * If no client is passed, a new one is created using the default server
31 | * "127.0.0.1" and the default port 6379.
32 | *
33 | * @param ClientInterface|null $client The client used to connect to Redis.
34 | */
35 | public function __construct(ClientInterface $client = null)
36 | {
37 | $this->client = $client ?: new Client();
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | protected function clientNotFoundValue()
44 | {
45 | return null;
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | protected function clientGet($key)
52 | {
53 | return $this->client->get($key);
54 | }
55 |
56 | /**
57 | * {@inheritdoc}
58 | */
59 | protected function clientGetMultiple(array $keys)
60 | {
61 | return $this->client->mget($keys);
62 | }
63 |
64 | /**
65 | * {@inheritdoc}
66 | */
67 | protected function clientSet($key, $value)
68 | {
69 | $this->client->set($key, $value);
70 | }
71 |
72 | /**
73 | * {@inheritdoc}
74 | */
75 | protected function clientRemove($key)
76 | {
77 | return (bool) $this->client->del($key);
78 | }
79 |
80 | /**
81 | * {@inheritdoc}
82 | */
83 | protected function clientExists($key)
84 | {
85 | return (bool) $this->client->exists($key);
86 | }
87 |
88 | /**
89 | * {@inheritdoc}
90 | */
91 | protected function clientClear()
92 | {
93 | $this->client->flushdb();
94 | }
95 |
96 | /**
97 | * {@inheritdoc}
98 | */
99 | protected function clientKeys()
100 | {
101 | return $this->client->keys('*');
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/RiakStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | use Basho\Riak\Riak;
15 | use Exception;
16 | use Webmozart\KeyValueStore\Api\KeyValueStore;
17 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
18 | use Webmozart\KeyValueStore\Api\ReadException;
19 | use Webmozart\KeyValueStore\Api\WriteException;
20 | use Webmozart\KeyValueStore\Util\KeyUtil;
21 | use Webmozart\KeyValueStore\Util\Serializer;
22 |
23 | /**
24 | * A key-value store backed by a Riak client.
25 | *
26 | * @since 1.0
27 | *
28 | * @author Bernhard Schussek
29 | */
30 | class RiakStore implements KeyValueStore
31 | {
32 | /**
33 | * @var string
34 | */
35 | private $bucketName;
36 |
37 | /**
38 | * @var Riak
39 | */
40 | private $client;
41 |
42 | /**
43 | * Creates a store backed by a Riak client.
44 | *
45 | * If no client is passed, a new one is created using the default server
46 | * "127.0.0.1" and the default port 8098.
47 | *
48 | * @param string $bucketName The name of the Riak bucket to use.
49 | * @param Riak|null $client The client used to connect to Riak.
50 | */
51 | public function __construct($bucketName, Riak $client = null)
52 | {
53 | $this->bucketName = $bucketName;
54 | $this->client = $client ?: new Riak();
55 | }
56 |
57 | /**
58 | * {@inheritdoc}
59 | */
60 | public function set($key, $value)
61 | {
62 | KeyUtil::validate($key);
63 |
64 | $serialized = Serializer::serialize($value);
65 |
66 | try {
67 | $this->client->bucket($this->bucketName)->newBinary($key, $serialized)->store();
68 | } catch (Exception $e) {
69 | throw WriteException::forException($e);
70 | }
71 | }
72 |
73 | /**
74 | * {@inheritdoc}
75 | */
76 | public function get($key, $default = null)
77 | {
78 | KeyUtil::validate($key);
79 |
80 | try {
81 | $object = $this->client->bucket($this->bucketName)->getBinary($key);
82 | $exists = $object->exists();
83 | } catch (Exception $e) {
84 | throw ReadException::forException($e);
85 | }
86 |
87 | if (!$exists) {
88 | return $default;
89 | }
90 |
91 | return Serializer::unserialize($object->getData());
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function getOrFail($key)
98 | {
99 | KeyUtil::validate($key);
100 |
101 | try {
102 | $object = $this->client->bucket($this->bucketName)->getBinary($key);
103 | $exists = $object->exists();
104 | } catch (Exception $e) {
105 | throw ReadException::forException($e);
106 | }
107 |
108 | if (!$exists) {
109 | throw NoSuchKeyException::forKey($key);
110 | }
111 |
112 | return Serializer::unserialize($object->getData());
113 | }
114 |
115 | /**
116 | * {@inheritdoc}
117 | */
118 | public function getMultiple(array $keys, $default = null)
119 | {
120 | KeyUtil::validateMultiple($keys);
121 |
122 | $values = array();
123 |
124 | try {
125 | $bucket = $this->client->bucket($this->bucketName);
126 |
127 | foreach ($keys as $key) {
128 | $object = $bucket->getBinary($key);
129 |
130 | $values[$key] = $object->exists() ? $object->getData() : false;
131 | }
132 | } catch (Exception $e) {
133 | throw ReadException::forException($e);
134 | }
135 |
136 | foreach ($values as $key => $value) {
137 | $values[$key] = false === $value
138 | ? $default
139 | : Serializer::unserialize($value);
140 | }
141 |
142 | return $values;
143 | }
144 |
145 | /**
146 | * {@inheritdoc}
147 | */
148 | public function getMultipleOrFail(array $keys)
149 | {
150 | KeyUtil::validateMultiple($keys);
151 |
152 | $values = array();
153 | $notFoundKeys = array();
154 |
155 | try {
156 | $bucket = $this->client->bucket($this->bucketName);
157 |
158 | foreach ($keys as $key) {
159 | $values[$key] = $bucket->getBinary($key);
160 |
161 | if (!$values[$key]->exists()) {
162 | $notFoundKeys[] = $key;
163 | }
164 | }
165 | } catch (Exception $e) {
166 | throw ReadException::forException($e);
167 | }
168 |
169 | if (0 !== count($notFoundKeys)) {
170 | throw NoSuchKeyException::forKeys($notFoundKeys);
171 | }
172 |
173 | foreach ($values as $key => $object) {
174 | $values[$key] = Serializer::unserialize($object->getData());
175 | }
176 |
177 | return $values;
178 | }
179 |
180 | /**
181 | * {@inheritdoc}
182 | */
183 | public function remove($key)
184 | {
185 | KeyUtil::validate($key);
186 |
187 | try {
188 | $object = $this->client->bucket($this->bucketName)->get($key);
189 |
190 | if (!$object->exists()) {
191 | return false;
192 | }
193 |
194 | $object->delete();
195 | } catch (Exception $e) {
196 | throw WriteException::forException($e);
197 | }
198 |
199 | return true;
200 | }
201 |
202 | /**
203 | * {@inheritdoc}
204 | */
205 | public function exists($key)
206 | {
207 | KeyUtil::validate($key);
208 |
209 | try {
210 | return $this->client->bucket($this->bucketName)->get($key)->exists();
211 | } catch (Exception $e) {
212 | throw ReadException::forException($e);
213 | }
214 | }
215 |
216 | /**
217 | * {@inheritdoc}
218 | */
219 | public function clear()
220 | {
221 | try {
222 | $bucket = $this->client->bucket($this->bucketName);
223 |
224 | foreach ($bucket->getKeys() as $key) {
225 | $bucket->get($key)->delete();
226 | }
227 | } catch (Exception $e) {
228 | throw WriteException::forException($e);
229 | }
230 | }
231 |
232 | /**
233 | * {@inheritdoc}
234 | */
235 | public function keys()
236 | {
237 | try {
238 | return $this->client->bucket($this->bucketName)->getKeys();
239 | } catch (Exception $e) {
240 | throw ReadException::forException($e);
241 | }
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/src/SerializingArrayStore.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore;
13 |
14 | /**
15 | * A key-value store backed by a PHP array with serialized entries.
16 | *
17 | * The contents of the store are lost when the store is released from memory.
18 | *
19 | * This store behaves more like persistent key-value stores than
20 | * {@link ArrayStore}. It is useful for testing.
21 | *
22 | * @since 1.0
23 | *
24 | * @author Bernhard Schussek
25 | *
26 | * @deprecated Deprecated as of version 1.0, will be removed in version
27 | * 2.0. Use the `ArrayStore` with the `SERIALIZE` flag
28 | * instead.
29 | */
30 | class SerializingArrayStore extends ArrayStore
31 | {
32 | public function __construct(array $array = array())
33 | {
34 | parent::__construct($array, parent::SERIALIZE);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Util/KeyUtil.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Util;
13 |
14 | use Webmozart\KeyValueStore\Api\InvalidKeyException;
15 |
16 | /**
17 | * Utility methods for dealing with key-value store keys.
18 | *
19 | * @since 1.0
20 | *
21 | * @author Bernhard Schussek
22 | */
23 | final class KeyUtil
24 | {
25 | /**
26 | * Validates that a key is valid.
27 | *
28 | * @param mixed $key The tested key.
29 | *
30 | * @throws InvalidKeyException If the key is invalid.
31 | */
32 | public static function validate($key)
33 | {
34 | if (!is_string($key) && !is_int($key)) {
35 | throw InvalidKeyException::forKey($key);
36 | }
37 | }
38 |
39 | /**
40 | * Validates that multiple keys are valid.
41 | *
42 | * @param array $keys The tested keys.
43 | *
44 | * @throws InvalidKeyException If a key is invalid.
45 | */
46 | public static function validateMultiple($keys)
47 | {
48 | foreach ($keys as $key) {
49 | if (!is_string($key) && !is_int($key)) {
50 | throw InvalidKeyException::forKey($key);
51 | }
52 | }
53 | }
54 |
55 | private function __construct()
56 | {
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Util/Serializer.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Util;
13 |
14 | use Exception;
15 | use Webmozart\KeyValueStore\Api\SerializationFailedException;
16 | use Webmozart\KeyValueStore\Api\UnserializationFailedException;
17 |
18 | /**
19 | * Wrapper for `serialize()`/`unserialize()` that throws proper exceptions.
20 | *
21 | * @since 1.0
22 | *
23 | * @author Bernhard Schussek
24 | */
25 | final class Serializer
26 | {
27 | /**
28 | * Serializes a value.
29 | *
30 | * @param mixed $value The value to serialize.
31 | *
32 | * @return string The serialized value.
33 | *
34 | * @throws SerializationFailedException If the value cannot be serialized.
35 | */
36 | public static function serialize($value)
37 | {
38 | if (is_resource($value)) {
39 | throw SerializationFailedException::forValue($value);
40 | }
41 |
42 | try {
43 | $serialized = serialize($value);
44 | } catch (Exception $e) {
45 | throw SerializationFailedException::forValue($value, $e->getMessage(), $e->getCode(), $e);
46 | }
47 |
48 | return $serialized;
49 | }
50 |
51 | /**
52 | * Unserializes a value.
53 | *
54 | * @param mixed $serialized The serialized value.
55 | *
56 | * @return string The unserialized value.
57 | *
58 | * @throws UnserializationFailedException If the value cannot be unserialized.
59 | */
60 | public static function unserialize($serialized)
61 | {
62 | if (!is_string($serialized)) {
63 | throw UnserializationFailedException::forValue($serialized);
64 | }
65 |
66 | $errorMessage = null;
67 | $errorCode = 0;
68 |
69 | set_error_handler(function ($errno, $errstr) use (&$errorMessage, &$errorCode) {
70 | $errorMessage = $errstr;
71 | $errorCode = $errno;
72 | });
73 |
74 | $value = unserialize($serialized);
75 |
76 | restore_error_handler();
77 |
78 | if (null !== $errorMessage) {
79 | if (false !== $pos = strpos($errorMessage, '): ')) {
80 | // cut "unserialize(%path%):" to make message more readable
81 | $errorMessage = substr($errorMessage, $pos + 3);
82 | }
83 |
84 | throw UnserializationFailedException::forValue($serialized, $errorMessage, $errorCode);
85 | }
86 |
87 | return $value;
88 | }
89 |
90 | private function __construct()
91 | {
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/tests/AbstractCountableStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | /**
15 | * @since 1.0
16 | *
17 | * @author Bernhard Schussek
18 | * @author Titouan Galopin
19 | */
20 | abstract class AbstractCountableStoreTest extends AbstractKeyValueStoreTest
21 | {
22 | /**
23 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
24 | */
25 | abstract public function testCountThrowsReadExceptionIfReadFails();
26 |
27 | public function testCountCache()
28 | {
29 | $this->assertEquals(0, $this->store->count());
30 |
31 | $this->store->set('foo1', 'bar');
32 | $this->assertEquals(1, $this->store->count());
33 |
34 | $this->store->set('foo2', 'bar');
35 | $this->assertEquals(2, $this->store->count());
36 |
37 | $this->store->set('foo3', 'bar');
38 | $this->assertEquals(3, $this->store->count());
39 |
40 | $this->store->remove('foo2');
41 | $this->assertEquals(2, $this->store->count());
42 |
43 | $this->store->clear();
44 | $this->assertEquals(0, $this->store->count());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/AbstractMongoDbStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use ArrayIterator;
15 | use Exception;
16 | use MongoDB\Client;
17 | use Webmozart\KeyValueStore\MongoDbStore;
18 | use Webmozart\KeyValueStore\Tests\Fixtures\TestException;
19 |
20 | /**
21 | * @since 1.0
22 | *
23 | * @author Bernhard Schussek
24 | */
25 | abstract class AbstractMongoDbStoreTest extends AbstractKeyValueStoreTest
26 | {
27 | const DATABASE_NAME = 'webmozart-key-value-store-test-db';
28 |
29 | const COLLECTION_NAME = 'test-collection';
30 |
31 | private static $supported;
32 |
33 | /**
34 | * @var Client
35 | */
36 | protected $client;
37 |
38 | public static function setUpBeforeClass()
39 | {
40 | parent::setUpBeforeClass();
41 |
42 | if (!class_exists('MongoDB\Client')) {
43 | self::$supported = false;
44 |
45 | return;
46 | }
47 |
48 | try {
49 | $client = new Client();
50 | $client->listDatabases();
51 |
52 | self::$supported = true;
53 | } catch (Exception $e) {
54 | self::$supported = false;
55 | }
56 | }
57 |
58 | protected function setUp()
59 | {
60 | if (!self::$supported) {
61 | $this->markTestSkipped('MongoDB is not running or the mongodb/mongodb package ist not installed.');
62 | }
63 |
64 | $this->client = new Client();
65 |
66 | parent::setUp();
67 | }
68 |
69 | protected function tearDown()
70 | {
71 | if (!self::$supported) {
72 | return;
73 | }
74 |
75 | parent::tearDown();
76 |
77 | $this->client->dropDatabase(self::DATABASE_NAME);
78 | }
79 |
80 | /**
81 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
82 | * @expectedExceptionMessage I failed!
83 | */
84 | public function testSetThrowsWriteExceptionIfWriteFails()
85 | {
86 | $exception = new TestException('I failed!');
87 |
88 | $collection = $this->getMockBuilder('MongoDB\Collection')
89 | ->disableOriginalConstructor()
90 | ->getMock();
91 |
92 | $collection->expects($this->once())
93 | ->method('replaceOne')
94 | ->willThrowException($exception);
95 |
96 | $store = new MongoDbStore($collection);
97 | $store->set('key', 'value');
98 | }
99 |
100 | /**
101 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
102 | * @expectedExceptionMessage I failed!
103 | */
104 | public function testRemoveThrowsWriteExceptionIfWriteFails()
105 | {
106 | $exception = new TestException('I failed!');
107 |
108 | $collection = $this->getMockBuilder('MongoDB\Collection')
109 | ->disableOriginalConstructor()
110 | ->getMock();
111 |
112 | $collection->expects($this->once())
113 | ->method('deleteOne')
114 | ->willThrowException($exception);
115 |
116 | $store = new MongoDbStore($collection);
117 | $store->remove('key');
118 | }
119 |
120 | /**
121 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
122 | * @expectedExceptionMessage I failed!
123 | */
124 | public function testRemoveThrowsWriteExceptionIfGetDeletedCountFails()
125 | {
126 | $exception = new TestException('I failed!');
127 |
128 | $collection = $this->getMockBuilder('MongoDB\Collection')
129 | ->disableOriginalConstructor()
130 | ->getMock();
131 | $result = $this->getMockBuilder('MongoDB\DeleteResult')
132 | ->disableOriginalConstructor()
133 | ->getMock();
134 |
135 | $collection->expects($this->once())
136 | ->method('deleteOne')
137 | ->willReturn($result);
138 |
139 | $result->expects($this->once())
140 | ->method('getDeletedCount')
141 | ->willThrowException($exception);
142 |
143 | $store = new MongoDbStore($collection);
144 | $store->remove('key');
145 | }
146 |
147 | /**
148 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
149 | * @expectedExceptionMessage I failed!
150 | */
151 | public function testClearThrowsWriteExceptionIfWriteFails()
152 | {
153 | $exception = new TestException('I failed!');
154 |
155 | $collection = $this->getMockBuilder('MongoDB\Collection')
156 | ->disableOriginalConstructor()
157 | ->getMock();
158 |
159 | $collection->expects($this->once())
160 | ->method('drop')
161 | ->willThrowException($exception);
162 |
163 | $store = new MongoDbStore($collection);
164 | $store->clear();
165 | }
166 |
167 | /**
168 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
169 | * @expectedExceptionMessage I failed!
170 | */
171 | public function testGetThrowsReadExceptionIfReadFails()
172 | {
173 | $exception = new TestException('I failed!');
174 |
175 | $collection = $this->getMockBuilder('MongoDB\Collection')
176 | ->disableOriginalConstructor()
177 | ->getMock();
178 |
179 | $collection->expects($this->once())
180 | ->method('findOne')
181 | ->willThrowException($exception);
182 |
183 | $store = new MongoDbStore($collection);
184 | $store->get('key');
185 | }
186 |
187 | /**
188 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
189 | */
190 | public function testGetThrowsExceptionIfNotUnserializable()
191 | {
192 | $collection = $this->getMockBuilder('MongoDB\Collection')
193 | ->disableOriginalConstructor()
194 | ->getMock();
195 |
196 | $collection->expects($this->once())
197 | ->method('findOne')
198 | ->willReturn(array('_id' => 'key', 'value' => 'foobar'));
199 |
200 | $store = new MongoDbStore($collection);
201 | $store->get('key');
202 | }
203 |
204 | /**
205 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
206 | * @expectedExceptionMessage I failed!
207 | */
208 | public function testGetOrFailThrowsReadExceptionIfReadFails()
209 | {
210 | $exception = new TestException('I failed!');
211 |
212 | $collection = $this->getMockBuilder('MongoDB\Collection')
213 | ->disableOriginalConstructor()
214 | ->getMock();
215 |
216 | $collection->expects($this->once())
217 | ->method('findOne')
218 | ->willThrowException($exception);
219 |
220 | $store = new MongoDbStore($collection);
221 | $store->getOrFail('key');
222 | }
223 |
224 | /**
225 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
226 | */
227 | public function testGetOrFailThrowsExceptionIfNotUnserializable()
228 | {
229 | $collection = $this->getMockBuilder('MongoDB\Collection')
230 | ->disableOriginalConstructor()
231 | ->getMock();
232 |
233 | $collection->expects($this->once())
234 | ->method('findOne')
235 | ->willReturn(array('_id' => 'key', 'value' => 'foobar'));
236 |
237 | $store = new MongoDbStore($collection);
238 | $store->getOrFail('key');
239 | }
240 |
241 | /**
242 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
243 | * @expectedExceptionMessage I failed!
244 | */
245 | public function testGetMultipleThrowsReadExceptionIfReadFails()
246 | {
247 | $exception = new TestException('I failed!');
248 |
249 | $collection = $this->getMockBuilder('MongoDB\Collection')
250 | ->disableOriginalConstructor()
251 | ->getMock();
252 |
253 | $collection->expects($this->once())
254 | ->method('find')
255 | ->willThrowException($exception);
256 |
257 | $store = new MongoDbStore($collection);
258 | $store->getMultiple(array('key'));
259 | }
260 |
261 | /**
262 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
263 | */
264 | public function testGetMultipleThrowsExceptionIfNotUnserializable()
265 | {
266 | $collection = $this->getMockBuilder('MongoDB\Collection')
267 | ->disableOriginalConstructor()
268 | ->getMock();
269 |
270 | $cursor = new ArrayIterator(array(
271 | array('_id' => 'key', 'value' => 'foobar'),
272 | ));
273 |
274 | $collection->expects($this->once())
275 | ->method('find')
276 | ->willReturn($cursor);
277 |
278 | $store = new MongoDbStore($collection);
279 | $store->getMultiple(array('key'));
280 | }
281 |
282 | /**
283 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
284 | * @expectedExceptionMessage I failed!
285 | */
286 | public function testGetMultipleOrFailThrowsReadExceptionIfReadFails()
287 | {
288 | $exception = new TestException('I failed!');
289 |
290 | $collection = $this->getMockBuilder('MongoDB\Collection')
291 | ->disableOriginalConstructor()
292 | ->getMock();
293 |
294 | $collection->expects($this->once())
295 | ->method('find')
296 | ->willThrowException($exception);
297 |
298 | $store = new MongoDbStore($collection);
299 | $store->getMultipleOrFail(array('key'));
300 | }
301 |
302 | /**
303 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
304 | */
305 | public function testGetMultipleOrFailThrowsExceptionIfNotUnserializable()
306 | {
307 | $collection = $this->getMockBuilder('MongoDB\Collection')
308 | ->disableOriginalConstructor()
309 | ->getMock();
310 |
311 | $cursor = new ArrayIterator(array(
312 | array('_id' => 'key', 'value' => 'foobar'),
313 | ));
314 |
315 | $collection->expects($this->once())
316 | ->method('find')
317 | ->willReturn($cursor);
318 |
319 | $store = new MongoDbStore($collection);
320 | $store->getMultipleOrFail(array('key'));
321 | }
322 |
323 | /**
324 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
325 | * @expectedExceptionMessage I failed!
326 | */
327 | public function testExistsThrowsReadExceptionIfReadFails()
328 | {
329 | $exception = new TestException('I failed!');
330 |
331 | $collection = $this->getMockBuilder('MongoDB\Collection')
332 | ->disableOriginalConstructor()
333 | ->getMock();
334 |
335 | $collection->expects($this->once())
336 | ->method('count')
337 | ->willThrowException($exception);
338 |
339 | $store = new MongoDbStore($collection);
340 | $store->exists('key');
341 | }
342 |
343 | /**
344 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
345 | * @expectedExceptionMessage I failed!
346 | */
347 | public function testKeysThrowsReadExceptionIfReadFails()
348 | {
349 | $exception = new TestException('I failed!');
350 |
351 | $collection = $this->getMockBuilder('MongoDB\Collection')
352 | ->disableOriginalConstructor()
353 | ->getMock();
354 |
355 | $collection->expects($this->once())
356 | ->method('find')
357 | ->willThrowException($exception);
358 |
359 | $store = new MongoDbStore($collection);
360 | $store->keys();
361 | }
362 | }
363 |
--------------------------------------------------------------------------------
/tests/AbstractSortableCountableStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | /**
15 | * @since 1.0
16 | *
17 | * @author Bernhard Schussek
18 | * @author Titouan Galopin
19 | */
20 | abstract class AbstractSortableCountableStoreTest extends AbstractCountableStoreTest
21 | {
22 | /**
23 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
24 | */
25 | abstract public function testSortThrowsReadExceptionIfReadFails();
26 |
27 | /**
28 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
29 | */
30 | abstract public function testSortThrowsWriteExceptionIfWriteFails();
31 |
32 | public function testSortRegularStringKeys()
33 | {
34 | $this->store->set('c', 1);
35 | $this->store->set('a', 2);
36 | $this->store->set('b', 3);
37 |
38 | $this->store->sort();
39 |
40 | $this->assertSame(array(
41 | 'a' => 2,
42 | 'b' => 3,
43 | 'c' => 1,
44 | ), $this->store->getMultiple($this->store->keys()));
45 | }
46 |
47 | public function testSortRegularIntegerKeys()
48 | {
49 | $this->store->set(3, 'a');
50 | $this->store->set(1, 'b');
51 | $this->store->set(2, 'c');
52 |
53 | $this->store->sort();
54 |
55 | $this->assertSame(array(
56 | 1 => 'b',
57 | 2 => 'c',
58 | 3 => 'a',
59 | ), $this->store->getMultiple($this->store->keys()));
60 | }
61 |
62 | public function testSortStringStringKeys()
63 | {
64 | $this->store->set('c', 1);
65 | $this->store->set('a', 2);
66 | $this->store->set('b', 3);
67 |
68 | $this->store->sort(SORT_STRING);
69 |
70 | $this->assertSame(array(
71 | 'a' => 2,
72 | 'b' => 3,
73 | 'c' => 1,
74 | ), $this->store->getMultiple($this->store->keys()));
75 | }
76 |
77 | public function testSortNumericIntegerKeys()
78 | {
79 | $this->store->set(3, 'a');
80 | $this->store->set(1, 'b');
81 | $this->store->set(2, 'c');
82 |
83 | $this->store->sort(SORT_NUMERIC);
84 |
85 | $this->assertSame(array(
86 | 1 => 'b',
87 | 2 => 'c',
88 | 3 => 'a',
89 | ), $this->store->getMultiple($this->store->keys()));
90 | }
91 |
92 | public function testSortNaturalStringKeys()
93 | {
94 | if (PHP_VERSION_ID < 50400) {
95 | $this->markTestSkipped('SORT_NATURAL not available');
96 | }
97 |
98 | $this->store->set('10', 'c');
99 | $this->store->set('1', 'g');
100 | $this->store->set('100', 'a');
101 | $this->store->set('9', 'b');
102 | $this->store->set('7a', 'h');
103 | $this->store->set('7b', 'd');
104 | $this->store->set('_5', 'z');
105 |
106 | $this->store->sort(SORT_NATURAL);
107 |
108 | $this->assertSame(array(
109 | '1' => 'g',
110 | '7a' => 'h',
111 | '7b' => 'd',
112 | '9' => 'b',
113 | '10' => 'c',
114 | '100' => 'a',
115 | '_5' => 'z',
116 | ), $this->store->getMultiple($this->store->keys()));
117 | }
118 |
119 | public function testSortCaseInsensitiveStringKeys()
120 | {
121 | if (PHP_VERSION_ID < 50400) {
122 | $this->markTestSkipped('SORT_FLAG_CASE not available');
123 | }
124 |
125 | $this->store->set('_Ac', 'A');
126 | $this->store->set('abc', 'F');
127 | $this->store->set('_ab', 'G');
128 | $this->store->set('ABB', 'E');
129 | $this->store->set('Bce', 'C');
130 | $this->store->set('bcd', 'D');
131 | $this->store->set('bCf', 'E');
132 |
133 | $this->store->sort(SORT_STRING | SORT_FLAG_CASE);
134 |
135 | $this->assertSame(array(
136 | '_ab' => 'G',
137 | '_Ac' => 'A',
138 | 'ABB' => 'E',
139 | 'abc' => 'F',
140 | 'bcd' => 'D',
141 | 'Bce' => 'C',
142 | 'bCf' => 'E',
143 | ), $this->store->getMultiple($this->store->keys()));
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/tests/ArrayStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use stdClass;
15 | use Webmozart\KeyValueStore\ArrayStore;
16 |
17 | /**
18 | * @since 1.0
19 | *
20 | * @author Bernhard Schussek
21 | */
22 | class ArrayStoreTest extends AbstractSortableCountableStoreTest
23 | {
24 | protected function createStore()
25 | {
26 | return new ArrayStore();
27 | }
28 |
29 | protected function createPopulatedStore(array $values)
30 | {
31 | return new ArrayStore($values);
32 | }
33 |
34 | public function testCreateStoreWithElements()
35 | {
36 | $object = new stdClass();
37 | $store = $this->createPopulatedStore(array(0 => 'a', 1 => $object));
38 |
39 | $this->assertSame('a', $store->get(0));
40 | $this->assertEquals($object, $store->get(1));
41 | }
42 |
43 | /**
44 | * @dataProvider provideInvalidValues
45 | */
46 | public function testSetThrowsExceptionIfValueNotSerializable($value)
47 | {
48 | // ArrayStore never serializes its values
49 | $this->assertTrue(true);
50 | }
51 |
52 | public function testSetThrowsWriteExceptionIfWriteFails()
53 | {
54 | // ArrayStore never writes a storage
55 | $this->assertTrue(true);
56 | }
57 |
58 | public function testRemoveThrowsWriteExceptionIfWriteFails()
59 | {
60 | // ArrayStore never writes a storage
61 | $this->assertTrue(true);
62 | }
63 |
64 | public function testClearThrowsWriteExceptionIfWriteFails()
65 | {
66 | // ArrayStore never writes a storage
67 | $this->assertTrue(true);
68 | }
69 |
70 | public function testGetThrowsReadExceptionIfReadFails()
71 | {
72 | // ArrayStore never reads a storage
73 | $this->assertTrue(true);
74 | }
75 |
76 | public function testGetThrowsExceptionIfNotUnserializable()
77 | {
78 | // ArrayStore never serializes its values
79 | $this->assertTrue(true);
80 | }
81 |
82 | public function testGetOrFailThrowsReadExceptionIfReadFails()
83 | {
84 | // ArrayStore never reads a storage
85 | $this->assertTrue(true);
86 | }
87 |
88 | public function testGetOrFailThrowsExceptionIfNotUnserializable()
89 | {
90 | // ArrayStore never serializes its values
91 | $this->assertTrue(true);
92 | }
93 |
94 | public function testGetMultipleThrowsReadExceptionIfReadFails()
95 | {
96 | // ArrayStore never reads a storage
97 | $this->assertTrue(true);
98 | }
99 |
100 | public function testGetMultipleThrowsExceptionIfNotUnserializable()
101 | {
102 | // ArrayStore never serializes its values
103 | $this->assertTrue(true);
104 | }
105 |
106 | public function testGetMultipleOrFailThrowsReadExceptionIfReadFails()
107 | {
108 | // ArrayStore never reads a storage
109 | $this->assertTrue(true);
110 | }
111 |
112 | public function testGetMultipleOrFailThrowsExceptionIfNotUnserializable()
113 | {
114 | // ArrayStore never serializes its values
115 | $this->assertTrue(true);
116 | }
117 |
118 | public function testExistsThrowsReadExceptionIfReadFails()
119 | {
120 | // ArrayStore never reads a storage
121 | $this->assertTrue(true);
122 | }
123 |
124 | public function testKeysThrowsReadExceptionIfReadFails()
125 | {
126 | // ArrayStore never reads a storage
127 | $this->assertTrue(true);
128 | }
129 |
130 | public function testSortThrowsReadExceptionIfReadFails()
131 | {
132 | // ArrayStore never reads a storage
133 | $this->assertTrue(true);
134 | }
135 |
136 | public function testSortThrowsWriteExceptionIfWriteFails()
137 | {
138 | // ArrayStore never write a storage
139 | $this->assertTrue(true);
140 | }
141 |
142 | public function testCountThrowsReadExceptionIfReadFails()
143 | {
144 | // ArrayStore never reads a storage
145 | $this->assertTrue(true);
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/tests/DbalStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Doctrine\DBAL\DriverManager;
16 | use Webmozart\KeyValueStore\Api\KeyValueStore;
17 | use Webmozart\KeyValueStore\Api\SortableStore;
18 | use Webmozart\KeyValueStore\DbalStore;
19 | use Webmozart\KeyValueStore\Tests\Fixtures\TestException;
20 |
21 | /**
22 | * @since 1.0
23 | *
24 | * @author Bernhard Schussek
25 | * @author Michiel Boeckaert
26 | */
27 | class DbalStoreTest extends AbstractKeyValueStoreTest
28 | {
29 | protected static $dbalStore;
30 |
31 | public static function setUpBeforeClass()
32 | {
33 | parent::setUpBeforeClass();
34 |
35 | $connection = DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'memory' => true));
36 | $schemaManager = $connection->getSchemaManager();
37 | $schema = $schemaManager->createSchema();
38 | self::$dbalStore = new DbalStore($connection, 'store');
39 |
40 | if (!$schema->hasTable(self::$dbalStore->getTableName())) {
41 | $schemaManager->createTable(self::$dbalStore->getTableForCreate());
42 | }
43 | }
44 |
45 | /**
46 | * @return KeyValueStore|SortableStore The created store.
47 | */
48 | protected function createStore()
49 | {
50 | return self::$dbalStore;
51 | }
52 |
53 | public function testSettingAValueTwiceUpdatesTheValue()
54 | {
55 | $this->store->set('a', '123');
56 | $this->store->set('a', '124');
57 | $this->assertEquals('124', $this->store->get('a'));
58 | }
59 |
60 | public function provideUnsafeTableNamesSoWeCanBlowUpOurDataBase()
61 | {
62 | return array(
63 | array(null),
64 | array(false),
65 | array(array()),
66 | array(''),
67 | );
68 | }
69 |
70 | /**
71 | * @dataProvider provideUnsafeTableNamesSoWeCanBlowUpOurDataBase
72 | * @expectedException \InvalidArgumentException
73 | */
74 | public function testTheTableNameNeedsToBeANotEmptyString($tableName)
75 | {
76 | $connection = $this->getConnectionMock();
77 | new DbalStore($connection, $tableName);
78 | }
79 |
80 | public function testGetTableNameWorks()
81 | {
82 | $connection = $this->getConnectionMock();
83 | $store = new DbalStore($connection, 'foo');
84 | $this->assertEquals('foo', $store->getTableName());
85 | }
86 |
87 | public function testGetTableNameDefaultsToStore()
88 | {
89 | $connection = $this->getConnectionMock();
90 | $store = new DbalStore($connection);
91 | $this->assertEquals('store', $store->getTableName());
92 | }
93 |
94 | /**
95 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
96 | */
97 | public function testSetThrowsWriteExceptionIfWriteFails()
98 | {
99 | $connection = $this->getConnectionMock();
100 | $connection->expects($this->once())->method('fetchAssoc')->willThrowException(new TestException('I failed'));
101 |
102 | $store = new DbalStore($connection, 'store');
103 | $store->set('foo', 'bar');
104 | }
105 |
106 | /**
107 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
108 | */
109 | public function testRemoveThrowsWriteExceptionIfWriteFails()
110 | {
111 | $connection = $this->getConnectionMock();
112 | $connection->expects($this->once())->method('delete')->willThrowException(new TestException('I failed'));
113 |
114 | $store = new DbalStore($connection, 'store');
115 | $store->remove('foo');
116 | }
117 |
118 | /**
119 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
120 | */
121 | public function testClearThrowsWriteExceptionIfWriteFails()
122 | {
123 | $connection = $this->getConnectionMock();
124 | $connection->expects($this->once())->method('query')->willThrowException(new TestException('I failed'));
125 |
126 | $store = new DbalStore($connection, 'store');
127 | $store->clear();
128 | }
129 |
130 | /**
131 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
132 | */
133 | public function testGetThrowsReadExceptionIfReadFails()
134 | {
135 | $connection = $this->getConnectionMock();
136 | $connection->expects($this->once())->method('fetchAssoc')->willThrowException(new TestException('I failed'));
137 |
138 | $store = new DbalStore($connection, 'store');
139 | $store->get('foo');
140 | }
141 |
142 | /**
143 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
144 | */
145 | public function testGetThrowsExceptionIfNotUnserializable()
146 | {
147 | $connection = $this->getConnectionMock();
148 | $connection->expects($this->once())->method('fetchAssoc')
149 | ->willReturn(array('meta_key' => 'foo', 'meta_value' => 'foo_bar'));
150 |
151 | $store = new DbalStore($connection, 'store');
152 | $store->get('foo');
153 | }
154 |
155 | /**
156 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
157 | */
158 | public function testGetOrFailThrowsReadExceptionIfReadFails()
159 | {
160 | $connection = $this->getConnectionMock();
161 | $connection->expects($this->once())->method('fetchAssoc')->willThrowException(new TestException('I failed'));
162 |
163 | $store = new DbalStore($connection, 'store');
164 | $store->getOrFail('foo');
165 | }
166 |
167 | /**
168 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
169 | */
170 | public function testGetOrFailThrowsExceptionIfNotUnserializable()
171 | {
172 | $connection = $this->getConnectionMock();
173 | $connection->expects($this->once())->method('fetchAssoc')
174 | ->willReturn(array('meta_key' => 'foo', 'meta_value' => 'foo_bar'));
175 |
176 | $store = new DbalStore($connection, 'store');
177 | $store->getOrFail('foo');
178 | }
179 |
180 | /**
181 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
182 | */
183 | public function testGetMultipleThrowsReadExceptionIfReadFails()
184 | {
185 | $connection = $this->getConnectionMock();
186 | $connection->expects($this->once())->method('executeQuery')->willThrowException(new TestException('I failed'));
187 |
188 | $store = new DbalStore($connection, 'store');
189 | $store->getMultiple(array('foo', 'bar'));
190 | }
191 |
192 | /**
193 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
194 | */
195 | public function testGetMultipleThrowsExceptionIfNotUnserializable()
196 | {
197 | $connection = $this->getConnectionMock();
198 | $statement = $this->getStatementMock();
199 | $connection->expects($this->once())->method('executeQuery')->willReturn($statement);
200 | $statement->expects($this->once())->method('fetchAll')
201 | ->willReturn(array(array('meta_key' => 'my_key', 'meta_value' => 'foo_bar')));
202 |
203 | $store = new DbalStore($connection, 'store');
204 | $store->getMultiple(array('foo', 'bar'));
205 | }
206 |
207 | /**
208 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
209 | */
210 | public function testGetMultipleOrFailThrowsReadExceptionIfReadFails()
211 | {
212 | $connection = $this->getConnectionMock();
213 | $connection->expects($this->once())->method('executeQuery')->willThrowException(new TestException('I failed'));
214 |
215 | $store = new DbalStore($connection, 'store');
216 | $store->getMultiple(array('foo', 'bar'));
217 | }
218 |
219 | /**
220 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
221 | */
222 | public function testGetMultipleOrFailThrowsExceptionIfNotUnserializable()
223 | {
224 | $connection = $this->getConnectionMock();
225 | $statement = $this->getStatementMock();
226 | $connection->expects($this->once())->method('executeQuery')->willReturn($statement);
227 | $statement->expects($this->once())->method('fetchAll')
228 | ->willReturn(array(array('meta_key' => 'my_key', 'meta_value' => 'foo_bar')));
229 |
230 | $store = new DbalStore($connection, 'store');
231 | $store->getMultipleOrFail(array('foo', 'bar'));
232 | }
233 |
234 | /**
235 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
236 | */
237 | public function testExistsThrowsReadExceptionIfReadFails()
238 | {
239 | $connection = $this->getConnectionMock();
240 | $connection->expects($this->once())->method('fetchAssoc')->willThrowException(new TestException('I failed'));
241 |
242 | $store = new DbalStore($connection, 'store');
243 | $store->exists('foo');
244 | }
245 |
246 | /**
247 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
248 | */
249 | public function testKeysThrowsReadExceptionIfReadFails()
250 | {
251 | $connection = $this->getConnectionMock();
252 | $connection->expects($this->once())->method('query')->willThrowException(new TestException('I failed'));
253 |
254 | $store = new DbalStore($connection, 'store');
255 | $store->keys();
256 | }
257 |
258 | /**
259 | * @return \PHPUnit_Framework_MockObject_MockObject|Connection
260 | */
261 | protected function getConnectionMock()
262 | {
263 | $connection = $this->getMockBuilder('Doctrine\DBAL\Connection')
264 | ->disableOriginalConstructor()
265 | ->getMock();
266 |
267 | return $connection;
268 | }
269 |
270 | /**
271 | * @return \PHPUnit_Framework_MockObject_MockObject
272 | */
273 | protected function getStatementMock()
274 | {
275 | $statement = $this->getMockBuilder('Doctrine\DBAL\Driver\Statement')
276 | ->disableOriginalConstructor()
277 | ->getMock();
278 |
279 | return $statement;
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/tests/Decorator/AbstractDecoratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Decorator;
13 |
14 | use PHPUnit_Framework_MockObject_MockObject;
15 | use PHPUnit_Framework_TestCase;
16 | use Webmozart\KeyValueStore\Api\KeyValueStore;
17 | use Webmozart\KeyValueStore\Decorator\AbstractDecorator;
18 | use Webmozart\KeyValueStore\Decorator\SortableDecorator;
19 |
20 | /**
21 | * @since 1.0
22 | *
23 | * @author Bernhard Schussek
24 | * @author Titouan Galopin
25 | */
26 | abstract class AbstractDecoratorTest extends PHPUnit_Framework_TestCase
27 | {
28 | /**
29 | * @var PHPUnit_Framework_MockObject_MockObject|KeyValueStore
30 | */
31 | protected $innerStore;
32 |
33 | /**
34 | * @var SortableDecorator
35 | */
36 | protected $decorator;
37 |
38 | /**
39 | * @param KeyValueStore $innerStore
40 | *
41 | * @return KeyValueStore|AbstractDecorator The created store.
42 | */
43 | abstract protected function createDecorator(KeyValueStore $innerStore);
44 |
45 | protected function setUp()
46 | {
47 | $this->innerStore = $this->getMock('Webmozart\KeyValueStore\Api\KeyValueStore');
48 | $this->decorator = $this->createDecorator($this->innerStore);
49 | }
50 |
51 | protected function tearDown()
52 | {
53 | if ($this->decorator) {
54 | $this->decorator->clear();
55 | }
56 | }
57 |
58 | public function testGetDelegate()
59 | {
60 | $this->innerStore->expects($this->once())
61 | ->method('get')
62 | ->with('key');
63 |
64 | $this->decorator->get('key');
65 | }
66 |
67 | public function testGetOrFailDelegate()
68 | {
69 | $this->innerStore->expects($this->once())
70 | ->method('getOrFail')
71 | ->with('key');
72 |
73 | $this->decorator->getOrFail('key');
74 | }
75 |
76 | public function testGetMultipleDelegate()
77 | {
78 | $this->innerStore->expects($this->once())
79 | ->method('getMultiple')
80 | ->with(array('key1', 'key2'));
81 |
82 | $this->decorator->getMultiple(array('key1', 'key2'));
83 | }
84 |
85 | public function testGetMultipleOrFailDelegate()
86 | {
87 | $this->innerStore->expects($this->once())
88 | ->method('getMultipleOrFail')
89 | ->with(array('key1', 'key2'));
90 |
91 | $this->decorator->getMultipleOrFail(array('key1', 'key2'));
92 | }
93 |
94 | public function testSetDelegate()
95 | {
96 | $this->innerStore->expects($this->once())
97 | ->method('set')
98 | ->with('key', 'value');
99 |
100 | $this->decorator->set('key', 'value');
101 | }
102 |
103 | public function testRemoveDelegate()
104 | {
105 | $this->innerStore->expects($this->once())
106 | ->method('remove')
107 | ->with('key');
108 |
109 | $this->decorator->remove('key');
110 | }
111 |
112 | public function testExistsDelegate()
113 | {
114 | $this->innerStore->expects($this->once())
115 | ->method('exists')
116 | ->with('key');
117 |
118 | $this->decorator->exists('key');
119 | }
120 |
121 | public function testClearDelegate()
122 | {
123 | $this->innerStore->expects($this->once())
124 | ->method('clear');
125 |
126 | $this->decorator->clear();
127 | }
128 |
129 | public function testKeysDelegate()
130 | {
131 | $this->innerStore->expects($this->once())
132 | ->method('keys');
133 |
134 | $this->decorator->keys();
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/tests/Decorator/CachingDecoratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Decorator;
13 |
14 | use Doctrine\Common\Cache\Cache;
15 | use PHPUnit_Framework_MockObject_MockObject;
16 | use Webmozart\KeyValueStore\Api\InvalidKeyException;
17 | use Webmozart\KeyValueStore\Api\KeyValueStore;
18 | use Webmozart\KeyValueStore\Api\NoSuchKeyException;
19 | use Webmozart\KeyValueStore\Api\SerializationFailedException;
20 | use Webmozart\KeyValueStore\Api\WriteException;
21 | use Webmozart\KeyValueStore\Decorator\CachingDecorator;
22 |
23 | /**
24 | * @since 1.0
25 | *
26 | * @author Bernhard Schussek
27 | */
28 | class CachingDecoratorTest extends AbstractDecoratorTest
29 | {
30 | /**
31 | * @var PHPUnit_Framework_MockObject_MockObject|Cache
32 | */
33 | private $cache;
34 |
35 | protected function createDecorator(KeyValueStore $innerStore)
36 | {
37 | $this->cache = $this->getMock('Webmozart\KeyValueStore\Tests\Fixtures\TestClearableCache');
38 |
39 | return new CachingDecorator($this->innerStore, $this->cache);
40 | }
41 |
42 | public function testGetDelegate()
43 | {
44 | // We test the get in another way
45 | $this->assertTrue(true);
46 | }
47 |
48 | public function testGetMultipleDelegate()
49 | {
50 | // We test the getMultiple in another way
51 | $this->assertTrue(true);
52 | }
53 |
54 | public function testGetMultipleOrFailDelegate()
55 | {
56 | // We test the getMultipleOrFail in another way
57 | $this->assertTrue(true);
58 | }
59 |
60 | /**
61 | * @expectedException \InvalidArgumentException
62 | */
63 | public function testCreateFailsIfNeitherClearableNorFlushable()
64 | {
65 | $this->cache = $this->getMock('Doctrine\Common\Cache\Cache');
66 |
67 | new CachingDecorator($this->innerStore, $this->cache);
68 | }
69 |
70 | public function testSetWritesToCache()
71 | {
72 | $this->innerStore->expects($this->once())
73 | ->method('set')
74 | ->with('key', 'value');
75 |
76 | $this->cache->expects($this->once())
77 | ->method('save')
78 | ->with('key', 'value');
79 |
80 | $this->decorator->set('key', 'value');
81 | }
82 |
83 | public function testSetWritesTtlIfGiven()
84 | {
85 | $this->decorator = new CachingDecorator($this->innerStore, $this->cache, 100);
86 |
87 | $this->innerStore->expects($this->once())
88 | ->method('set')
89 | ->with('key', 'value');
90 |
91 | $this->cache->expects($this->once())
92 | ->method('save')
93 | ->with('key', 'value', 100);
94 |
95 | $this->decorator->set('key', 'value');
96 | }
97 |
98 | /**
99 | * @expectedException \Webmozart\KeyValueStore\Api\SerializationFailedException
100 | */
101 | public function testSetDoesNotWriteToCacheIfKeyValueStoreFails()
102 | {
103 | $this->innerStore->expects($this->once())
104 | ->method('set')
105 | ->with('key', 'value')
106 | ->willThrowException(new SerializationFailedException());
107 |
108 | $this->cache->expects($this->never())
109 | ->method('save');
110 |
111 | $this->decorator->set('key', 'value');
112 | }
113 |
114 | public function testGetReturnsFromCacheIfCached()
115 | {
116 | $this->cache->expects($this->at(0))
117 | ->method('contains')
118 | ->with('key')
119 | ->willReturn(true);
120 |
121 | $this->innerStore->expects($this->never())
122 | ->method('getOrFail');
123 |
124 | $this->cache->expects($this->at(1))
125 | ->method('fetch')
126 | ->with('key')
127 | ->willReturn('value');
128 |
129 | $this->assertSame('value', $this->decorator->get('key'));
130 | }
131 |
132 | public function testGetWritesToCacheIfNotCached()
133 | {
134 | $this->cache->expects($this->at(0))
135 | ->method('contains')
136 | ->with('key')
137 | ->willReturn(false);
138 |
139 | $this->innerStore->expects($this->once())
140 | ->method('getOrFail')
141 | ->with('key')
142 | ->willReturn('value');
143 |
144 | $this->cache->expects($this->at(1))
145 | ->method('save')
146 | ->with('key', 'value');
147 |
148 | $this->assertSame('value', $this->decorator->get('key'));
149 | }
150 |
151 | public function testGetWritesTtlIfNotCached()
152 | {
153 | $this->decorator = new CachingDecorator($this->innerStore, $this->cache, 100);
154 |
155 | $this->cache->expects($this->at(0))
156 | ->method('contains')
157 | ->with('key')
158 | ->willReturn(false);
159 |
160 | $this->innerStore->expects($this->once())
161 | ->method('getOrFail')
162 | ->with('key')
163 | ->willReturn('value');
164 |
165 | $this->cache->expects($this->at(1))
166 | ->method('save')
167 | ->with('key', 'value', 100);
168 |
169 | $this->assertSame('value', $this->decorator->get('key'));
170 | }
171 |
172 | public function testGetDoesNotSaveToCacheIfKeyNotFound()
173 | {
174 | $this->cache->expects($this->once())
175 | ->method('contains')
176 | ->with('key')
177 | ->willReturn(false);
178 |
179 | $this->innerStore->expects($this->once())
180 | ->method('getOrFail')
181 | ->willThrowException(NoSuchKeyException::forKey('key'));
182 |
183 | $this->cache->expects($this->never())
184 | ->method('save');
185 |
186 | $this->assertSame('default', $this->decorator->get('key', 'default'));
187 | }
188 |
189 | public function testGetOrFailReturnsFromCacheIfCached()
190 | {
191 | $this->cache->expects($this->at(0))
192 | ->method('contains')
193 | ->with('key')
194 | ->willReturn(true);
195 |
196 | $this->innerStore->expects($this->never())
197 | ->method('getOrFail');
198 |
199 | $this->cache->expects($this->at(1))
200 | ->method('fetch')
201 | ->with('key')
202 | ->willReturn('value');
203 |
204 | $this->assertSame('value', $this->decorator->getOrFail('key'));
205 | }
206 |
207 | public function testGetOrFailWritesToCacheIfNotCached()
208 | {
209 | $this->cache->expects($this->at(0))
210 | ->method('contains')
211 | ->with('key')
212 | ->willReturn(false);
213 |
214 | $this->innerStore->expects($this->once())
215 | ->method('getOrFail')
216 | ->with('key')
217 | ->willReturn('value');
218 |
219 | $this->cache->expects($this->at(1))
220 | ->method('save')
221 | ->with('key', 'value');
222 |
223 | $this->assertSame('value', $this->decorator->getOrFail('key'));
224 | }
225 |
226 | public function testGetOrFailWritesTtlIfNotCached()
227 | {
228 | $this->decorator = new CachingDecorator($this->innerStore, $this->cache, 100);
229 |
230 | $this->cache->expects($this->at(0))
231 | ->method('contains')
232 | ->with('key')
233 | ->willReturn(false);
234 |
235 | $this->innerStore->expects($this->once())
236 | ->method('getOrFail')
237 | ->with('key')
238 | ->willReturn('value');
239 |
240 | $this->cache->expects($this->at(1))
241 | ->method('save')
242 | ->with('key', 'value', 100);
243 |
244 | $this->assertSame('value', $this->decorator->getOrFail('key'));
245 | }
246 |
247 | /**
248 | * @expectedException \Webmozart\KeyValueStore\Api\NoSuchKeyException
249 | */
250 | public function testGetOrFailForwardsNoSuchKeyException()
251 | {
252 | $this->cache->expects($this->once())
253 | ->method('contains')
254 | ->with('key')
255 | ->willReturn(false);
256 |
257 | $this->innerStore->expects($this->once())
258 | ->method('getOrFail')
259 | ->willThrowException(NoSuchKeyException::forKey('key'));
260 |
261 | $this->cache->expects($this->never())
262 | ->method('save');
263 |
264 | $this->decorator->getOrFail('key');
265 | }
266 |
267 | public function testGetMultipleMergesCachedAndNonCachedEntries()
268 | {
269 | $this->cache->expects($this->exactly(3))
270 | ->method('contains')
271 | ->willReturnMap(array(
272 | array('a', false),
273 | array('b', true),
274 | array('c', false),
275 | ));
276 |
277 | $this->innerStore->expects($this->once())
278 | ->method('getMultiple')
279 | ->with(array('a', 2 => 'c'), 'default')
280 | ->willReturn(array(
281 | 'a' => 'value1',
282 | 'c' => 'default',
283 | ));
284 |
285 | $this->cache->expects($this->once())
286 | ->method('fetch')
287 | ->with('b')
288 | ->willReturn('value2');
289 |
290 | // We don't know which keys to save and which not
291 | $this->cache->expects($this->never())
292 | ->method('save');
293 |
294 | $values = $this->decorator->getMultiple(array('a', 'b', 'c'), 'default');
295 |
296 | // Undefined order
297 | ksort($values);
298 |
299 | $this->assertSame(array(
300 | 'a' => 'value1',
301 | 'b' => 'value2',
302 | 'c' => 'default',
303 | ), $values);
304 | }
305 |
306 | public function testGetMultipleOrFailMergesCachedAndNonCachedEntries()
307 | {
308 | $this->cache->expects($this->exactly(3))
309 | ->method('contains')
310 | ->willReturnMap(array(
311 | array('a', false),
312 | array('b', true),
313 | array('c', false),
314 | ));
315 |
316 | $this->innerStore->expects($this->once())
317 | ->method('getMultipleOrFail')
318 | ->with(array('a', 2 => 'c'))
319 | ->willReturn(array(
320 | 'a' => 'value1',
321 | 'c' => 'value3',
322 | ));
323 |
324 | $this->cache->expects($this->once())
325 | ->method('fetch')
326 | ->with('b')
327 | ->willReturn('value2');
328 |
329 | $this->cache->expects($this->exactly(2))
330 | ->method('save')
331 | ->withConsecutive(array('a', 'value1'), array('c', 'value3'));
332 |
333 | $values = $this->decorator->getMultipleOrFail(array('a', 'b', 'c'));
334 |
335 | // Undefined order
336 | ksort($values);
337 |
338 | $this->assertSame(array(
339 | 'a' => 'value1',
340 | 'b' => 'value2',
341 | 'c' => 'value3',
342 | ), $values);
343 | }
344 |
345 | public function testExistsQueriesCache()
346 | {
347 | $this->cache->expects($this->once())
348 | ->method('contains')
349 | ->with('key')
350 | ->willReturn(true);
351 |
352 | $this->innerStore->expects($this->never())
353 | ->method('exists');
354 |
355 | $this->assertTrue($this->decorator->exists('key'));
356 | }
357 |
358 | /**
359 | * @dataProvider provideTrueFalse
360 | */
361 | public function testExistsQueriesStoreIfNotCached($result)
362 | {
363 | $this->cache->expects($this->once())
364 | ->method('contains')
365 | ->with('key')
366 | ->willReturn(false);
367 |
368 | $this->innerStore->expects($this->once())
369 | ->method('exists')
370 | ->with('key')
371 | ->willReturn($result);
372 |
373 | $this->assertSame($result, $this->decorator->exists('key'));
374 | }
375 |
376 | public function provideTrueFalse()
377 | {
378 | return array(
379 | array(true),
380 | array(false),
381 | );
382 | }
383 |
384 | public function testRemoveDeletesFromCache()
385 | {
386 | $this->innerStore->expects($this->once())
387 | ->method('remove')
388 | ->with('key');
389 |
390 | $this->cache->expects($this->once())
391 | ->method('delete')
392 | ->with('key');
393 |
394 | $this->decorator->remove('key');
395 | }
396 |
397 | /**
398 | * @expectedException \Webmozart\KeyValueStore\Api\InvalidKeyException
399 | */
400 | public function testRemoveDoesNotDeleteFromCacheIfRemovalFails()
401 | {
402 | $this->innerStore->expects($this->once())
403 | ->method('remove')
404 | ->with('key')
405 | ->willThrowException(new InvalidKeyException());
406 |
407 | $this->cache->expects($this->never())
408 | ->method('delete');
409 |
410 | $this->decorator->remove('key');
411 | }
412 |
413 | public function testClearDeletesAllFromCache()
414 | {
415 | $this->cache = $this->getMock('Webmozart\KeyValueStore\Tests\\Fixtures\TestClearableCache');
416 | $this->decorator = new CachingDecorator($this->innerStore, $this->cache);
417 |
418 | $this->innerStore->expects($this->once())
419 | ->method('clear');
420 |
421 | $this->cache->expects($this->once())
422 | ->method('deleteAll');
423 |
424 | $this->decorator->clear();
425 | }
426 |
427 | /**
428 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
429 | */
430 | public function testClearDoesNotDeleteAllFromCacheIfClearFails()
431 | {
432 | $this->cache = $this->getMock('Webmozart\KeyValueStore\Tests\\Fixtures\TestClearableCache');
433 | $this->decorator = new CachingDecorator($this->innerStore, $this->cache);
434 |
435 | $this->innerStore->expects($this->once())
436 | ->method('clear')
437 | ->willThrowException(new WriteException());
438 |
439 | $this->cache->expects($this->never())
440 | ->method('deleteAll');
441 |
442 | $this->decorator->clear();
443 | }
444 |
445 | public function testClearFlushesCache()
446 | {
447 | $this->cache = $this->getMock('Webmozart\KeyValueStore\Tests\\Fixtures\TestFlushableCache');
448 | $this->decorator = new CachingDecorator($this->innerStore, $this->cache);
449 |
450 | $this->innerStore->expects($this->once())
451 | ->method('clear');
452 |
453 | $this->cache->expects($this->once())
454 | ->method('flushAll');
455 |
456 | $this->decorator->clear();
457 | }
458 |
459 | public function testClearDeletesAllIfFlushableAndClearable()
460 | {
461 | $this->cache = $this->getMock('Webmozart\KeyValueStore\Tests\\Fixtures\TestClearableFlushableCache');
462 | $this->decorator = new CachingDecorator($this->innerStore, $this->cache);
463 |
464 | $this->innerStore->expects($this->once())
465 | ->method('clear');
466 |
467 | $this->cache->expects($this->once())
468 | ->method('deleteAll');
469 |
470 | $this->decorator->clear();
471 | }
472 |
473 | public function testKeysForwardsKeysFromStore()
474 | {
475 | $this->innerStore->expects($this->once())
476 | ->method('keys')
477 | ->willReturn(array('a', 'b', 'c'));
478 |
479 | $this->assertSame(array('a', 'b', 'c'), $this->decorator->keys());
480 | }
481 | }
482 |
--------------------------------------------------------------------------------
/tests/Decorator/CountableDecoratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Decorator;
13 |
14 | use Webmozart\KeyValueStore\Api\KeyValueStore;
15 | use Webmozart\KeyValueStore\Decorator\CountableDecorator;
16 |
17 | /**
18 | * @since 1.0
19 | *
20 | * @author Bernhard Schussek
21 | * @author Titouan Galopin
22 | */
23 | class CountableDecoratorTest extends AbstractDecoratorTest
24 | {
25 | protected function createDecorator(KeyValueStore $innerStore)
26 | {
27 | return new CountableDecorator($innerStore);
28 | }
29 |
30 | public function testCountCache()
31 | {
32 | $this->innerStore->expects($this->at(0))
33 | ->method('keys')
34 | ->willReturn(array('key1', 'key2'));
35 |
36 | $this->innerStore->expects($this->at(1))
37 | ->method('set')
38 | ->with('key3', 'value3');
39 |
40 | $this->innerStore->expects($this->at(2))
41 | ->method('keys')
42 | ->willReturn(array('key1', 'key2', 'key3'));
43 |
44 | $this->assertEquals(2, $this->decorator->count());
45 | $this->assertEquals(2, $this->decorator->count());
46 |
47 | $this->decorator->set('key3', 'value3');
48 |
49 | $this->assertEquals(3, $this->decorator->count());
50 | $this->assertEquals(3, $this->decorator->count());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Decorator/SortableDecoratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Decorator;
13 |
14 | use Webmozart\KeyValueStore\Api\KeyValueStore;
15 | use Webmozart\KeyValueStore\ArrayStore;
16 | use Webmozart\KeyValueStore\Decorator\SortableDecorator;
17 |
18 | /**
19 | * @since 1.0
20 | *
21 | * @author Bernhard Schussek
22 | * @author Titouan Galopin
23 | */
24 | class SortableDecoratorTest extends AbstractDecoratorTest
25 | {
26 | protected function createDecorator(KeyValueStore $innerStore)
27 | {
28 | return new SortableDecorator($innerStore);
29 | }
30 |
31 | public function testSortRegularStringKeys()
32 | {
33 | $store = new SortableDecorator(new ArrayStore());
34 | $store->set('c', 1);
35 | $store->set('a', 2);
36 | $store->set('b', 3);
37 |
38 | $store->sort();
39 |
40 | $this->assertSame(array(
41 | 'a' => 2,
42 | 'b' => 3,
43 | 'c' => 1,
44 | ), $store->getMultiple($store->keys()));
45 | }
46 |
47 | public function testSortRegularIntegerKeys()
48 | {
49 | $store = new SortableDecorator(new ArrayStore());
50 | $store->set(3, 'a');
51 | $store->set(1, 'b');
52 | $store->set(2, 'c');
53 |
54 | $store->sort();
55 |
56 | $this->assertSame(array(
57 | 1 => 'b',
58 | 2 => 'c',
59 | 3 => 'a',
60 | ), $store->getMultiple($store->keys()));
61 | }
62 |
63 | public function testSortStringStringKeys()
64 | {
65 | $store = new SortableDecorator(new ArrayStore());
66 | $store->set('c', 1);
67 | $store->set('a', 2);
68 | $store->set('b', 3);
69 |
70 | $store->sort(SORT_STRING);
71 |
72 | $this->assertSame(array(
73 | 'a' => 2,
74 | 'b' => 3,
75 | 'c' => 1,
76 | ), $store->getMultiple($store->keys()));
77 | }
78 |
79 | public function testSortNumericIntegerKeys()
80 | {
81 | $store = new SortableDecorator(new ArrayStore());
82 | $store->set(3, 'a');
83 | $store->set(1, 'b');
84 | $store->set(2, 'c');
85 |
86 | $store->sort(SORT_NUMERIC);
87 |
88 | $this->assertSame(array(
89 | 1 => 'b',
90 | 2 => 'c',
91 | 3 => 'a',
92 | ), $store->getMultiple($store->keys()));
93 | }
94 |
95 | public function testSortNaturalStringKeys()
96 | {
97 | if (PHP_VERSION_ID < 50400) {
98 | $this->markTestSkipped('SORT_NATURAL not available');
99 | }
100 |
101 | $store = new SortableDecorator(new ArrayStore());
102 |
103 | $store->set('10', 'c');
104 | $store->set('1', 'g');
105 | $store->set('100', 'a');
106 | $store->set('9', 'b');
107 | $store->set('7a', 'h');
108 | $store->set('7b', 'd');
109 | $store->set('_5', 'z');
110 |
111 | $store->sort(SORT_NATURAL);
112 |
113 | $this->assertSame(array(
114 | '1' => 'g',
115 | '7a' => 'h',
116 | '7b' => 'd',
117 | '9' => 'b',
118 | '10' => 'c',
119 | '100' => 'a',
120 | '_5' => 'z',
121 | ), $store->getMultiple($store->keys()));
122 | }
123 |
124 | public function testSortCaseInsensitiveStringKeys()
125 | {
126 | if (PHP_VERSION_ID < 50400) {
127 | $this->markTestSkipped('SORT_FLAG_CASE not available');
128 | }
129 |
130 | $store = new SortableDecorator(new ArrayStore());
131 |
132 | $store->set('_Ac', 'A');
133 | $store->set('abc', 'F');
134 | $store->set('_ab', 'G');
135 | $store->set('ABB', 'E');
136 | $store->set('Bce', 'C');
137 | $store->set('bcd', 'D');
138 | $store->set('bCf', 'E');
139 |
140 | $store->sort(SORT_STRING | SORT_FLAG_CASE);
141 |
142 | $this->assertSame(array(
143 | '_ab' => 'G',
144 | '_Ac' => 'A',
145 | 'ABB' => 'E',
146 | 'abc' => 'F',
147 | 'bcd' => 'D',
148 | 'Bce' => 'C',
149 | 'bCf' => 'E',
150 | ), $store->getMultiple($store->keys()));
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/tests/Fixtures/NotSerializable.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Fixtures;
13 |
14 | use BadMethodCallException;
15 |
16 | /**
17 | * @since 1.0
18 | *
19 | * @author Bernhard Schussek
20 | */
21 | class NotSerializable
22 | {
23 | public function __sleep()
24 | {
25 | throw new BadMethodCallException('You cannot serialize this object.');
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Fixtures/PrivateProperties.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Fixtures;
13 |
14 | /**
15 | * @since 1.0
16 | *
17 | * @author Bernhard Schussek
18 | */
19 | class PrivateProperties
20 | {
21 | private $property;
22 |
23 | public function __construct($property)
24 | {
25 | $this->property = $property;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Fixtures/TestClearableCache.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Fixtures;
13 |
14 | use Doctrine\Common\Cache\Cache;
15 | use Doctrine\Common\Cache\ClearableCache;
16 |
17 | /**
18 | * @since 1.0
19 | *
20 | * @author Bernhard Schussek
21 | */
22 | interface TestClearableCache extends Cache, ClearableCache
23 | {
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Fixtures/TestClearableFlushableCache.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Fixtures;
13 |
14 | use Doctrine\Common\Cache\Cache;
15 | use Doctrine\Common\Cache\ClearableCache;
16 | use Doctrine\Common\Cache\FlushableCache;
17 |
18 | /**
19 | * @since 1.0
20 | *
21 | * @author Bernhard Schussek
22 | */
23 | interface TestClearableFlushableCache extends Cache, ClearableCache, FlushableCache
24 | {
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Fixtures/TestException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Fixtures;
13 |
14 | use Exception;
15 |
16 | /**
17 | * @since 1.0
18 | *
19 | * @author Bernhard Schussek
20 | */
21 | class TestException extends Exception
22 | {
23 | }
24 |
--------------------------------------------------------------------------------
/tests/Fixtures/TestFlushableCache.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Fixtures;
13 |
14 | use Doctrine\Common\Cache\Cache;
15 | use Doctrine\Common\Cache\FlushableCache;
16 |
17 | /**
18 | * @since 1.0
19 | *
20 | * @author Bernhard Schussek
21 | */
22 | interface TestFlushableCache extends Cache, FlushableCache
23 | {
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Fixtures/file:
--------------------------------------------------------------------------------
1 | asdfasdfasdf
2 |
--------------------------------------------------------------------------------
/tests/NonSerializingBinaryMongoDbStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Webmozart\KeyValueStore\MongoDbStore;
15 |
16 | /**
17 | * @since 1.0
18 | *
19 | * @author Bernhard Schussek
20 | */
21 | class NonSerializingBinaryMongoDbStoreTest extends AbstractMongoDbStoreTest
22 | {
23 | protected function createStore()
24 | {
25 | $collection = $this->client->selectCollection(
26 | self::DATABASE_NAME,
27 | self::COLLECTION_NAME
28 | );
29 |
30 | return new MongoDbStore($collection, MongoDbStore::NO_SERIALIZE | MongoDbStore::SUPPORT_BINARY);
31 | }
32 |
33 | /**
34 | * @dataProvider provideScalarValues
35 | */
36 | public function testSetSupportsScalarValues($value)
37 | {
38 | // JSON cannot handle binary data
39 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
40 |
41 | parent::testSetSupportsScalarValues($value);
42 | }
43 |
44 | /**
45 | * @dataProvider provideObjectValues
46 | */
47 | public function testSetSupportsObjectValues($value)
48 | {
49 | // JSON cannot handle binary data
50 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
51 |
52 | parent::testSetSupportsObjectValues($value);
53 | }
54 |
55 | /**
56 | * @dataProvider provideArrayValues
57 | */
58 | public function testSetSupportsArrayValues($value)
59 | {
60 | // JSON cannot handle binary data
61 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
62 |
63 | parent::testSetSupportsArrayValues($value);
64 | }
65 |
66 | /**
67 | * @dataProvider provideBinaryArrayValues
68 | */
69 | public function testSetSupportsBinaryArrayValues($value)
70 | {
71 | // JSON cannot handle binary data
72 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
73 |
74 | parent::testSetSupportsBinaryArrayValues($value);
75 | }
76 |
77 | /**
78 | * @dataProvider provideBinaryObjectValues
79 | */
80 | public function testSetSupportsBinaryObjectValues($value)
81 | {
82 | // JSON cannot handle binary data
83 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
84 |
85 | parent::testSetSupportsBinaryObjectValues($value);
86 | }
87 |
88 | /**
89 | * @dataProvider provideInvalidValues
90 | */
91 | public function testSetThrowsExceptionIfValueNotSerializable($value)
92 | {
93 | // JSON cannot handle binary data
94 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
95 |
96 | parent::testSetThrowsExceptionIfValueNotSerializable($value);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/tests/NonSerializingMongoDbStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Webmozart\KeyValueStore\MongoDbStore;
15 |
16 | /**
17 | * @since 1.0
18 | *
19 | * @author Bernhard Schussek
20 | */
21 | class NonSerializingMongoDbStoreTest extends AbstractMongoDbStoreTest
22 | {
23 | protected function createStore()
24 | {
25 | $collection = $this->client->selectCollection(
26 | self::DATABASE_NAME,
27 | self::COLLECTION_NAME
28 | );
29 |
30 | return new MongoDbStore($collection, MongoDbStore::NO_SERIALIZE);
31 | }
32 |
33 | /**
34 | * @dataProvider provideBinaryValues
35 | */
36 | public function testSetSupportsBinaryValues($value)
37 | {
38 | // JSON cannot handle binary data
39 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
40 |
41 | parent::testSetSupportsBinaryValues($value);
42 | }
43 |
44 | /**
45 | * @dataProvider provideBinaryArrayValues
46 | */
47 | public function testSetSupportsBinaryArrayValues($value)
48 | {
49 | // JSON cannot handle binary data
50 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
51 |
52 | parent::testSetSupportsBinaryArrayValues($value);
53 | }
54 |
55 | /**
56 | * @dataProvider provideBinaryObjectValues
57 | */
58 | public function testSetSupportsBinaryObjectValues($value)
59 | {
60 | // JSON cannot handle binary data
61 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
62 |
63 | parent::testSetSupportsBinaryObjectValues($value);
64 | }
65 |
66 | /**
67 | * @dataProvider provideObjectValues
68 | */
69 | public function testSetSupportsObjectValues($value)
70 | {
71 | // JSON cannot handle binary data
72 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
73 |
74 | parent::testSetSupportsObjectValues($value);
75 | }
76 |
77 | /**
78 | * @dataProvider provideInvalidValues
79 | */
80 | public function testSetThrowsExceptionIfValueNotSerializable($value)
81 | {
82 | // JSON cannot handle binary data
83 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
84 |
85 | parent::testSetThrowsExceptionIfValueNotSerializable($value);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/NullStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use PHPUnit_Framework_TestCase;
15 | use Webmozart\KeyValueStore\NullStore;
16 |
17 | /**
18 | * @since 1.0
19 | *
20 | * @author Bernhard Schussek
21 | */
22 | class NullStoreTest extends PHPUnit_Framework_TestCase
23 | {
24 | public function testGetAlwaysReturnsDefault()
25 | {
26 | $store = new NullStore();
27 |
28 | $store->set('foo', 'bar');
29 |
30 | $this->assertSame('baz', $store->get('foo', 'baz'));
31 | }
32 |
33 | /**
34 | * @expectedException \Webmozart\KeyValueStore\Api\NoSuchKeyException
35 | */
36 | public function testGetAlwaysThrowsException()
37 | {
38 | $store = new NullStore();
39 |
40 | $store->set('foo', 'bar');
41 |
42 | $store->getOrFail('foo');
43 | }
44 |
45 | public function testGetMultipleAlwaysReturnsDefault()
46 | {
47 | $store = new NullStore();
48 |
49 | $store->set('foo', 'bar');
50 |
51 | $this->assertSame(array(
52 | 'foo' => 'default',
53 | 'bar' => 'default',
54 | ), $store->getMultiple(array('foo', 'bar'), 'default'));
55 | }
56 |
57 | /**
58 | * @expectedException \Webmozart\KeyValueStore\Api\NoSuchKeyException
59 | */
60 | public function testGetMultipleOrFailAlwaysThrowsException()
61 | {
62 | $store = new NullStore();
63 |
64 | $store->set('foo', 'bar');
65 |
66 | $store->getMultipleOrFail(array('foo'));
67 | }
68 |
69 | public function testExistsAlwaysReturnsFalse()
70 | {
71 | $store = new NullStore();
72 |
73 | $store->set('foo', 'bar');
74 |
75 | $this->assertFalse($store->exists('foo'));
76 | }
77 |
78 | public function testRemoveAlwaysReturnsFalse()
79 | {
80 | $store = new NullStore();
81 |
82 | $store->set('foo', 'bar');
83 |
84 | $this->assertFalse($store->remove('foo'));
85 | }
86 |
87 | public function testKeysAlwaysReturnsEmptyArray()
88 | {
89 | $store = new NullStore();
90 |
91 | $store->set('foo', 'bar');
92 |
93 | $this->assertSame(array(), $store->keys());
94 | }
95 |
96 | public function testClearDoesNothing()
97 | {
98 | $store = new NullStore();
99 |
100 | $store->set('foo1', 'bar1');
101 | $store->set('foo2', 'bar2');
102 | $store->clear();
103 |
104 | $this->assertSame(array(), $store->keys());
105 | }
106 |
107 | public function testSortDoesNothing()
108 | {
109 | $store = new NullStore();
110 |
111 | $store->set('foo1', 'bar1');
112 | $store->set('foo2', 'bar2');
113 | $store->sort();
114 |
115 | $this->assertSame(array(), $store->keys());
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/tests/PhpRedisStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Redis;
15 | use Webmozart\KeyValueStore\PhpRedisStore;
16 | use Webmozart\KeyValueStore\Tests\Fixtures\TestException;
17 |
18 | /**
19 | * @since 1.0
20 | *
21 | * @author Bernhard Schussek
22 | * @author Philipp Wahala
23 | */
24 | class PhpRedisStoreTest extends AbstractKeyValueStoreTest
25 | {
26 | private static $supported;
27 |
28 | public static function setUpBeforeClass()
29 | {
30 | parent::setUpBeforeClass();
31 |
32 | if (!class_exists('\Redis', false)) {
33 | self::$supported = false;
34 |
35 | return;
36 | }
37 |
38 | $redis = new Redis();
39 |
40 | self::$supported = @$redis->connect('127.0.0.1', 6379);
41 | }
42 |
43 | protected function setUp()
44 | {
45 | if (!self::$supported) {
46 | $this->markTestSkipped('PhpRedis is not available or Redis is not running.');
47 | }
48 |
49 | parent::setUp();
50 | }
51 |
52 | protected function createStore()
53 | {
54 | return new PhpRedisStore();
55 | }
56 |
57 | /**
58 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
59 | * @expectedExceptionMessage I failed!
60 | */
61 | public function testSetThrowsWriteExceptionIfWriteFails()
62 | {
63 | $exception = new TestException('I failed!');
64 |
65 | $redis = $this->getMockBuilder('Redis')
66 | ->disableOriginalConstructor()
67 | ->getMock();
68 |
69 | $redis->expects($this->once())
70 | ->method('set')
71 | ->willThrowException($exception);
72 |
73 | $store = new PhpRedisStore($redis);
74 | $store->set('key', 'value');
75 | }
76 |
77 | /**
78 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
79 | * @expectedExceptionMessage I failed!
80 | */
81 | public function testRemoveThrowsWriteExceptionIfWriteFails()
82 | {
83 | $exception = new TestException('I failed!');
84 |
85 | $redis = $this->getMockBuilder('Redis')
86 | ->disableOriginalConstructor()
87 | ->getMock();
88 |
89 | $redis->expects($this->once())
90 | ->method('del')
91 | ->willThrowException($exception);
92 |
93 | $store = new PhpRedisStore($redis);
94 | $store->remove('key');
95 | }
96 |
97 | /**
98 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
99 | * @expectedExceptionMessage I failed!
100 | */
101 | public function testClearThrowsWriteExceptionIfWriteFails()
102 | {
103 | $exception = new TestException('I failed!');
104 |
105 | $redis = $this->getMockBuilder('Redis')
106 | ->disableOriginalConstructor()
107 | ->getMock();
108 |
109 | $redis->expects($this->once())
110 | ->method('flushdb')
111 | ->willThrowException($exception);
112 |
113 | $store = new PhpRedisStore($redis);
114 | $store->clear();
115 | }
116 |
117 | /**
118 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
119 | * @expectedExceptionMessage I failed!
120 | */
121 | public function testGetThrowsReadExceptionIfReadFails()
122 | {
123 | $exception = new TestException('I failed!');
124 |
125 | $redis = $this->getMockBuilder('Redis')
126 | ->disableOriginalConstructor()
127 | ->getMock();
128 |
129 | $redis->expects($this->once())
130 | ->method('get')
131 | ->willThrowException($exception);
132 |
133 | $store = new PhpRedisStore($redis);
134 | $store->get('key');
135 | }
136 |
137 | /**
138 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
139 | */
140 | public function testGetThrowsExceptionIfNotUnserializable()
141 | {
142 | $redis = $this->getMockBuilder('Redis')
143 | ->disableOriginalConstructor()
144 | ->getMock();
145 |
146 | $redis->expects($this->once())
147 | ->method('get')
148 | ->willReturn('foobar');
149 |
150 | $store = new PhpRedisStore($redis);
151 | $store->get('key');
152 | }
153 |
154 | /**
155 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
156 | * @expectedExceptionMessage I failed!
157 | */
158 | public function testGetOrFailThrowsReadExceptionIfReadFails()
159 | {
160 | $exception = new TestException('I failed!');
161 |
162 | $redis = $this->getMockBuilder('Redis')
163 | ->disableOriginalConstructor()
164 | ->getMock();
165 |
166 | $redis->expects($this->once())
167 | ->method('get')
168 | ->willThrowException($exception);
169 |
170 | $store = new PhpRedisStore($redis);
171 | $store->getOrFail('key');
172 | }
173 |
174 | /**
175 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
176 | */
177 | public function testGetOrFailThrowsExceptionIfNotUnserializable()
178 | {
179 | $redis = $this->getMockBuilder('Redis')
180 | ->disableOriginalConstructor()
181 | ->getMock();
182 |
183 | $redis->expects($this->once())
184 | ->method('get')
185 | ->willReturn('foobar');
186 |
187 | $store = new PhpRedisStore($redis);
188 | $store->getOrFail('key');
189 | }
190 |
191 | /**
192 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
193 | * @expectedExceptionMessage I failed!
194 | */
195 | public function testGetMultipleThrowsReadExceptionIfReadFails()
196 | {
197 | $exception = new TestException('I failed!');
198 |
199 | $redis = $this->getMockBuilder('Redis')
200 | ->disableOriginalConstructor()
201 | ->getMock();
202 |
203 | $redis->expects($this->once())
204 | ->method('getMultiple')
205 | ->willThrowException($exception);
206 |
207 | $store = new PhpRedisStore($redis);
208 | $store->getMultiple(array('key'));
209 | }
210 |
211 | /**
212 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
213 | */
214 | public function testGetMultipleThrowsExceptionIfNotUnserializable()
215 | {
216 | $redis = $this->getMockBuilder('Redis')
217 | ->disableOriginalConstructor()
218 | ->getMock();
219 |
220 | $redis->expects($this->once())
221 | ->method('getMultiple')
222 | ->willReturn(array('foobar'));
223 |
224 | $store = new PhpRedisStore($redis);
225 | $store->getMultiple(array('key'));
226 | }
227 |
228 | /**
229 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
230 | * @expectedExceptionMessage I failed!
231 | */
232 | public function testGetMultipleOrFailThrowsReadExceptionIfReadFails()
233 | {
234 | $exception = new TestException('I failed!');
235 |
236 | $redis = $this->getMockBuilder('Redis')
237 | ->disableOriginalConstructor()
238 | ->getMock();
239 |
240 | $redis->expects($this->once())
241 | ->method('getMultiple')
242 | ->willThrowException($exception);
243 |
244 | $store = new PhpRedisStore($redis);
245 | $store->getMultipleOrFail(array('key'));
246 | }
247 |
248 | /**
249 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
250 | */
251 | public function testGetMultipleOrFailThrowsExceptionIfNotUnserializable()
252 | {
253 | $redis = $this->getMockBuilder('Redis')
254 | ->disableOriginalConstructor()
255 | ->getMock();
256 |
257 | $redis->expects($this->once())
258 | ->method('getMultiple')
259 | ->willReturn(array('foobar'));
260 |
261 | $store = new PhpRedisStore($redis);
262 | $store->getMultipleOrFail(array('key'));
263 | }
264 |
265 | /**
266 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
267 | * @expectedExceptionMessage I failed!
268 | */
269 | public function testExistsThrowsReadExceptionIfReadFails()
270 | {
271 | $exception = new TestException('I failed!');
272 |
273 | $redis = $this->getMockBuilder('Redis')
274 | ->disableOriginalConstructor()
275 | ->getMock();
276 |
277 | $redis->expects($this->once())
278 | ->method('exists')
279 | ->willThrowException($exception);
280 |
281 | $store = new PhpRedisStore($redis);
282 | $store->exists('key');
283 | }
284 |
285 | /**
286 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
287 | * @expectedExceptionMessage I failed!
288 | */
289 | public function testKeysThrowsReadExceptionIfReadFails()
290 | {
291 | $exception = new TestException('I failed!');
292 |
293 | $redis = $this->getMockBuilder('Redis')
294 | ->disableOriginalConstructor()
295 | ->getMock();
296 |
297 | $redis->expects($this->once())
298 | ->method('keys')
299 | ->willThrowException($exception);
300 |
301 | $store = new PhpRedisStore($redis);
302 | $store->keys();
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/tests/PredisStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Predis\Client;
15 | use Predis\Connection\ConnectionException;
16 | use Webmozart\KeyValueStore\PredisStore;
17 | use Webmozart\KeyValueStore\Tests\Fixtures\TestException;
18 |
19 | /**
20 | * @since 1.0
21 | *
22 | * @author Bernhard Schussek
23 | */
24 | class PredisStoreTest extends AbstractKeyValueStoreTest
25 | {
26 | private static $supported;
27 |
28 | public static function setUpBeforeClass()
29 | {
30 | parent::setUpBeforeClass();
31 |
32 | $client = new Client();
33 |
34 | try {
35 | $client->connect();
36 | $client->disconnect();
37 | self::$supported = true;
38 | } catch (ConnectionException $e) {
39 | self::$supported = false;
40 | }
41 | }
42 |
43 | protected function setUp()
44 | {
45 | if (!self::$supported) {
46 | $this->markTestSkipped('Redis is not running.');
47 | }
48 |
49 | parent::setUp();
50 | }
51 |
52 | protected function createStore()
53 | {
54 | return new PredisStore();
55 | }
56 |
57 | /**
58 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
59 | * @expectedExceptionMessage I failed!
60 | */
61 | public function testSetThrowsWriteExceptionIfWriteFails()
62 | {
63 | $exception = new TestException('I failed!');
64 |
65 | $client = $this->getMock('Predis\ClientInterface');
66 |
67 | $client->expects($this->once())
68 | ->method('__call')
69 | ->with('set')
70 | ->willThrowException($exception);
71 |
72 | $store = new PredisStore($client);
73 | $store->set('key', 'value');
74 | }
75 |
76 | /**
77 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
78 | * @expectedExceptionMessage I failed!
79 | */
80 | public function testRemoveThrowsWriteExceptionIfWriteFails()
81 | {
82 | $exception = new TestException('I failed!');
83 |
84 | $client = $this->getMock('Predis\ClientInterface');
85 |
86 | $client->expects($this->once())
87 | ->method('__call')
88 | ->with('del')
89 | ->willThrowException($exception);
90 |
91 | $store = new PredisStore($client);
92 | $store->remove('key');
93 | }
94 |
95 | /**
96 | * @expectedException \Webmozart\KeyValueStore\Api\WriteException
97 | * @expectedExceptionMessage I failed!
98 | */
99 | public function testClearThrowsWriteExceptionIfWriteFails()
100 | {
101 | $exception = new TestException('I failed!');
102 |
103 | $client = $this->getMock('Predis\ClientInterface');
104 |
105 | $client->expects($this->once())
106 | ->method('__call')
107 | ->with('flushdb')
108 | ->willThrowException($exception);
109 |
110 | $store = new PredisStore($client);
111 | $store->clear();
112 | }
113 |
114 | /**
115 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
116 | * @expectedExceptionMessage I failed!
117 | */
118 | public function testGetThrowsReadExceptionIfReadFails()
119 | {
120 | $exception = new TestException('I failed!');
121 |
122 | $client = $this->getMock('Predis\ClientInterface');
123 |
124 | $client->expects($this->once())
125 | ->method('__call')
126 | ->with('get')
127 | ->willThrowException($exception);
128 |
129 | $store = new PredisStore($client);
130 | $store->get('key');
131 | }
132 |
133 | /**
134 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
135 | */
136 | public function testGetThrowsExceptionIfNotUnserializable()
137 | {
138 | $client = $this->getMock('Predis\ClientInterface');
139 |
140 | $client->expects($this->once())
141 | ->method('__call')
142 | ->with('get')
143 | ->willReturn('foobar');
144 |
145 | $store = new PredisStore($client);
146 | $store->get('key');
147 | }
148 |
149 | /**
150 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
151 | * @expectedExceptionMessage I failed!
152 | */
153 | public function testGetOrFailThrowsReadExceptionIfReadFails()
154 | {
155 | $exception = new TestException('I failed!');
156 |
157 | $client = $this->getMock('Predis\ClientInterface');
158 |
159 | $client->expects($this->once())
160 | ->method('__call')
161 | ->with('get')
162 | ->willThrowException($exception);
163 |
164 | $store = new PredisStore($client);
165 | $store->getOrFail('key');
166 | }
167 |
168 | /**
169 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
170 | */
171 | public function testGetOrFailThrowsExceptionIfNotUnserializable()
172 | {
173 | $client = $this->getMock('Predis\ClientInterface');
174 |
175 | $client->expects($this->once())
176 | ->method('__call')
177 | ->with('get')
178 | ->willReturn('foobar');
179 |
180 | $store = new PredisStore($client);
181 | $store->getOrFail('key');
182 | }
183 |
184 | /**
185 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
186 | * @expectedExceptionMessage I failed!
187 | */
188 | public function testGetMultipleThrowsReadExceptionIfReadFails()
189 | {
190 | $exception = new TestException('I failed!');
191 |
192 | $client = $this->getMock('Predis\ClientInterface');
193 |
194 | $client->expects($this->once())
195 | ->method('__call')
196 | ->with('mget')
197 | ->willThrowException($exception);
198 |
199 | $store = new PredisStore($client);
200 | $store->getMultiple(array('key'));
201 | }
202 |
203 | /**
204 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
205 | */
206 | public function testGetMultipleThrowsExceptionIfNotUnserializable()
207 | {
208 | $client = $this->getMock('Predis\ClientInterface');
209 |
210 | $client->expects($this->once())
211 | ->method('__call')
212 | ->with('mget')
213 | ->willReturn(array('foobar'));
214 |
215 | $store = new PredisStore($client);
216 | $store->getMultiple(array('key'));
217 | }
218 |
219 | /**
220 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
221 | * @expectedExceptionMessage I failed!
222 | */
223 | public function testGetMultipleOrFailThrowsReadExceptionIfReadFails()
224 | {
225 | $exception = new TestException('I failed!');
226 |
227 | $client = $this->getMock('Predis\ClientInterface');
228 |
229 | $client->expects($this->once())
230 | ->method('__call')
231 | ->with('mget')
232 | ->willThrowException($exception);
233 |
234 | $store = new PredisStore($client);
235 | $store->getMultipleOrFail(array('key'));
236 | }
237 |
238 | /**
239 | * @expectedException \Webmozart\KeyValueStore\Api\UnserializationFailedException
240 | */
241 | public function testGetMultipleOrFailThrowsExceptionIfNotUnserializable()
242 | {
243 | $client = $this->getMock('Predis\ClientInterface');
244 |
245 | $client->expects($this->once())
246 | ->method('__call')
247 | ->with('mget')
248 | ->willReturn(array('foobar'));
249 |
250 | $store = new PredisStore($client);
251 | $store->getMultipleOrFail(array('key'));
252 | }
253 |
254 | /**
255 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
256 | * @expectedExceptionMessage I failed!
257 | */
258 | public function testExistsThrowsReadExceptionIfReadFails()
259 | {
260 | $exception = new TestException('I failed!');
261 |
262 | $client = $this->getMock('Predis\ClientInterface');
263 |
264 | $client->expects($this->once())
265 | ->method('__call')
266 | ->with('exists')
267 | ->willThrowException($exception);
268 |
269 | $store = new PredisStore($client);
270 | $store->exists('key');
271 | }
272 |
273 | /**
274 | * @expectedException \Webmozart\KeyValueStore\Api\ReadException
275 | * @expectedExceptionMessage I failed!
276 | */
277 | public function testKeysThrowsReadExceptionIfReadFails()
278 | {
279 | $exception = new TestException('I failed!');
280 |
281 | $client = $this->getMock('Predis\ClientInterface');
282 |
283 | $client->expects($this->once())
284 | ->method('__call')
285 | ->with('keys')
286 | ->willThrowException($exception);
287 |
288 | $store = new PredisStore($client);
289 | $store->keys();
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/tests/SerializingArrayStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Webmozart\KeyValueStore\SerializingArrayStore;
15 |
16 | /**
17 | * @since 1.0
18 | *
19 | * @author Bernhard Schussek
20 | */
21 | class SerializingArrayStoreTest extends ArrayStoreTest
22 | {
23 | protected function createStore()
24 | {
25 | return new SerializingArrayStore();
26 | }
27 |
28 | protected function createPopulatedStore(array $values)
29 | {
30 | return new SerializingArrayStore($values);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/SerializingBinaryMongoDbStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Webmozart\KeyValueStore\MongoDbStore;
15 |
16 | /**
17 | * @since 1.0
18 | *
19 | * @author Bernhard Schussek
20 | */
21 | class SerializingBinaryMongoDbStoreTest extends AbstractMongoDbStoreTest
22 | {
23 | protected function createStore()
24 | {
25 | $collection = $this->client->selectCollection(
26 | self::DATABASE_NAME,
27 | self::COLLECTION_NAME
28 | );
29 |
30 | return new MongoDbStore($collection, MongoDbStore::SUPPORT_BINARY);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/SerializingMongoDbStoreTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests;
13 |
14 | use Webmozart\KeyValueStore\MongoDbStore;
15 |
16 | /**
17 | * @since 1.0
18 | *
19 | * @author Bernhard Schussek
20 | */
21 | class SerializingMongoDbStoreTest extends AbstractMongoDbStoreTest
22 | {
23 | protected function createStore()
24 | {
25 | $collection = $this->client->selectCollection(
26 | self::DATABASE_NAME,
27 | self::COLLECTION_NAME
28 | );
29 |
30 | return new MongoDbStore($collection);
31 | }
32 |
33 | /**
34 | * @dataProvider provideBinaryValues
35 | */
36 | public function testSetSupportsBinaryValues($value)
37 | {
38 | // JSON cannot handle binary data
39 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
40 |
41 | parent::testSetSupportsBinaryValues($value);
42 | }
43 |
44 | /**
45 | * @dataProvider provideBinaryArrayValues
46 | */
47 | public function testSetSupportsBinaryArrayValues($value)
48 | {
49 | // JSON cannot handle binary data
50 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
51 |
52 | parent::testSetSupportsBinaryArrayValues($value);
53 | }
54 |
55 | /**
56 | * @dataProvider provideBinaryObjectValues
57 | */
58 | public function testSetSupportsBinaryObjectValues($value)
59 | {
60 | // JSON cannot handle binary data
61 | $this->setExpectedException('\Webmozart\KeyValueStore\Api\UnsupportedValueException');
62 |
63 | parent::testSetSupportsBinaryObjectValues($value);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/tests/Util/SerializerTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Webmozart\KeyValueStore\Tests\Util;
13 |
14 | use PHPUnit_Framework_TestCase;
15 | use Webmozart\KeyValueStore\Api\SerializationFailedException;
16 | use Webmozart\KeyValueStore\Api\UnserializationFailedException;
17 | use Webmozart\KeyValueStore\Tests\Fixtures\NotSerializable;
18 | use Webmozart\KeyValueStore\Util\Serializer;
19 |
20 | /**
21 | * @since 1.0
22 | *
23 | * @author Bernhard Schussek
24 | */
25 | class SerializerTest extends PHPUnit_Framework_TestCase
26 | {
27 | public function testSerialize()
28 | {
29 | $data = (object) array('foo' => 'bar');
30 |
31 | $this->assertEquals($data, Serializer::unserialize(Serializer::serialize($data)));
32 | }
33 |
34 | public function testSerializeFailsIfResource()
35 | {
36 | $data = fopen(__FILE__, 'r');
37 |
38 | try {
39 | Serializer::serialize($data);
40 | $this->fail('Expected a SerializationFailedException');
41 | } catch (SerializationFailedException $e) {
42 | }
43 |
44 | $this->assertTrue(true, 'Exception caught');
45 | }
46 |
47 | public function testSerializeFailsIfNotSerializable()
48 | {
49 | $data = new NotSerializable();
50 |
51 | try {
52 | Serializer::serialize($data);
53 | $this->fail('Expected a SerializationFailedException');
54 | } catch (SerializationFailedException $e) {
55 | }
56 |
57 | $this->assertTrue(true, 'Exception caught');
58 | }
59 |
60 | public function testUnserializeFailsIfInvalidString()
61 | {
62 | try {
63 | Serializer::unserialize('foobar');
64 | $this->fail('Expected an UnserializationFailedException');
65 | } catch (UnserializationFailedException $e) {
66 | $this->assertContains('Error at offset 0', $e->getMessage());
67 | }
68 | }
69 |
70 | public function testUnserializeFailsIfNoString()
71 | {
72 | try {
73 | Serializer::unserialize(1234);
74 | $this->fail('Expected an UnserializationFailedException');
75 | } catch (UnserializationFailedException $e) {
76 | $this->assertContains('Could not unserialize value of type integer.', $e->getMessage());
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------