├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── example
├── basic.php
├── cacheariumprobe.png
└── debugprobe.php
├── phpunit.xml
├── src
├── Backend
│ ├── CacheAPC.php
│ ├── CacheFilesystem.php
│ ├── CacheMemcached.php
│ ├── CacheNull.php
│ ├── CacheRAM.php
│ └── external
│ │ ├── Lite.php
│ │ └── Timed.php
├── Cache.php
├── CacheAbstract.php
├── CacheData.php
├── CacheKey.php
├── CacheLogEnum.php
├── Cached.php
├── CachedObject.php
└── Exceptions
│ ├── CacheInvalidBackendException.php
│ ├── CacheInvalidDataException.php
│ ├── CacheInvalidParameterException.php
│ ├── CacheKeyClashException.php
│ ├── CacheStoreFailure.php
│ ├── CacheUnsupportedOperation.php
│ └── NotCachedException.php
└── test
├── CacheBasicTest.php
├── CacheCallbackTest.php
├── CacheDataTest.php
├── CacheInterfaceTest.php
├── CacheKeyTest.php
├── CacheLogEnumTest.php
├── CacheMemcachedTest.php
├── CacheNullTest.php
├── CacheRamTest.php
├── CacheStartEndTest.php
└── CacheTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | *.phar
2 | vendor/
3 | nbproject/
4 | .settings/
5 | .buildpath
6 | .project
7 | composer.lock
8 | /html/
9 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | excluded_paths: [test/*, example/*]
3 |
4 | checks:
5 | php:
6 | code_rating: true
7 | duplication: true
8 | tools:
9 | php_cs_fixer: true
10 | php_code_sniffer: true
11 | php_cpd: true
12 | php_hhvm: true
13 | php_mess_detector: true
14 | php_analyzer: true
15 | php_pdepend: true
16 | sensiolabs_security_checker: true
17 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.4
4 | - 5.5
5 | - 5.6
6 |
7 | before_script:
8 | - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
9 | - composer self-update || true
10 | - composer --prefer-source --dev install
11 |
12 | script:
13 | - vendor/bin/phpunit --debug --coverage-clover=coverage.clover
14 |
15 | sudo: false
16 |
17 | services:
18 | - memcached
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Corollarium
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/Corollarium/cachearium)
2 | [](https://scrutinizer-ci.com/g/Corollarium/cachearium/?branch=master)
3 | [](https://packagist.org/packages/corollarium/cachearium)
4 | [](https://packagist.org/packages/corollarium/cachearium)
5 | [](https://packagist.org/packages/corollarium/cachearium)
6 |
7 | # Cachearium
8 |
9 | High level cache in your PHP applications. What, another one? Nope, this one is better. Fast, simple and with easy invalidation. Includes:
10 |
11 | - recursive cache system, all the nested russian dolls you ever wanted
12 | - easy to integrate with your [existing classes](#cache-associated-with-an-object-model-that-you-can-easily-clean)
13 | - key based cache [expiration](https://signalvnoise.com/posts/3113), no more headaches to invalidate stuff
14 | - [multiple dependencies](#store-a-value-with-multiple-dependencies)
15 | - lifetime expiration, because stuff rots
16 | - [low level cache](#store-a-single-value-and-invalidate-it) storage access, when you want to go raw
17 | - lots of [examples](https://github.com/Corollarium/cachearium/tree/master/example) and [tests](https://github.com/Corollarium/cachearium/tree/master/test) ready to copy/paste
18 | - [variable fragments](#cache-with-a-variable-fragment) for things that are almost the same but not quite
19 | - [pluggable backend modules](#backends): RAM, Memcached, Filesystem and you can add your own
20 | - [detailed logs](#to-see-a-detailed-log) for profiling and debugging, and also see what is cached [live in your webpage](#live-cache-probes)
21 |
22 | Cachearium was developed by [Corollarium](https://corollarium.com) because we needed a great cache system.
23 |
24 | # Installation
25 |
26 | ## Composer
27 |
28 | Add this to your composer.json: [see packagist](https://packagist.org/packages/corollarium/cachearium)
29 |
30 | If you prefer the cutting edge version, with only the freshest bugs:
31 |
32 | ```
33 | "corollarium/cachearium": "dev-master"
34 | ```
35 |
36 | ## Manual
37 |
38 | No composer? No fret!
39 |
40 | - Download the package
41 | - Include `require_once('cachearium/Cached.php');`
42 |
43 | # Debug and profile
44 |
45 | ## Live cache probes
46 |
47 | 
48 |
49 | Image showing cache debug probes. Pink areas are not cached. Green areas are cached. Note that they are nested. The red squares show the dialog with information about each cache hit/miss so you can easily see the cache key, which backend was used and other relevant information.
50 |
51 | Probes are only available when you call start()/end().
52 |
53 | ```php
54 |
55 | $cache::$debugOnPage = true;
56 |
57 | ...
58 | if (!$cache->start($key)) {
59 | // some stuff
60 | $cache->end();
61 | }
62 | ...
63 |
64 | // this is required for the probes
65 | $cache->footerDebug();
66 |
67 | ```
68 |
69 | ## To see a detailed log
70 |
71 | ```php
72 |
73 | $cache->setLog(true);
74 | ....
75 | $cache->report(); // will print a detailed report
76 | ```
77 |
78 | # Use cases/examples
79 |
80 | See the example/ directory, because it's all there for you. Really, just point a webserver
81 | there and play.
82 |
83 | ## Store a single value and invalidate it
84 |
85 | This is basic storage.
86 |
87 | ```php
88 | $data = 'xxxx';
89 |
90 | // store
91 | $cache = CacheAbstract::factory('your backend');
92 | $cache->store($data, new CacheKey('Namespace', 'Subname'));
93 |
94 | // get it later
95 | try {
96 | $data2 = $cache->get(new CacheKey('Namespace', 'Subname'));
97 | // $data2 == 'xxxx';
98 | }
99 | catch (NotCachedException($e)) {
100 | // handle not cached
101 | }
102 |
103 | // store new value with automatic invalidation
104 | $data = 'yyy';
105 | $cache->store($data, new CacheKey('Namespace', 'Subname'));
106 | ```
107 |
108 | ## Store using CacheData
109 |
110 | CacheData provides a more sophisticated class to store values.
111 |
112 |
113 | ```php
114 | $data = 'xxxx';
115 |
116 | // store
117 | $cache = CacheAbstract::factory('your backend');
118 | $cache->storeData(new CacheData(new CacheKey('Namespace', 'Subname'), $data));
119 |
120 | // get it later
121 | try {
122 | $data2 = $cache->getData(new CacheKey('Namespace', 'Subname'));
123 | // $data2->getFirstData() == 'xxxxx'
124 | }
125 | catch (NotCachedException($e)) {
126 | // handle not cached
127 | }
128 |
129 | // store new value with automatic invalidation
130 | $lifeTime = 3000;
131 | $fancyData = 'someData';
132 | $cache->storeData(new CacheData(new CacheKey('Namespace', 'Subname'), $fancyData), $lifeTime);
133 | ```
134 |
135 | ## Store a value with multiple dependencies
136 |
137 | You can have multiple dependencies so you can invalidate all cache data that
138 | relate to a certain key.
139 |
140 | ```php
141 | $cache = CacheAbstract::factory('your backend');
142 |
143 | // create a storage key and bucket
144 | $key = new CacheKey('Namespace', 'Subname');
145 | $cd = new CacheData($key, $data);
146 |
147 | // add dependencies. setDependencies will generate immediately, avoiding races.
148 | // otherwise you find results, the DB changes in another process and you get a
149 | // stale dependency. note that addDependencies does not do that, leaving the
150 | // hash to be calculated later
151 | $dep = new CacheKey('Namespace', 'SomeDep');
152 | $cd->setDependencies([$dep]);
153 |
154 | // store.
155 | $data = 'xxxx';
156 | $cache->storeData($cd);
157 |
158 | // at this point $cache->get($key) will return your data
159 |
160 | // invalidate a dependency. This will be called on your write method.
161 | $cache->invalidate($dep);
162 |
163 | // at this point $cache->get($key) will throw an NotCachedException
164 | ```
165 |
166 | ### Example: Store searches and invalidate them when an attribute is written to
167 |
168 | ```php
169 | function someSearch() {
170 | $key = new CacheKey('search', 'someSearch'); // keys for this data
171 | $cache = CacheAbstract::factory('backend');
172 | try {
173 | return $cache->get($key); // TODO
174 | }
175 | catch (NotCachedException($e)) {
176 | // handle not cached below
177 | }
178 |
179 | $searchdata = getSomeData(); // results of some horrible slow query
180 |
181 | // attributes that are used in this search
182 | $dependencies = [
183 | new CacheKey('attribute', 'name'),
184 | new CacheKey('attribute', 'description')
185 | new CacheKey('attribute', 'cost')
186 | ];
187 |
188 | // create cache data
189 | $cd =
190 | $cache->storeData(
191 | (new CacheData($key, $searchdata))
192 | ->setDependencies($dependencies);
193 | );
194 |
195 | return $searchdata;
196 | }
197 |
198 | function writeSomeStuff() {
199 | // changed or added some attribute value in some object
200 |
201 | $cache = CacheAbstract::factory('backend');
202 | $cache->invalidate(new CacheKey('attribute', 'name')); // invalidates any cache that depends on this key
203 | }
204 | ```
205 |
206 | ## Cache associated with an object/model that you can easily clean
207 |
208 | It's likely that you have a MVC application. Model classes can easily cache data
209 |
210 | ```php
211 | class Foo extends Model {
212 | use Cached;
213 |
214 | /**
215 | * Unique object id in your application (primary key)
216 | */
217 | public function getId() {
218 | return $this->id;
219 | }
220 |
221 | public function cacheClean() {
222 | $cache = CacheAbstract::factory('backend');
223 | $cache->clean('Foo', $this->getId());
224 | }
225 |
226 | public function save() {
227 | // save stuff on db
228 | $this->cacheClean(); // clear any cache associated with this item
229 | }
230 |
231 | public function cacheStore($data, $key) {
232 | $cache = CacheAbstract::factory('backend');
233 | return $cache->save($data, 'Foo', $this->getId(), $key);
234 | }
235 |
236 | public function cacheGet($key) {
237 | $cache = CacheAbstract::factory('backend');
238 | return $cache->get('Foo', $this->getId(), $key);
239 | }
240 | }
241 | ```
242 |
243 | ## Nested cache for contents. Useful for generating HTML made of fragments
244 |
245 | This uses the russian doll approach to bubble up any invalidations. This means that
246 | if you have a list of items and you change one of them, you only invalidate its own
247 | cache entry and the entry for the whole list. You can regenerate the list with a
248 | single DB hit.
249 |
250 | ```php
251 |
252 | $cache = CacheAbstract::factory('your backend');
253 |
254 | $cache->start(new CacheKey('main'));
255 |
256 | $cache->start(new CacheKey('header'));
257 | $cache->end();
258 |
259 | foreach ($somestuff as $stuff) {
260 | $stuff->render();
261 | }
262 |
263 | $cache->start(new CacheKey('footer'));
264 |
265 | $cache->end();
266 | $cache->end();
267 |
268 | class Stuff {
269 | public function getCacheKey() {
270 | return new CacheKey('stuff', $this->getId());
271 | }
272 |
273 | public function write() {
274 | write_some_stuff();
275 |
276 | $cache = CacheAbstract::factory('your backend');
277 | $cache->clean($this->getCacheKey());
278 | }
279 |
280 | public function render() {
281 | $cache = CacheAbstract::factory('your backend');
282 | $cache->start($stuff->getCacheKey()->setSub('render'));
283 |
284 | $html = '
some html here
';
285 |
286 | // other dependency if you have it
287 | $cache->addDependency($otherstuff->getCacheKey());
288 |
289 | $cache->end();
290 | }
291 | }
292 | ```
293 |
294 | ## Cache with a variable fragment
295 | This is how to handle something such as a site header, that is almost completely the
296 | same except for a small part that varies for each user.
297 |
298 | ```php
299 |
300 | function callbackTesterStart() {
301 | return rand();
302 | }
303 |
304 | $key = new CacheKey("startcallback", 1);
305 | $cache->start($key);
306 | echo "something ";
307 |
308 | // this will never be cached, every call to start() will use the rest
309 | // of the cached data and call the callback everytime for the variable data
310 | $cache->appendCallback('callbackTesterStart');
311 |
312 | // everything goes on as planned here
313 | echo " otherthing";
314 | $output = $cache->end(false);
315 | ```
316 |
317 | ## Always add something specific to the cache keys
318 |
319 | Let's say for example you have a multi-language website. Caching fragments will
320 | always need to add the language as part of the key. Cachearium provides a simple
321 | way to do this by creating a special function:
322 |
323 | ```php
324 | function application_cacheDependencies() {
325 | // can return an array or a string
326 | return [Application::getLanguage(), somethingelse()];
327 | }
328 | ```
329 |
330 | This will be added automatically to your keys in every call to start(). If you
331 | need to override this for a single call, use recursiveStart() instead.
332 |
333 | ## Cleaning the house
334 |
335 | You can clear the entire cache with `$cache->clear()` or `CacheAbstract::clearAll()`.
336 |
337 | # Backends
338 |
339 | ## Null
340 |
341 | Does nothing. You can use it to turn off your caches for tests without changing
342 | any code calls.
343 |
344 | ## RAM
345 |
346 | Caches in RAM, for the duration of the request only. Useful for quick caches that
347 | should not persist between requests.
348 |
349 | ## Memcache
350 |
351 | Uses Memcache as a backend, and stores data in RAM temporarily to avoid repeated
352 | requests in the same run.
353 |
354 | ## Filesystem
355 |
356 | Stores in the filesystem.
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "corollarium/cachearium",
3 | "description": "Cache in your PHP applications. Fast, simple and with easy invalidation.",
4 | "license": "MIT",
5 | "type": "library",
6 | "homepage": "https://github.com/Corollarium/cachearium",
7 | "keywords": ["PHP", "cache"],
8 | "minimum-stability": "dev",
9 | "authors": [
10 | {
11 | "name": "Corollarium",
12 | "email": "email@corollarium.com"
13 | }
14 | ],
15 | "require": {
16 | "php": ">=5.4.0"
17 | },
18 | "require-dev": {
19 | "phpunit/phpunit": "4.1.*"
20 | },
21 | "suggest": {
22 | "ext-memcached": ">=2.1.0"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "Cachearium\\": "src/"
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/example/basic.php:
--------------------------------------------------------------------------------
1 | setLog(true);
13 |
14 | ?>
15 |
16 |
17 | Cachearium debug probe
18 |
19 |
45 |
46 |
47 | start(new CacheKey("outside", 1))) {
54 | // not cached
55 | echo '';
56 | echo "some random bla bla" . rand();
57 | echo '
';
58 | $cache->end();
59 | }
60 | }
61 |
62 | echo 'first time is not cached
';
63 | someCachedStuff();
64 |
65 | echo 'second time is cached
';
66 | someCachedStuff();
67 |
68 | echo '
';
69 |
70 | $cache->footerDebug();
71 |
72 | $cache->report();
73 |
74 | ?>
75 |
76 |
--------------------------------------------------------------------------------
/example/cacheariumprobe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Corollarium/cachearium/bf1bbd73cc7c85e96fdeed7e38e4216f23cebbc9/example/cacheariumprobe.png
--------------------------------------------------------------------------------
/example/debugprobe.php:
--------------------------------------------------------------------------------
1 | setLog(true);
13 |
14 | ?>
15 |
16 |
17 | Cachearium debug probe
18 |
19 |
44 |
45 |
46 | start(new CacheKey("outside", 1))) {
49 | // not cached
50 |
51 | // big div
52 | echo 'Outside
';
53 |
54 | // some smaller divs
55 | for ($i = 0; $i < 3; $i++) {
56 | if (!$cache->start(new CacheKey("medium", $i))) {
57 | echo '
';
58 |
59 | // some even smaller divs
60 | for ($j = 0; $j < 3; $j++) {
61 | if (!$cache->start(new CacheKey("small", $j))) {
62 | echo '
';
63 | echo "Got here $i - $j";
64 | echo '
';
65 | $cache->end();
66 | }
67 | }
68 | echo '
';
69 | $cache->end();
70 | }
71 | }
72 |
73 | // this should be cached
74 | echo '
This below will be cached, since we saved it above
';
75 | $cache->start(new CacheKey("medium", 0));
76 |
77 | echo '
';
78 | $cache->end();
79 | }
80 |
81 | // this is required for the probes
82 | $cache->footerDebug();
83 |
84 | $cache->report();
85 |
86 | ?>
87 |
88 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | ./test/
15 |
16 |
17 |
18 |
19 | ./src
20 |
21 | ./src/Cache.php
22 | ./src/Backend/external
23 | test/
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Backend/CacheAPC.php:
--------------------------------------------------------------------------------
1 | enable();
39 | }
40 |
41 | public function enable() {
42 | if (!extension_loaded('apc')) {
43 | $this->enabled = false;
44 | return false;
45 | }
46 | return parent::enable();
47 | }
48 |
49 | private function checkValidArgs($base, $id, $sub) {
50 | if (is_array($base) || is_array($id) || !is_string($sub)) {
51 | throw new CacheInvalidDataException('Invalid get parameter');
52 | }
53 | }
54 |
55 | public function get($base, $id, $sub = LH_DEFAULT_CACHE_ID) {
56 | // @codeCoverageIgnoreStart
57 | if (!$this->enabled) {
58 | throw new NotCachedException();
59 | }
60 | // @codeCoverageIgnoreEnd
61 |
62 | if (!is_string($sub)) {
63 | $sub = md5(serialize($sub));
64 | }
65 | $this->checkValidArgs($base, $id, $sub);
66 |
67 | $key = (new CacheKey($base, $id, $sub))->getHash();
68 | $success = false;
69 | $data = apc_fetch($key, $success);
70 | if (!$success) {
71 | $this->log(CacheLogEnum::MISSED, $base, $id, $sub);
72 | throw new NotCachedException();
73 | }
74 | return $data;
75 | }
76 |
77 | public function store($data, $base, $id, $sub = LH_DEFAULT_CACHE_ID, $lifetime = 0) {
78 | // @codeCoverageIgnoreStart
79 | if (!$this->enabled) {
80 | return false;
81 | }
82 | // @codeCoverageIgnoreEnd
83 |
84 | if (!is_string($sub)) {
85 | $sub = md5(serialize($sub));
86 | }
87 | $this->checkValidArgs($base, $id, $sub);
88 |
89 | $key = (new CacheKey($base, $id, $sub))->getHash();
90 | apc_store($key, $data, $lifetime);
91 | return true;
92 | }
93 |
94 | public function delete($base, $id, $sub = LH_DEFAULT_CACHE_ID) {
95 | if (!is_string($sub)) {
96 | $sub = md5(serialize($sub));
97 | }
98 |
99 | $this->checkValidArgs($base, $id, $sub);
100 |
101 | $key = (new CacheKey($base, $id, $sub))->getHash();
102 | apc_delete($key);
103 | return true;
104 | }
105 |
106 | public function clean($base, $id) {
107 | // TODO
108 | return true;
109 | }
110 |
111 | public function clear() {
112 | apc_clear_cache('user');
113 | return true;
114 | }
115 |
116 | public function prefetch($data) {
117 | // nothing.
118 | }
119 |
120 | public function report() {
121 | if ($this->should_log == false) {
122 | return;
123 | }
124 | // TODO
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Backend/CacheFilesystem.php:
--------------------------------------------------------------------------------
1 | lifetime = 3600 * 24 * 30;
51 | $this->enable();
52 | }
53 |
54 | /**
55 | * Sets path to store data
56 | *
57 | * @param string $path
58 | * @throws RuntimeException
59 | * @return CacheFS
60 | * @codeCoverageIgnore
61 | */
62 | public function setPath($path) {
63 | if (!is_writable($this->path) || !file_exists($this->path) || !is_dir($this->path)) {
64 | throw new RuntimeException('Invalid dir or missing permissions');
65 | }
66 |
67 | $this->path = $path . '/';
68 |
69 | // reload
70 | if ($this->isEnabled()) {
71 | $this->enable();
72 | }
73 |
74 | return $this;
75 | }
76 |
77 | public function enable() {
78 | $this->cache = new \Cache_Lite_Timed(
79 | array(
80 | 'cacheDir' => $this->path,
81 | 'lifeTime' => $this->getDefaultLifetime(), // in seconds
82 | 'automaticCleaningFactor' => 200,
83 | 'hashedDirectoryLevel' => 1,
84 | 'writeControl' => false,
85 | )
86 | );
87 | return parent::enable();
88 | }
89 |
90 | /**
91 | * (non-PHPdoc)
92 | * @see \Cachearium\CacheAbstract::hashKey($k)
93 | */
94 | protected function hashKey(CacheKey $k) {
95 | $group = $this->namespace . $k->base . $k->id;
96 | return $group;
97 | }
98 |
99 | /**
100 | * (non-PHPdoc)
101 | * @see \Cachearium\CacheAbstract::increment($value, $k, $default)
102 | */
103 | public function increment($value, CacheKey $k, $default = 0) {
104 | throw new \Cachearium\Exceptions\CacheUnsupportedOperation();
105 | }
106 |
107 | /**
108 | * (non-PHPdoc)
109 | * @see \Cachearium\CacheAbstract::get($k)
110 | */
111 | public function get(CacheKey $k) {
112 | // @codeCoverageIgnoreStart
113 | if (!$this->enabled) {
114 | throw new \Cachearium\Exceptions\NotCachedException();
115 | }
116 | // @codeCoverageIgnoreEnd
117 |
118 | $group = $this->hashKey($k);
119 | if (!is_string($k->sub)) {
120 | $cacheid = md5(serialize($k->sub));
121 | }
122 | else {
123 | $cacheid = $k->sub;
124 | }
125 | $retval = $this->cache->get($cacheid, $group);
126 |
127 | $this->log(
128 | ($retval !== false ? CacheLogEnum::ACCESSED : CacheLogEnum::MISSED),
129 | $k
130 | );
131 |
132 | if ($retval) {
133 | return unserialize($retval);
134 | }
135 | throw new \Cachearium\Exceptions\NotCachedException();
136 | }
137 |
138 | /**
139 | * (non-PHPdoc)
140 | * @see \Cachearium\CacheAbstract::store($data, $k, $lifetime)
141 | */
142 | public function store($data, CacheKey $k, $lifetime = -1) {
143 | // @codeCoverageIgnoreStart
144 | if (!$this->enabled) {
145 | return false;
146 | }
147 | // @codeCoverageIgnoreEnd
148 |
149 | $group = $this->hashKey($k);
150 | if (!is_string($k->sub)) {
151 | $cacheid = md5(serialize($k->sub));
152 | }
153 | else {
154 | $cacheid = $k->sub;
155 | }
156 | return $this->cache->save(
157 | serialize($data), $cacheid, $group, ($lifetime < 0 ? $this->getDefaultLifetime() : $lifetime)
158 | );
159 | }
160 |
161 | /**
162 | * (non-PHPdoc)
163 | * @see \Cachearium\CacheAbstract::delete($k)
164 | */
165 | public function delete(CacheKey $k) {
166 | $group = $this->hashKey($k);
167 | if (!is_string($k->sub)) {
168 | $cacheid = md5(serialize($k->sub));
169 | }
170 | else {
171 | $cacheid = $k->sub;
172 | }
173 | $this->log(CacheLogEnum::DELETED, $k);
174 | return $this->cache->remove($cacheid, $group);
175 | }
176 |
177 | /**
178 | * (non-PHPdoc)
179 | * @see \Cachearium\CacheAbstract::setDefaultLifetime($lifetime)
180 | */
181 | public function setDefaultLifetime($lifetime = 0) {
182 | parent::setDefaultLifetime($lifetime);
183 | $this->cache->setLifeTime($this->getDefaultLifetime());
184 | }
185 |
186 | /**
187 | * (non-PHPdoc)
188 | * @see \Cachearium\CacheAbstract::cleanP($base, $id)
189 | */
190 | public function cleanP($base, $id) {
191 | // @codeCoverageIgnoreStart
192 | if (!$this->enabled) {
193 | return false;
194 | }
195 | // @codeCoverageIgnoreEnd
196 |
197 | $group = $this->hashKey(new CacheKey($base, $id));
198 | $retval = $this->cache->clean($group, 'ingroup');
199 | $this->log(CacheLogEnum::CLEANED, new CacheKey($base, $id));
200 | return $retval;
201 | }
202 |
203 | /**
204 | * (non-PHPdoc)
205 | * @see \Cachearium\CacheAbstract::clear()
206 | */
207 | public function clear() {
208 | if ($this->cache) {
209 | $this->cache->clean();
210 | }
211 | return true;
212 | }
213 |
214 | /**
215 | * (non-PHPdoc)
216 | * @see CacheAbstract::prefetch()
217 | * @codeCoverageIgnore
218 | */
219 | public function prefetch($data) {
220 | // nothing.
221 | }
222 |
223 | /**
224 | * (non-PHPdoc)
225 | * @see \Cachearium\CacheAbstract::report()
226 | * @codeCoverageIgnore
227 | */
228 | public function report() {
229 | if ($this->should_log == false) {
230 | return;
231 | }
232 | echo 'Cache FS system
';
233 | echo '
System is: ' . ($this->enabled ? 'enabled' : 'disabled') . '
';
234 | echo '
';
235 | foreach ($this->cache_log as $entry) {
236 | echo '- ' . CacheLogEnum::getName($entry['status']) . $entry['message'] . '
';
237 | }
238 | echo '
';
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/Backend/CacheMemcached.php:
--------------------------------------------------------------------------------
1 | addServers($servers);
41 | }
42 | }
43 |
44 | return $instances;
45 | }
46 |
47 | /**
48 | * Is memcached available in this system?
49 | *
50 | * @return boolean
51 | */
52 | static public function hasMemcachedExt() {
53 | return extension_loaded('memcached');
54 | }
55 |
56 | // @codeCoverageIgnoreStart
57 | // Prevent users to clone the instance
58 | public function __clone() {
59 | trigger_error('Cloning is not allowed.', LH_TRIGGER_UNEXPECTED);
60 | }
61 | // @codeCoverageIgnoreEnd
62 |
63 | /**
64 | * @codeCoverageIgnore
65 | */
66 | public function errorCallback() {
67 | // memcache error, probably offline. Logging to DB is bad (will overflow
68 | // the DB). We should really restart memcached
69 | // TODO: via Batch?
70 | $this->disable();
71 | }
72 |
73 | public function getFetches() {
74 | return []; // TODO
75 | foreach ($data as $item) {
76 | $x = unserialize($item['keys']);
77 | if ($x === false) {
78 | continue;
79 | }
80 |
81 | parent::store($x, $item);
82 | }
83 |
84 | return $this->fetches;
85 | }
86 |
87 | /**
88 | * Constructor.
89 | * @codeCoverageIgnore
90 | */
91 | private function __construct() {
92 | if (!self::hasMemcachedExt()) {
93 | $this->disable();
94 | return;
95 | }
96 | $this->memcached = new \Memcached;
97 | if (!$this->memcached) {
98 | $this->disable();
99 | return;
100 | }
101 |
102 | if (\Memcached::HAVE_IGBINARY) {
103 | $this->memcached->setOption(\Memcached::OPT_SERIALIZER, \Memcached::SERIALIZER_IGBINARY);
104 | }
105 | $this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
106 | $this->lifetime = 3600;
107 | }
108 |
109 | /**
110 | *
111 | * @param array $servers Each entry in servers is supposed to be an array
112 | * containing hostname, port, and, optionally, weight of the server.
113 | * $servers = array(
114 | * array('mem1.domain.com', 11211, 33),
115 | * array('mem2.domain.com', 11211, 67)
116 | * );
117 | * @return boolean
118 | * @codeCoverageIgnore
119 | */
120 | public function addServers($servers) {
121 | if (!self::hasMemcachedExt()) {
122 | return false;
123 | }
124 | return $this->memcached->addServers($servers);
125 | }
126 |
127 | /**
128 | * Converts cachekey to a string for the data group.
129 | *
130 | * @param CacheKey $k
131 | * @return string
132 | */
133 | private function getGroupString(CacheKey $k) {
134 | return md5(strtr($this->namespace . $k->getBase() . $k->getId(), ' ', '_'));
135 | }
136 |
137 | /**
138 | * (non-PHPdoc)
139 | * @see \Cachearium\Backend\CacheRAM::hashKey()
140 | */
141 | protected function hashKey(CacheKey $k) {
142 | $group = $this->getGroupString($k);
143 | $ns_key = $this->memcached->get($group);
144 |
145 | // if not set, initialize it
146 | if ($ns_key == false) {
147 | $ns_key = 1;
148 | $this->memcached->set($group, $ns_key);
149 | }
150 | $group = $group . $ns_key;
151 |
152 | if (!is_string($k->sub)) {
153 | $sub = md5(serialize($k->sub));
154 | }
155 | else {
156 | $sub = $k->sub;
157 | }
158 | $group .= $sub;
159 |
160 | return $group;
161 | }
162 |
163 | /**
164 | * (non-PHPdoc)
165 | * @see \Cachearium\Backend\CacheRAM::get()
166 | */
167 | public function get(CacheKey $k) {
168 | // @codeCoverageIgnoreStart
169 | if (!$this->enabled) {
170 | throw new NotCachedException();
171 | }
172 | // @codeCoverageIgnoreEnd
173 |
174 | // see if it is in RAM
175 | $should_log = $this->should_log;
176 | try {
177 | $this->should_log = false;
178 | $data = parent::get($k);
179 | $this->should_log = $should_log;
180 | $this->log(CacheLogEnum::PREFETCHED, $k);
181 | return $data;
182 | }
183 | catch (NotCachedException $e) {
184 | $this->should_log = $should_log;
185 | }
186 |
187 | $group = $this->hashKey($k);
188 |
189 | $this->fetches++;
190 | $retval = $this->memcached->get($group);
191 |
192 | $this->log(
193 | ($retval !== false ? CacheLogEnum::ACCESSED : CacheLogEnum::MISSED),
194 | $k
195 | );
196 | if ($retval == false) {
197 | throw new NotCachedException();
198 | }
199 |
200 | $x = unserialize($retval);
201 | if ($x === false) {
202 | throw new NotCachedException();
203 | }
204 |
205 | parent::store($x, $k);
206 |
207 | return $x;
208 | }
209 |
210 | /**
211 | * (non-PHPdoc)
212 | * @see \Cachearium\Backend\CacheRAM::increment()
213 | */
214 | public function increment($value, CacheKey $k, $default = 0) {
215 | // @codeCoverageIgnoreStart
216 | if (!$this->enabled) {
217 | return $default;
218 | }
219 | // @codeCoverageIgnoreEnd
220 |
221 | $group = $this->hashKey($k);
222 |
223 | $this->log(CacheLogEnum::SAVED, $k);
224 |
225 | $x = $this->memcached->increment(
226 | $group, $value, $default, $this->lifetime
227 | );
228 | parent::store($x, $k, $this->lifetime);
229 |
230 | return $x;
231 | }
232 |
233 | /**
234 | * (non-PHPdoc)
235 | * @see \Cachearium\Backend\CacheRAM::store()
236 | */
237 | public function store($data, CacheKey $k, $lifetime = 0) {
238 | // @codeCoverageIgnoreStart
239 | if (!$this->enabled) {
240 | return false;
241 | }
242 | // @codeCoverageIgnoreEnd
243 |
244 | $group = $this->hashKey($k);
245 |
246 | $this->log(CacheLogEnum::SAVED, $k);
247 |
248 | $x = $this->memcached->set(
249 | $group, serialize($data), $lifetime ? $lifetime : $this->lifetime
250 | );
251 | parent::store($data, $k, $lifetime);
252 |
253 | return $x;
254 | }
255 |
256 | /**
257 | * (non-PHPdoc)
258 | * @see \Cachearium\Backend\CacheRAM::delete()
259 | */
260 | public function delete(CacheKey $k) {
261 | // @codeCoverageIgnoreStart
262 | if (!$this->enabled) {
263 | throw new NotCachedException();
264 | }
265 | // @codeCoverageIgnoreEnd
266 |
267 | $group = $this->hashKey($k);
268 |
269 | $this->log(CacheLogEnum::DELETED, $k);
270 |
271 | parent::delete($k);
272 | return $this->memcached->delete($group);
273 | }
274 |
275 | /**
276 | * (non-PHPdoc)
277 | * @see \Cachearium\Backend\CacheRAM::cleanP()
278 | */
279 | public function cleanP($base, $id) {
280 | // @codeCoverageIgnoreStart
281 | if (!$this->enabled) {
282 | throw new NotCachedException();
283 | }
284 | // @codeCoverageIgnoreEnd
285 |
286 | $group = $this->getGroupString(new CacheKey($base, $id));
287 |
288 | parent::cleanP($base, $id);
289 | $this->memcached->increment($group);
290 | return true;
291 | }
292 |
293 | /**
294 | * (non-PHPdoc)
295 | * @see \Cachearium\Backend\CacheRAM::clear()
296 | */
297 | public function clear() {
298 | if ($this->memcached) {
299 | $this->memcached->flush();
300 | }
301 | parent::clear();
302 | return true;
303 | }
304 |
305 | public function prefetchKeys($keys) {
306 | $retval = $this->memcached->get($keys);
307 | foreach ($retval as $i) {
308 | }
309 | }
310 |
311 | /**
312 | * (non-PHPdoc)
313 | * @see \Cachearium\Backend\CacheRAM::prefetch()
314 | */
315 | public function prefetch($data) {
316 | $keys = array();
317 |
318 | foreach ($data as &$item) {
319 | $keys[] = $this->hashKey($item);
320 | }
321 |
322 | $this->memcached->getDelayed($keys);
323 |
324 | // TODO: fetchall vs get?
325 | }
326 |
327 | /**
328 | * Clear prefetched data. This is rarely useful.
329 | */
330 | public function prefetchClear() {
331 | parent::clear();
332 | }
333 |
334 | /**
335 | * (non-PHPdoc)
336 | * @see \Cachearium\Backend\CacheRAM::report()
337 | * @codeCoverageIgnore
338 | */
339 | public function report() {
340 | if ($this->should_log == false) {
341 | return;
342 | }
343 | echo '';
351 | echo 'Cache MemCache system
';
352 | echo '
System is: ' . ($this->enabled ? 'enabled' : 'disabled') . '
';
353 | echo '
Total fetches: ' . ($this->fetches) . '
';
354 |
355 | $stats = array_fill_keys(array_keys(CacheLogEnum::getNames()), 0);
356 | echo '
';
357 | foreach ($this->cache_log as $entry) {
358 | echo '- ' . CacheLogEnum::getName($entry['status']) . $entry['message'] . '
';
359 | $stats[$entry['status']]++;
360 | }
361 | echo '
';
362 |
363 | echo '
';
364 | foreach ($stats as $key => $val) {
365 | echo '- ' . CacheLogEnum::getName($key) . '=' . $val . '
';
366 | }
367 | echo '
';
368 | echo '
';
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/src/Backend/CacheNull.php:
--------------------------------------------------------------------------------
1 | disable();
43 | }
44 |
45 | public function enable() {
46 | }
47 |
48 | public function get(CacheKey $k) {
49 | throw new NotCachedException();
50 | }
51 |
52 | public function increment($value, CacheKey $k, $default = 0) {
53 | return $default;
54 | }
55 |
56 | public function store($data, CacheKey $k, $lifetime = 0) {
57 | return true;
58 | }
59 |
60 | public function delete(CacheKey $k) {
61 | return true;
62 | }
63 |
64 | public function cleanP($base, $id) {
65 | return true;
66 | }
67 |
68 | public function clear() {
69 | return true;
70 | }
71 |
72 | public function start(CacheKey $k, $lifetime = null, $print = true, $fail = false) {
73 | return false;
74 | }
75 |
76 | public function end($print = true) {
77 | return '';
78 | }
79 |
80 | public function prefetch($data) {
81 | }
82 |
83 | /**
84 | * @codeCoverageIgnore
85 | */
86 | protected function hashKey(CacheKey $k) {
87 | return $k->getBase() . $k->getId() . serialize($k->getSub());
88 | }
89 |
90 | /**
91 | * @codeCoverageIgnore
92 | */
93 | public function report() {
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/Backend/CacheRAM.php:
--------------------------------------------------------------------------------
1 | namespace . $k->base . $k->id . serialize($k->sub);
58 | }
59 |
60 | /**
61 | * @param CacheKey $k
62 | * @throws CacheInvalidDataException
63 | * @codeCoverageIgnore
64 | */
65 | private function checkValidArgs(CacheKey $k) {
66 | if (is_array($k->base) || is_array($k->id)) {
67 | throw new CacheInvalidDataException('Invalid get parameter');
68 | }
69 | }
70 |
71 | /**
72 | * (non-PHPdoc)
73 | * @see \Cachearium\CacheAbstract::getData($k)
74 | */
75 | public function getData(CacheKey $k) {
76 | $cd = CacheData::unserialize($this->get($k));
77 |
78 | if ($cd->checkUpdateToDate($this)) {
79 | return $cd;
80 | }
81 | $this->delete($k);
82 | throw new NotCachedException();
83 | }
84 |
85 | public function increment($value, CacheKey $k, $default = 0) {
86 | // @codeCoverageIgnoreStart
87 | if (!$this->enabled) {
88 | return $default;
89 | }
90 | // @codeCoverageIgnoreEnd
91 |
92 | if (!is_string($k->sub)) {
93 | $sub = md5(serialize($k->sub));
94 | }
95 | else {
96 | $sub = $k->sub;
97 | }
98 | $this->checkValidArgs($k);
99 |
100 | $idx = $this->namespace . $k->base . $k->id;
101 | if (isset($this->storage[$idx]) && isset($this->storage[$idx][$sub])) {
102 | $this->storage[$idx][$sub] += $value;
103 | }
104 | else {
105 | $this->storage[$idx][$sub] = $default;
106 | }
107 | return $this->storage[$idx][$sub];
108 | }
109 |
110 | public function get(CacheKey $k) {
111 | // @codeCoverageIgnoreStart
112 | if (!$this->enabled) {
113 | throw new NotCachedException();
114 | }
115 | // @codeCoverageIgnoreEnd
116 |
117 | if (!is_string($k->sub)) {
118 | $sub = md5(serialize($k->sub));
119 | }
120 | else {
121 | $sub = $k->sub;
122 | }
123 | $this->checkValidArgs($k);
124 |
125 | $idx = $this->namespace . $k->base . $k->id;
126 | if (isset($this->storage[$idx])
127 | and array_key_exists($sub, $this->storage[$idx])
128 | ) {
129 | $this->log(CacheLogEnum::ACCESSED, $k);
130 | return $this->storage[$idx][$sub];
131 | }
132 | $this->log(CacheLogEnum::MISSED, $k);
133 | throw new NotCachedException();
134 | }
135 |
136 | public function store($data, CacheKey $k, $lifetime = 0) {
137 | // @codeCoverageIgnoreStart
138 | if (!$this->enabled) {
139 | return false;
140 | }
141 | // @codeCoverageIgnoreEnd
142 |
143 | if (!is_string($k->sub)) {
144 | $sub = md5(serialize($k->sub));
145 | }
146 | else {
147 | $sub = $k->sub;
148 | }
149 | $this->checkValidArgs($k);
150 |
151 | $this->storage[$this->namespace . $k->base . $k->id][$sub] = $data;
152 | return true;
153 | }
154 |
155 | public function delete(CacheKey $k) {
156 | if (!is_string($k->sub)) {
157 | $sub = md5(serialize($k->sub));
158 | }
159 | else {
160 | $sub = $k->sub;
161 | }
162 |
163 | $this->checkValidArgs($k);
164 |
165 | unset($this->storage[$this->namespace . $k->base . $k->id][$sub]);
166 | return true;
167 | }
168 |
169 | public function cleanP($base, $id) {
170 | unset($this->storage[$this->namespace . $base . $id]);
171 | return true;
172 | }
173 |
174 | public function clear() {
175 | $this->storage = array();
176 | return true;
177 | }
178 |
179 | public function getMemoryLimit($memoryLimit) {
180 | return $this->memoryLimit;
181 | }
182 |
183 | /**
184 | *
185 | * @param integer $memoryLimit
186 | * @return \Cachearium\Backend\CacheRAM
187 | */
188 | public function setMemoryLimit($memoryLimit) {
189 | $this->memoryLimit = $memoryLimit;
190 | return $this;
191 | }
192 |
193 | /**
194 | * Clears cache if PHP memory usage is above a chosen limit
195 | * This checks the ENTIRE PHP memory usage, which may be a lot more
196 | * than what is used by this backend.
197 | *
198 | * @return boolean
199 | */
200 | public function limitRAM() {
201 | if (memory_get_usage() > $this->memoryLimit) {
202 | $this->clear();
203 | }
204 | return true;
205 | }
206 |
207 | /**
208 | * (non-PHPdoc)
209 | * @see CacheAbstract::prefetch()
210 | * @codeCoverageIgnore
211 | */
212 | public function prefetch($data) {
213 | // nothing.
214 | }
215 |
216 | /**
217 | * @codeCoverageIgnore
218 | */
219 | public function report() {
220 | if ($this->should_log == false) {
221 | return;
222 | }
223 | echo 'Cache RAM system
';
224 | echo '
System is: ' . ($this->enabled ? 'enabled' : 'disabled') . '
';
225 | $stats = array_fill_keys(array_keys(CacheLogEnum::getNames()), 0);
226 | echo '
';
227 | foreach ($this->cache_log as $entry) {
228 | echo '- ' . CacheLogEnum::getName($entry['status']) . $entry['message'] . '
';
229 | $stats[$entry['status']]++;
230 | }
231 | echo '
';
232 |
233 | echo '
';
234 | foreach ($stats as $key => $val) {
235 | echo '- ' . CacheLogEnum::getName($key) . '=' . $val . '
';
236 | }
237 | echo '
';
238 |
239 | echo '
';
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/src/Backend/external/Lite.php:
--------------------------------------------------------------------------------
1 |
15 | *
16 | * Nota : A chinese documentation (thanks to RainX ) is
17 | * available at :
18 | * http://rainx.phpmore.com/manual/cache_lite.html
19 | *
20 | * @package Cache_Lite
21 | * @category Caching
22 | * @version $Id: Lite.php,v 1.45 2006/06/03 08:10:33 fab Exp $
23 | * @author Fabien MARTY
24 | */
25 |
26 | define('CACHE_LITE_ERROR_RETURN', 1);
27 | define('CACHE_LITE_ERROR_DIE', 8);
28 |
29 | class Cache_Lite
30 | {
31 |
32 | // --- Private properties ---
33 |
34 | /**
35 | * Directory where to put the cache files
36 | * (make sure to add a trailing slash)
37 | *
38 | * @var string $_cacheDir
39 | */
40 | var $_cacheDir = '/tmp/';
41 |
42 | /**
43 | * Enable / disable caching
44 | *
45 | * (can be very usefull for the debug of cached scripts)
46 | *
47 | * @var boolean $_caching
48 | */
49 | var $_caching = true;
50 |
51 | /**
52 | * Cache lifetime (in seconds)
53 | *
54 | * If null, the cache is valid forever.
55 | *
56 | * @var int $_lifeTime
57 | */
58 | var $_lifeTime = 3600;
59 |
60 | /**
61 | * Enable / disable fileLocking
62 | *
63 | * (can avoid cache corruption under bad circumstances)
64 | *
65 | * @var boolean $_fileLocking
66 | */
67 | var $_fileLocking = true;
68 |
69 | /**
70 | * Timestamp of the last valid cache
71 | *
72 | * @var int $_refreshTime
73 | */
74 | var $_refreshTime;
75 |
76 | /**
77 | * File name (with path)
78 | *
79 | * @var string $_file
80 | */
81 | var $_file;
82 |
83 | /**
84 | * File name (without path)
85 | *
86 | * @var string $_fileName
87 | */
88 | var $_fileName;
89 |
90 | /**
91 | * Enable / disable write control (the cache is read just after writing to detect corrupt entries)
92 | *
93 | * Enable write control will lightly slow the cache writing but not the cache reading
94 | * Write control can detect some corrupt cache files but maybe it's not a perfect control
95 | *
96 | * @var boolean $_writeControl
97 | */
98 | var $_writeControl = true;
99 |
100 | /**
101 | * Enable / disable read control
102 | *
103 | * If enabled, a control key is embeded in cache file and this key is compared with the one
104 | * calculated after the reading.
105 | *
106 | * @var boolean $_writeControl
107 | */
108 | var $_readControl = false;
109 |
110 | /**
111 | * Type of read control (only if read control is enabled)
112 | *
113 | * Available values are :
114 | * 'md5' for a md5 hash control (best but slowest)
115 | * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
116 | * 'strlen' for a length only test (fastest)
117 | *
118 | * @var boolean $_readControlType
119 | */
120 | var $_readControlType = 'crc32';
121 |
122 | /**
123 | * Pear error mode (when raiseError is called)
124 | *
125 | * (see PEAR doc)
126 | *
127 | * @see setToDebug()
128 | * @var int $_pearErrorMode
129 | */
130 | var $_pearErrorMode = CACHE_LITE_ERROR_RETURN;
131 |
132 | /**
133 | * Current cache id
134 | *
135 | * @var string $_id
136 | */
137 | var $_id;
138 |
139 | /**
140 | * Current cache group
141 | *
142 | * @var string $_group
143 | */
144 | var $_group;
145 |
146 | /**
147 | * Enable / Disable "Memory Caching"
148 | *
149 | * NB : There is no lifetime for memory caching !
150 | *
151 | * @var boolean $_memoryCaching
152 | */
153 | var $_memoryCaching = false;
154 |
155 | /**
156 | * Enable / Disable "Only Memory Caching"
157 | * (be carefull, memory caching is "beta quality")
158 | *
159 | * @var boolean $_onlyMemoryCaching
160 | */
161 | var $_onlyMemoryCaching = false;
162 |
163 | /**
164 | * Memory caching array
165 | *
166 | * @var array $_memoryCachingArray
167 | */
168 | var $_memoryCachingArray = array();
169 |
170 | /**
171 | * Memory caching counter
172 | *
173 | * @var int $memoryCachingCounter
174 | */
175 | var $_memoryCachingCounter = 0;
176 |
177 | /**
178 | * Memory caching limit
179 | *
180 | * @var int $memoryCachingLimit
181 | */
182 | var $_memoryCachingLimit = 1000;
183 |
184 | /**
185 | * File Name protection
186 | *
187 | * if set to true, you can use any cache id or group name
188 | * if set to false, it can be faster but cache ids and group names
189 | * will be used directly in cache file names so be carefull with
190 | * special characters...
191 | *
192 | * @var boolean $fileNameProtection
193 | */
194 | var $_fileNameProtection = true;
195 |
196 | /**
197 | * Enable / disable automatic serialization
198 | *
199 | * it can be used to save directly datas which aren't strings
200 | * (but it's slower)
201 | *
202 | * @var boolean $_serialize
203 | */
204 | var $_automaticSerialization = false;
205 |
206 | /**
207 | * Disable / Tune the automatic cleaning process
208 | *
209 | * The automatic cleaning process destroy too old (for the given life time)
210 | * cache files when a new cache file is written.
211 | * 0 => no automatic cache cleaning
212 | * 1 => systematic cache cleaning
213 | * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
214 | *
215 | * @var int $_automaticCleaning
216 | */
217 | var $_automaticCleaningFactor = 0;
218 |
219 | /**
220 | * Nested directory level
221 | *
222 | * Set the hashed directory structure level. 0 means "no hashed directory
223 | * structure", 1 means "one level of directory", 2 means "two levels"...
224 | * This option can speed up Cache_Lite only when you have many thousands of
225 | * cache file. Only specific benchs can help you to choose the perfect value
226 | * for you. Maybe, 1 or 2 is a good start.
227 | *
228 | * @var int $_hashedDirectoryLevel
229 | */
230 | var $_hashedDirectoryLevel = 0;
231 |
232 | /**
233 | * Umask for hashed directory structure
234 | *
235 | * @var int $_hashedDirectoryUmask
236 | */
237 | var $_hashedDirectoryUmask = 0770;
238 |
239 | /**
240 | * API break for error handling in CACHE_LITE_ERROR_RETURN mode
241 | *
242 | * In CACHE_LITE_ERROR_RETURN mode, error handling was not good because
243 | * for example save() method always returned a boolean (a PEAR_Error object
244 | * would be better in CACHE_LITE_ERROR_RETURN mode). To correct this without
245 | * breaking the API, this option (false by default) can change this handling.
246 | *
247 | * @var boolean
248 | */
249 | var $_errorHandlingAPIBreak = false;
250 |
251 | // --- Public methods ---
252 |
253 | /**
254 | * Constructor
255 | *
256 | * $options is an assoc. Available options are :
257 | * $options = array(
258 | * 'cacheDir' => directory where to put the cache files (string),
259 | * 'caching' => enable / disable caching (boolean),
260 | * 'lifeTime' => cache lifetime in seconds (int),
261 | * 'fileLocking' => enable / disable fileLocking (boolean),
262 | * 'writeControl' => enable / disable write control (boolean),
263 | * 'readControl' => enable / disable read control (boolean),
264 | * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string),
265 | * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int),
266 | * 'memoryCaching' => enable / disable memory caching (boolean),
267 | * 'onlyMemoryCaching' => enable / disable only memory caching (boolean),
268 | * 'memoryCachingLimit' => max nbr of records to store into memory caching (int),
269 | * 'fileNameProtection' => enable / disable automatic file name protection (boolean),
270 | * 'automaticSerialization' => enable / disable automatic serialization (boolean),
271 | * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int),
272 | * 'hashedDirectoryLevel' => level of the hashed directory system (int),
273 | * 'hashedDirectoryUmask' => umask for hashed directory structure (int),
274 | * 'errorHandlingAPIBreak' => API break for better error handling ? (boolean)
275 | * );
276 | *
277 | * @param array $options options
278 | * @access public
279 | */
280 | function __construct($options = array(NULL))
281 | {
282 | foreach($options as $key => $value) {
283 | $this->setOption($key, $value);
284 | }
285 | }
286 |
287 | /**
288 | * Generic way to set a Cache_Lite option
289 | *
290 | * see Cache_Lite constructor for available options
291 | *
292 | * @var string $name name of the option
293 | * @var mixed $value value of the option
294 | * @access public
295 | */
296 | function setOption($name, $value)
297 | {
298 | $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode');
299 | if (in_array($name, $availableOptions)) {
300 | $property = '_'.$name;
301 | $this->$property = $value;
302 | }
303 | }
304 |
305 | /**
306 | * Test if a cache is available and (if yes) return it
307 | *
308 | * @param string $id cache id
309 | * @param string $group name of the cache group
310 | * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
311 | * @return string data of the cache (else : false)
312 | * @access public
313 | */
314 | function get($id, $group = 'default', $doNotTestCacheValidity = false)
315 | {
316 | $this->_id = $id;
317 | $this->_group = $group;
318 | $data = false;
319 | if ($this->_caching) {
320 | $this->_setRefreshTime();
321 | $this->_setFileName($id, $group);
322 | clearstatcache();
323 | if ($this->_memoryCaching) {
324 | if (isset($this->_memoryCachingArray[$this->_file])) {
325 | if ($this->_automaticSerialization) {
326 | return unserialize($this->_memoryCachingArray[$this->_file]);
327 | }
328 | return $this->_memoryCachingArray[$this->_file];
329 | }
330 | if ($this->_onlyMemoryCaching) {
331 | return false;
332 | }
333 | }
334 | if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) {
335 | if (file_exists($this->_file)) {
336 | $data = $this->_read();
337 | }
338 | } else {
339 | if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) {
340 | $data = $this->_read();
341 | }
342 | }
343 | if (($data) and ($this->_memoryCaching)) {
344 | $this->_memoryCacheAdd($data);
345 | }
346 | if (($this->_automaticSerialization) and (is_string($data))) {
347 | $data = unserialize($data);
348 | }
349 | return $data;
350 | }
351 | return false;
352 | }
353 |
354 | /**
355 | * Save some data in a cache file
356 | *
357 | * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on)
358 | * @param string $id cache id
359 | * @param string $group name of the cache group
360 | * @return boolean true if no problem (else : false or a PEAR_Error object)
361 | * @access public
362 | */
363 | function save($data, $id = NULL, $group = 'default')
364 | {
365 | if ($this->_caching) {
366 | if ($this->_automaticSerialization) {
367 | $data = serialize($data);
368 | }
369 | if (isset($id)) {
370 | $this->_setFileName($id, $group);
371 | }
372 | if ($this->_memoryCaching) {
373 | $this->_memoryCacheAdd($data);
374 | if ($this->_onlyMemoryCaching) {
375 | return true;
376 | }
377 | }
378 | if ($this->_automaticCleaningFactor>0) {
379 | $rand = rand(1, $this->_automaticCleaningFactor);
380 | if ($rand==1) {
381 | $this->clean(false, 'old');
382 | }
383 | }
384 | if ($this->_writeControl) {
385 | $res = $this->_writeAndControl($data);
386 | if (is_bool($res)) {
387 | if ($res) {
388 | return true;
389 | }
390 | // if $res if false, we need to invalidate the cache
391 | @touch($this->_file, time() - 2*abs($this->_lifeTime));
392 | return false;
393 | }
394 | } else {
395 | $res = $this->_write($data);
396 | }
397 | if (is_object($res)) {
398 | // $res is a PEAR_Error object
399 | if (!($this->_errorHandlingAPIBreak)) {
400 | return false; // we return false (old API)
401 | }
402 | }
403 | return $res;
404 | }
405 | return false;
406 | }
407 |
408 | /**
409 | * Remove a cache file
410 | *
411 | * @param string $id cache id
412 | * @param string $group name of the cache group
413 | * @return boolean true if no problem
414 | * @access public
415 | */
416 | function remove($id, $group = 'default')
417 | {
418 | $this->_setFileName($id, $group);
419 | if ($this->_memoryCaching) {
420 | if (isset($this->_memoryCachingArray[$this->_file])) {
421 | unset($this->_memoryCachingArray[$this->_file]);
422 | $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
423 | }
424 | if ($this->_onlyMemoryCaching) {
425 | return true;
426 | }
427 | }
428 | return $this->_unlink($this->_file);
429 | }
430 |
431 | /**
432 | * Clean the cache
433 | *
434 | * if no group is specified all cache files will be destroyed
435 | * else only cache files of the specified group will be destroyed
436 | *
437 | * @param string $group name of the cache group
438 | * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup',
439 | * 'callback_myFunction'
440 | * @return boolean true if no problem
441 | * @access public
442 | */
443 | function clean($group = false, $mode = 'ingroup')
444 | {
445 | return $this->_cleanDir($this->_cacheDir, $group, $mode);
446 | }
447 |
448 | /**
449 | * Set to debug mode
450 | *
451 | * When an error is found, the script will stop and the message will be displayed
452 | * (in debug mode only).
453 | *
454 | * @access public
455 | */
456 | function setToDebug()
457 | {
458 | $this->setOption('pearErrorMode', CACHE_LITE_ERROR_DIE);
459 | }
460 |
461 | /**
462 | * Set a new life time
463 | *
464 | * @param int $newLifeTime new life time (in seconds)
465 | * @access public
466 | */
467 | function setLifeTime($newLifeTime)
468 | {
469 | $this->_lifeTime = $newLifeTime;
470 | $this->_setRefreshTime();
471 | }
472 |
473 | /**
474 | * Save the state of the caching memory array into a cache file cache
475 | *
476 | * @param string $id cache id
477 | * @param string $group name of the cache group
478 | * @access public
479 | */
480 | function saveMemoryCachingState($id, $group = 'default')
481 | {
482 | if ($this->_caching) {
483 | $array = array(
484 | 'counter' => $this->_memoryCachingCounter,
485 | 'array' => $this->_memoryCachingState
486 | );
487 | $data = serialize($array);
488 | $this->save($data, $id, $group);
489 | }
490 | }
491 |
492 | /**
493 | * Load the state of the caching memory array from a given cache file cache
494 | *
495 | * @param string $id cache id
496 | * @param string $group name of the cache group
497 | * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
498 | * @access public
499 | */
500 | function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false)
501 | {
502 | if ($this->_caching) {
503 | if ($data = $this->get($id, $group, $doNotTestCacheValidity)) {
504 | $array = unserialize($data);
505 | $this->_memoryCachingCounter = $array['counter'];
506 | $this->_memoryCachingArray = $array['array'];
507 | }
508 | }
509 | }
510 |
511 | /**
512 | * Return the cache last modification time
513 | *
514 | * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY !
515 | *
516 | * @return int last modification time
517 | */
518 | function lastModified()
519 | {
520 | return @filemtime($this->_file);
521 | }
522 |
523 | /**
524 | * Trigger error
525 | *
526 | * @param string $msg error message
527 | * @param int $code error code
528 | * @access public
529 | */
530 | function raiseError($msg, $code)
531 | {
532 | return new Exception($msg);
533 | }
534 |
535 | /**
536 | * Extend the life of a valid cache file
537 | *
538 | * see http://pear.php.net/bugs/bug.php?id=6681
539 | *
540 | * @access public
541 | */
542 | function extendLife()
543 | {
544 | @touch($this->_file);
545 | }
546 |
547 | // --- Private methods ---
548 |
549 | /**
550 | * Compute & set the refresh time
551 | *
552 | * @access private
553 | */
554 | function _setRefreshTime()
555 | {
556 | if (is_null($this->_lifeTime)) {
557 | $this->_refreshTime = null;
558 | } else {
559 | $this->_refreshTime = time() - $this->_lifeTime;
560 | }
561 | }
562 |
563 | /**
564 | * Remove a file
565 | *
566 | * @param string $file complete file path and name
567 | * @return boolean true if no problem
568 | * @access private
569 | */
570 | function _unlink($file)
571 | {
572 | if (!@unlink($file)) {
573 | return $this->raiseError('Cache_Lite : Unable to remove cache !', -3);
574 | }
575 | return true;
576 | }
577 |
578 | /**
579 | * Recursive function for cleaning cache file in the given directory
580 | *
581 | * @param string $dir directory complete path (with a trailing slash)
582 | * @param string $group name of the cache group
583 | * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup',
584 | 'callback_myFunction'
585 | * @return boolean true if no problem
586 | * @access private
587 | */
588 | function _cleanDir($dir, $group = false, $mode = 'ingroup')
589 | {
590 | if ($this->_fileNameProtection) {
591 | $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
592 | } else {
593 | $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
594 | }
595 | if ($this->_memoryCaching) {
596 | while (list($key, ) = each($this->_memoryCachingArray)) {
597 | if (strpos($key, $motif, 0)) {
598 | unset($this->_memoryCachingArray[$key]);
599 | $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
600 | }
601 | }
602 | if ($this->_onlyMemoryCaching) {
603 | return true;
604 | }
605 | }
606 | if (!($dh = opendir($dir))) {
607 | return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
608 | }
609 | $result = true;
610 | while ($file = readdir($dh)) {
611 | if (($file != '.') && ($file != '..')) {
612 | if (substr($file, 0, 6)=='cache_') {
613 | $file2 = $dir . $file;
614 | if (is_file($file2)) {
615 | switch (substr($mode, 0, 9)) {
616 | case 'old':
617 | // files older than lifeTime get deleted from cache
618 | if (!is_null($this->_lifeTime)) {
619 | if ((time() - @filemtime($file2)) > $this->_lifeTime) {
620 | $result = ($result and ($this->_unlink($file2)));
621 | }
622 | }
623 | break;
624 | case 'notingroup':
625 | if (!strpos($file2, $motif, 0)) {
626 | $result = ($result and ($this->_unlink($file2)));
627 | }
628 | break;
629 | case 'callback_':
630 | $func = substr($mode, 9, strlen($mode) - 9);
631 | if ($func($file2, $group)) {
632 | $result = ($result and ($this->_unlink($file2)));
633 | }
634 | break;
635 | case 'ingroup':
636 | default:
637 | if (strpos($file2, $motif, 0)) {
638 | $result = ($result and ($this->_unlink($file2)));
639 | }
640 | break;
641 | }
642 | }
643 | if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) {
644 | $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode)));
645 | }
646 | }
647 | }
648 | }
649 | return $result;
650 | }
651 |
652 | /**
653 | * Add some date in the memory caching array
654 | *
655 | * @param string $data data to cache
656 | * @access private
657 | */
658 | function _memoryCacheAdd($data)
659 | {
660 | $this->_memoryCachingArray[$this->_file] = $data;
661 | if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) {
662 | list($key, ) = each($this->_memoryCachingArray);
663 | unset($this->_memoryCachingArray[$key]);
664 | } else {
665 | $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1;
666 | }
667 | }
668 |
669 | /**
670 | * Make a file name (with path)
671 | *
672 | * @param string $id cache id
673 | * @param string $group name of the group
674 | * @access private
675 | */
676 | function _setFileName($id, $group)
677 | {
678 | if ($this->_fileNameProtection) {
679 | $suffix = 'cache_'.md5($group).'_'.md5($id);
680 | } else {
681 | $suffix = 'cache_'.$group.'_'.$id;
682 | }
683 | $root = $this->_cacheDir;
684 | if ($this->_hashedDirectoryLevel>0) {
685 | $hash = md5($suffix);
686 | for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) {
687 | $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
688 | }
689 | }
690 | $this->_fileName = $suffix;
691 | $this->_file = $root.$suffix;
692 | }
693 |
694 | /**
695 | * Read the cache file and return the content
696 | *
697 | * @return string content of the cache file (else : false or a PEAR_Error object)
698 | * @access private
699 | */
700 | function _read()
701 | {
702 | $fp = @fopen($this->_file, "rb");
703 | if ($this->_fileLocking) @flock($fp, LOCK_SH);
704 | if ($fp) {
705 | clearstatcache();
706 | $length = @filesize($this->_file);
707 | // $mqr = get_magic_quotes_runtime();
708 | // set_magic_quotes_runtime(0);
709 | if ($this->_readControl) {
710 | $hashControl = @fread($fp, 32);
711 | $length = $length - 32;
712 | }
713 | if ($length) {
714 | $data = @fread($fp, $length);
715 | } else {
716 | $data = '';
717 | }
718 | // set_magic_quotes_runtime($mqr);
719 | if ($this->_fileLocking) @flock($fp, LOCK_UN);
720 | @fclose($fp);
721 | if ($this->_readControl) {
722 | $hashData = $this->_hash($data, $this->_readControlType);
723 | if ($hashData != $hashControl) {
724 | if (!(is_null($this->_lifeTime))) {
725 | @touch($this->_file, time() - 2*abs($this->_lifeTime));
726 | } else {
727 | @unlink($this->_file);
728 | }
729 | return false;
730 | }
731 | }
732 | return $data;
733 | }
734 | return $this->raiseError('Cache_Lite : Unable to read cache !', -2);
735 | }
736 |
737 | /**
738 | * Write the given data in the cache file
739 | *
740 | * @param string $data data to put in cache
741 | * @return boolean true if ok (a PEAR_Error object else)
742 | * @access private
743 | */
744 | function _write($data)
745 | {
746 | $oldmask = umask(0);
747 | if ($this->_hashedDirectoryLevel > 0) {
748 | $hash = md5($this->_fileName);
749 | $root = $this->_cacheDir;
750 | for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) {
751 | $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/';
752 | if (!(@is_dir($root))) {
753 | @mkdir($root, $this->_hashedDirectoryUmask);
754 | }
755 | }
756 | }
757 | $fp = @fopen($this->_file, "wb");
758 | if ($fp) {
759 | if ($this->_fileLocking) @flock($fp, LOCK_EX);
760 | if ($this->_readControl) {
761 | @fwrite($fp, $this->_hash($data, $this->_readControlType), 32);
762 | }
763 | $len = strlen($data);
764 | @fwrite($fp, $data, $len);
765 | if ($this->_fileLocking) @flock($fp, LOCK_UN);
766 | @fclose($fp);
767 | umask($oldmask);
768 | return true;
769 | }
770 | umask($oldmask);
771 | return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1);
772 | }
773 |
774 | /**
775 | * Write the given data in the cache file and control it just after to avoir corrupted cache entries
776 | *
777 | * @param string $data data to put in cache
778 | * @return boolean true if the test is ok (else : false or a PEAR_Error object)
779 | * @access private
780 | */
781 | function _writeAndControl($data)
782 | {
783 | $result = $this->_write($data);
784 | if (is_object($result)) {
785 | return $result; # We return the PEAR_Error object
786 | }
787 | $dataRead = $this->_read();
788 | if (is_object($dataRead)) {
789 | return $result; # We return the PEAR_Error object
790 | }
791 | if ((is_bool($dataRead)) && (!$dataRead)) {
792 | return false;
793 | }
794 | return ($dataRead==$data);
795 | }
796 |
797 | /**
798 | * Make a control key with the string containing datas
799 | *
800 | * @param string $data data
801 | * @param string $controlType type of control 'md5', 'crc32' or 'strlen'
802 | * @return string control key
803 | * @access private
804 | */
805 | function _hash($data, $controlType)
806 | {
807 | switch ($controlType) {
808 | case 'md5':
809 | return md5($data);
810 | case 'crc32':
811 | return sprintf('% 32d', crc32($data));
812 | case 'strlen':
813 | return sprintf('% 32d', strlen($data));
814 | default:
815 | return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5);
816 | }
817 | }
818 |
819 | }
820 |
821 | ?>
822 |
--------------------------------------------------------------------------------
/src/Backend/external/Timed.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 |
18 | require_once('Lite.php');
19 |
20 | class Cache_Lite_Timed extends Cache_Lite
21 | {
22 | var $_bufferedLifetime;
23 |
24 | // --- Public methods ----
25 |
26 | /**
27 | * Constructor
28 | *
29 | * $options is an assoc. To have a look at availables options,
30 | * see the constructor of the Cache_Lite class in 'Cache_Lite.php'
31 | *
32 | * @param array $options options
33 | * @access public
34 | */
35 | function __construct($options = array(NULL))
36 | {
37 | parent::__construct($options);
38 | }
39 |
40 | /**
41 | * Save some data in a cache file
42 | *
43 | * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on)
44 | * @param string $id cache id
45 | * @param string $group name of the cache group
46 | * @param int $lifetime The time in seconds that this entry should live. Defaults to the lifetime
47 | * set by the constructor.
48 | * @return boolean true if no problem (else : false or a PEAR_Error object)
49 | * @access public
50 | */
51 | function save($data, $id = NULL, $group = 'default', $lifetime = null)
52 | {
53 | $res = parent::save($data, $id, $group);
54 | if ($res === true) {
55 | if ($lifetime == null) {
56 | $lifetime = $this->_bufferedLifetime;
57 | }
58 | if ($lifetime == null) {
59 | $lifetime = $this->_lifeTime;
60 | }
61 | $res = $this->_setLastModified(time() + $lifetime);
62 | if (is_object($res)) {
63 | // $res is a PEAR_Error object
64 | if (!($this->_errorHandlingAPIBreak)) {
65 | return false; // we return false (old API)
66 | }
67 | }
68 | }
69 | return $res;
70 | }
71 |
72 | /**
73 | * Sets the ctime/mtime status for a file for the given time.
74 | *
75 | * @param integer $time Unix timestamp
76 | * @return boolean
77 | */
78 | function _setLastModified($time) {
79 | if (@touch($this->_file, $time, $time) === false) {
80 | return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1);
81 | }
82 | return true;
83 | }
84 |
85 | /**
86 | * Override refresh time function. Returns current time.
87 | *
88 | */
89 | function _setRefreshTime() {
90 | if (is_null($this->_lifeTime)) {
91 | $this->_refreshTime = null;
92 | } else {
93 | $this->_refreshTime = time();
94 | }
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/src/Cache.php:
--------------------------------------------------------------------------------
1 | 0,
53 | CacheLogEnum::MISSED => 0,
54 | CacheLogEnum::DELETED => 0,
55 | CacheLogEnum::CLEANED => 0,
56 | CacheLogEnum::SAVED => 0,
57 | CacheLogEnum::PREFETCHED => 0,
58 | );
59 |
60 | /**
61 | * Stores cache log for debugging.
62 | * @var array
63 | */
64 | protected $cache_log = array();
65 |
66 | /**
67 | * Is log enabled? Log can take a lot of RAM, so only turn this on when
68 | * profiling.
69 | * @var boolean $should_log
70 | */
71 | protected $should_log = false;
72 |
73 | /**
74 | * Returns basic cache statistics. See $summary.
75 | *
76 | * @return array()
77 | */
78 | public static function getLogSummary() {
79 | return static::$summary;
80 | }
81 |
82 | public static function resetLogSummary() {
83 | static::$summary = array(
84 | CacheLogEnum::ACCESSED => 0,
85 | CacheLogEnum::MISSED => 0,
86 | CacheLogEnum::DELETED => 0,
87 | CacheLogEnum::CLEANED => 0,
88 | CacheLogEnum::SAVED => 0,
89 | CacheLogEnum::PREFETCHED => 0,
90 | );
91 | }
92 |
93 | /**
94 | *
95 | * @param boolean $b
96 | * @return CacheAbstract
97 | */
98 | public function setLog($b) {
99 | $this->should_log = $b;
100 | return $this;
101 | }
102 |
103 | /**
104 | * Returns a cache
105 | *
106 | * @param string $backend
107 | * @throws Cachearium\Exceptions\CacheInvalidBackendException
108 | * @return CacheAbstract
109 | */
110 | public static function factory($backend) {
111 | $classname = '\Cachearium\Backend\Cache' . $backend;
112 | if (!class_exists($classname)) {
113 | throw new Exceptions\CacheInvalidBackendException("Class does not exist");
114 | }
115 | return $classname::singleton();
116 | }
117 |
118 | /**
119 | * Clears all cache classes.
120 | * @codeCoverageIgnore
121 | */
122 | public static function clearAll() {
123 | $caches = [
124 | \Cachearium\Backend\CacheRAM::singleton(),
125 | \Cachearium\Backend\CacheFilesystem::singleton(),
126 | \Cachearium\Backend\CacheMemcached::singleton(),
127 | // TODO cache apc is broken \Cachearium\Backend\CacheAPC::singleton()
128 | ];
129 | foreach($caches as $cacheInst) {
130 | if ($cacheInst->isEnabled()) {
131 | $cacheInst->clear();
132 | }
133 | }
134 | }
135 |
136 | /**
137 | * Enable this cache
138 | *
139 | * @return CacheAbstract this
140 | */
141 | final public function setEnabled($b) {
142 | if ($b) {
143 | $this->enable();
144 | }
145 | else {
146 | $this->disable();
147 | }
148 | return $this;
149 | }
150 |
151 | /**
152 | * Enable this cache
153 | *
154 | * @return CacheAbstract this
155 | */
156 | public function enable() {
157 | $this->enabled = true;
158 | return $this;
159 | }
160 |
161 | /**
162 | * Disable this cache
163 | *
164 | * @return CacheAbstract
165 | */
166 | public function disable() {
167 | $this->enabled = false;
168 | return $this;
169 | }
170 |
171 | /**
172 | * @return True if cache is enabled, working and storing/retrieving data.
173 | */
174 | public function isEnabled() {
175 | return $this->enabled;
176 | }
177 |
178 | /**
179 | *
180 | * @param number $lifetime 0 for infinite
181 | */
182 | public function setDefaultLifetime($lifetime = 0) {
183 | $this->lifetime = $lifetime;
184 | }
185 |
186 | public function getDefaultLifetime() {
187 | return $this->lifetime;
188 | }
189 |
190 | /**
191 | * @param string $name An optional namespace.
192 | */
193 | public function setNamespace($name) {
194 | $this->namespace = $name;
195 | return $this;
196 | }
197 |
198 | /**
199 | * @return string
200 | */
201 | public function getNamespace() {
202 | return $this->namespace;
203 | }
204 |
205 | /**
206 | * Get cached entry.
207 | *
208 | * @param $k
209 | * @return mixed
210 | * @throws Cachearium\Exceptions\NotCachedException
211 | */
212 | abstract public function get(CacheKey $k);
213 |
214 | /**
215 | * Same as get(), but expanded parameters.
216 | *
217 | * @param string $base
218 | * @param string $id
219 | * @param mixed $sub
220 | * @return mixed
221 | * @throws Cachearium\Exceptions\NotCachedException
222 | * @see getK
223 | */
224 | public function getP($base, $id, $sub = null) {
225 | return $this->get(new CacheKey($base, $id, $sub));
226 | }
227 |
228 | /**
229 | * Same as get, but assumes data was stored with a CacheData object
230 | * and will treat it accordingly.
231 | *
232 | * @param CacheKey $k
233 | * @return CacheData
234 | * @throws Cachearium\Exceptions\NotCachedException
235 | */
236 | public function getData(CacheKey $k) {
237 | $cd = CacheData::unserialize($this->get($k));
238 | if ($cd->checkUpdateToDate($this)) {
239 | return $cd;
240 | }
241 | throw new Exceptions\NotCachedException();
242 | }
243 |
244 | /**
245 | * Same as getData(), but expanded parameters.
246 | *
247 | * @see getData()
248 | * @param string $base
249 | * @param string $id
250 | * @param mixed $sub
251 | */
252 | public function getDataP($base, $id, $sub = null) {
253 | return $this->getData(new CacheKey($base, $id, $sub));
254 | }
255 |
256 | /**
257 | * Gets data from multiple cache keys at once
258 | *
259 | * Backends may override this to provide an efficient implementation over multiple
260 | * calls to get().
261 | *
262 | * @param array $cacheid List of cache keys
263 | * @param callable $callback if present will be called for any \NotCachedExceptions.
264 | * Callback should have this signature: (CacheAbstract $c, CacheKey $k)
265 | * @return array:mixed array with data, using same keys as cacheid. Keys not
266 | * found in cache won't be present, but no exception will be thrown
267 | */
268 | public function getMulti(array $cacheid, $callback = null) {
269 | $retval = [];
270 | foreach ($cacheid as $k => $c) {
271 | try {
272 | $retval[$k] = $this->get($c);
273 | }
274 | catch (Exceptions\NotCachedException $e) {
275 | // if there is a callback, call it
276 | if ($callback) {
277 | $retval[$k] = call_user_func($callback, $this, $c);
278 | }
279 | }
280 | }
281 | return $retval;
282 | }
283 |
284 | /**
285 | * Increment a variable. Backend deals with this, but in general this is atomic.
286 | * Backend must only guarantee that the increment is made, but the final value
287 | * may not be current + $value due to concurrent accesses.
288 | *
289 | * @param integer $value
290 | * @param CacheKey $k
291 | * @param integer $default If key is not in cache, this value is returned.
292 | * @return integer
293 | */
294 | abstract public function increment($value, CacheKey $k, $default = 0);
295 |
296 | /**
297 | * Invalidates a dependency index. If the index does not exist it is created.
298 | * @param CacheKey $k
299 | */
300 | public function invalidate(CacheKey $k) {
301 | return $this->increment(1, $k, 0);
302 | }
303 |
304 | /**
305 | * Saves data in cache.
306 | *
307 | * @param mixed $data Data to save in cache
308 | * @param CacheKey $k
309 | * @param integer $lifetime The lifetime in sceonds, although it is up to the implementation whether
310 | * it is respected or not.
311 | * @return boolean true if no problem
312 | */
313 | abstract public function store($data, CacheKey $k, $lifetime = 0);
314 |
315 | /**
316 | * Same as store() but expanded parameters
317 | *
318 | * @param mixed $data
319 | * @param string $base
320 | * @param string $sub
321 | * @param string $id
322 | * @param number $lifetime
323 | * @return boolean true if no problem
324 | * @see store()
325 | */
326 | public function storeP($data, $base, $id, $sub = null, $lifetime = 0) {
327 | return $this->store($data, new CacheKey($base, $id, $sub), $lifetime);
328 | }
329 |
330 | /**
331 | * Same as store() but expanded parameters
332 | *
333 | * @param CacheData $data
334 | * @param number $lifetime
335 | * @return boolean true if no problem
336 | * @see store()
337 | */
338 | public function storeData(CacheData $data, $lifetime = 0) {
339 | return $this->store($data->updateDependenciesHash($this)->serialize(), $data->key, $lifetime);
340 | }
341 |
342 | /**
343 | * Deletes an entry from the cache
344 | *
345 | * @param CacheKey $k
346 | * @return boolean
347 | */
348 | abstract public function delete(CacheKey $k);
349 |
350 | /**
351 | * @see delete()
352 | * @param string $base
353 | * @param string $id
354 | * @param mixed $sub
355 | */
356 | public function deleteP($base, $id, $sub = null) {
357 | return $this->delete(new CacheKey($base, $id, $sub));
358 | }
359 |
360 | /**
361 | * Cleans cache: all entries with a certain $base and $id in the $key
362 | * are deleted.
363 | *
364 | * @param CacheKey $k
365 | * @return boolean true if no problem
366 | */
367 | public function clean(CacheKey $k) {
368 | return $this->cleanP($k->getBase(), $k->getId());
369 | }
370 |
371 | /**
372 | * Cleans cache: all entries with a certain $base and $id
373 | *
374 | * @return boolean true if no problem
375 | */
376 | abstract public function cleanP($base, $id);
377 |
378 | /**
379 | * Clears entire cache. Use sparingly.
380 | */
381 | abstract public function clear();
382 |
383 | /**
384 | * Prefetches data which will be used. This avoids multiple trips to the cache
385 | * server if they can be avoided.
386 | *
387 | * Backend may ignore this call and implement a noop.
388 | *
389 | * @param array $data array(0 => CacheKey, ...)
390 | */
391 | abstract public function prefetch($data);
392 |
393 | /**
394 | * Generates a report for this backend
395 | *
396 | * @codeCoverageIgnore
397 | */
398 | abstract public function report();
399 |
400 | /**
401 | * Starts a cache if it doesn't exist, or outputs the data and returns true.
402 | * Calls extraSub().
403 | *
404 | * @param CacheKey $k
405 | * @param string $lifetime The lifetime, in seconds
406 | * @param boolean $print if True echoes the data
407 | * @param boolean $fail if false throws an exception if something happens, such
408 | * as not cached
409 | * @return boolean|string True if cached
410 | * @review
411 | */
412 | public function start(CacheKey $k, $lifetime = null, $print = true, $fail = false) {
413 | $this->extraSub($k->sub);
414 |
415 | return $this->recursiveStart($k, $lifetime, $print, $fail);
416 | }
417 |
418 | /**
419 | * @see recursiveStart()
420 | */
421 | public function recursiveStartP($base, $id, $sub = null, $lifetime = null, $print = true, $fail = false) {
422 | return $this->recursivestart(new CacheKey($base, $id, $sub), $lifetime, $print, $fail);
423 | }
424 |
425 | /**
426 | * @see start()
427 | */
428 | public function startP($base, $id, $sub = null, $lifetime = null, $print = true, $fail = false) {
429 | return $this->start(new CacheKey($base, $id, $sub), $lifetime, $print, $fail);
430 | }
431 |
432 | /**
433 | * start() using a callable. Same as start()/c()/end().
434 | *
435 | * @param CacheKey $k
436 | * @param callable $c A callable. Whatever it prints will be cached.
437 | * @param array $cparams parameters for the callback, optional
438 | * @param integer $lifetime
439 | */
440 | public function startCallback(CacheKey $k, callable $c, array $cparams = [], $lifetime = null) {
441 | $data = $this->start($k, $lifetime);
442 | if ($data === false) {
443 | call_user_func_array($c, $cparams);
444 | $data = $this->end(false);
445 | }
446 | return $data;
447 | }
448 |
449 | /**
450 | * Appends a callback to the current start()/end() cache
451 | *
452 | * Callbacks are always called at runtime, their result is never cached at
453 | * this level. You may cache it in the callback, of course.
454 | *
455 | * @param function $callback
456 | * @return boolean
457 | * @review
458 | */
459 | public function appendCallback(callable $callback) {
460 | // @codeCoverageIgnoreStart
461 | if (!$this->enabled) {
462 | return false;
463 | }
464 | // @codeCoverageIgnoreEnd
465 |
466 | if (!$this->inloop) {
467 | return false;
468 | }
469 |
470 | $data = ob_get_contents();
471 | ob_clean();
472 | $this->loopdata[$this->inloop]->appendData($data);
473 | $this->loopdata[$this->inloop]->appendCallback($callback);
474 |
475 | return true;
476 | }
477 |
478 | /**
479 | * Returns a key given parameters. This is up to storage and different
480 | * values may be returned for the same parameters, as storages are likely
481 | * to use key-based cache expiration.
482 | *
483 | * @param CacheKey $k
484 | */
485 | abstract protected function hashKey(CacheKey $k);
486 |
487 | protected function keyFromDeps(CacheKey $k, $deps) {
488 | $mainkey = $this->hashKey($k);
489 | foreach ($deps as $d) { // TODO: arrays are ugly
490 | $mainkey .= $this->hashKey($d); // TODO: one fetch for all
491 | }
492 | $mainkey = md5($mainkey);
493 | return $mainkey;
494 | }
495 |
496 | /**
497 | * Get extra sub
498 | * @param unknown $sub
499 | */
500 | private function extraSub(&$sub) {
501 | if (!is_callable('application_cacheDependencies')) {
502 | return;
503 | }
504 | $extra = application_cacheDependencies();
505 | if (is_array($sub)) {
506 | $sub['cacheExtraSubApplication'] = $extra;
507 | }
508 | else {
509 | $sub .= $extra;
510 | }
511 | }
512 |
513 | public function newstart(CacheKey $k, $lifetime = null, $fail = false) {
514 | // @codeCoverageIgnoreStart
515 | if (!$this->enabled) {
516 | return false;
517 | }
518 | // @codeCoverageIgnoreEnd
519 |
520 | // fetch cache
521 | try {
522 | $cachedata = $this->getData($k);
523 | } catch (Exceptions\NotCachedException $e) {
524 | // not cached
525 | if ($fail) {
526 | throw $e;
527 | }
528 | }
529 |
530 | $this->inloop++;
531 | $this->loopdata[$this->inloop] = new CacheData();
532 | if ($this->inloop > 1) {
533 | // we are recursive. push whatever we have so far in the previous cache
534 | $data = ob_get_contents();
535 | ob_clean();
536 | $this->loopdata[$this->inloop - 1]->appendData($data);
537 | $this->loopdata[$this->inloop - 1]->appendRecursion($k);
538 | }
539 | else {
540 | // something was not cached below. We invalidated all cache
541 | // dependencies
542 | }
543 |
544 | $this->loopdata[$this->inloop]->setKey($k);
545 | $this->loopdata[$this->inloop]->lifetime = $lifetime ? $lifetime : $this->lifetime;
546 |
547 | ob_start();
548 | ob_implicit_flush(false);
549 |
550 | return false;
551 | }
552 |
553 | public function newEnd($print = true) {
554 | // @codeCoverageIgnoreStart
555 | if (!$this->enabled) {
556 | return false;
557 | }
558 | // @codeCoverageIgnoreEnd
559 |
560 | $data = ob_get_clean();
561 |
562 | /* @var $cachedata CacheData */
563 | $cachedata = $this->loopdata[$this->inloop];
564 | $cachedata->appendData($data);
565 |
566 | $cachedata->generateDependenciesHash($this);
567 | $mainkey = $this->keyFromDeps($cachedata->getKey(), $cachedata->dependencies);
568 | if (!$this->storeP($cachedata, 'cacherecursive', 0, $mainkey)) {
569 | throw new \Cachearium\Exceptions\CacheStoreFailure("Storing key");
570 | }
571 | if (!$this->storeData($cachedata)) {
572 | throw new \Cachearium\Exceptions\CacheStoreFailure("Storing data");
573 | }
574 |
575 | // if recursive
576 | $this->inloop--;
577 | if ($this->inloop > 0) {
578 | return false;
579 | }
580 |
581 | if ($print) {
582 | $key = "cache-" . rand();
583 | // @codeCoverageIgnoreStart
584 | if (static::$debugOnPage) {
585 | echo '';
593 | }
594 | // @codeCoverageIgnoreEnd
595 |
596 | echo $cachedata->stringify($this);
597 |
598 | // @codeCoverageIgnoreStart
599 | if (static::$debugOnPage) {
600 | echo '';
601 | }
602 | // @codeCoverageIgnoreEnd
603 | return;
604 | }
605 |
606 | return $cachedata->stringify($this);
607 | }
608 |
609 | /**
610 | * Prints HTML for cache debug probes -> opens tag
611 | *
612 | * @param string $key
613 | * @param CacheData $cachedata
614 | * @param string $type
615 | * @codeCoverageIgnore
616 | */
617 | protected function printProbeStart($key, CacheData $cachedata, $type) {
618 | echo '';
626 | }
627 |
628 | /**
629 | * Prints HTML for cache debug probes -> closes tag
630 | *
631 | * @param string $key
632 | * @param CacheData $cachedata
633 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
634 | * @codeCoverageIgnore
635 | */
636 | protected function printProbeEnd($key, CacheData $cachedata) {
637 | echo '';
638 | }
639 |
640 | /**
641 | *
642 | * @param CacheKey $k
643 | * @param integer $lifetime if null uses the class default
644 | * @param boolean $print
645 | * @param boolean $fail if true throws a NotCachedException if not cached.
646 | * @throws Cachearium\Exceptions\NotCachedException
647 | * @throws Cachearium\Exceptions\CacheKeyClashException
648 | * @return string The cached item as a string or false if not cached.
649 | */
650 | public function recursiveStart(CacheKey $k, $lifetime = null, $print = true, $fail = false) {
651 | // @codeCoverageIgnoreStart
652 | if (!$this->enabled) {
653 | return false;
654 | }
655 | // @codeCoverageIgnoreEnd
656 |
657 | foreach ($this->loopdata as $l) {
658 | /* @var $l CacheData */
659 | if ($l->checkClash($k)) {
660 | throw new Exceptions\CacheKeyClashException();
661 | }
662 | }
663 |
664 | // check if we are inside another cache for automatic dependencies.
665 | /* @var $cachedata CacheData */
666 | $cachedata = null;
667 | try {
668 | $cachedata = $this->getData($k);
669 |
670 | if (!$cachedata->checkUpdateToDate($this)) {
671 | // stale
672 | $cachedata = null;
673 | }
674 | // TODO $this->prefetch($cachedata->getDependencies());
675 | }
676 | catch (Exceptions\NotCachedException $e) {
677 | }
678 |
679 | // found. just return it.
680 | if ($cachedata) {
681 | try {
682 | $this->log(
683 | CacheLogEnum::ACCESSED,
684 | $cachedata->key,
685 | $cachedata->lifetime
686 | );
687 | $key = "cache-" . rand();
688 |
689 | $retval = $cachedata->stringify($this);
690 |
691 | if ($print) {
692 | // @codeCoverageIgnoreStart
693 | if (static::$debugOnPage) {
694 | $this->printProbeStart($key, $cachedata, 'hit');
695 | }
696 | // @codeCoverageIgnoreEnd
697 |
698 | echo $retval;
699 |
700 | // @codeCoverageIgnoreStart
701 | if (static::$debugOnPage) {
702 | $this->printProbeEnd($key, $cachedata);
703 | }
704 | // @codeCoverageIgnoreEnd
705 | }
706 | return $retval;
707 | }
708 | catch (Exceptions\NotCachedException $e) {
709 | $this->delete($k); // clear recursively
710 | if ($this->inloop) {
711 | throw $e;
712 | }
713 | }
714 | }
715 | if ($fail) {
716 | throw new Exceptions\NotCachedException();
717 | }
718 |
719 | $this->inloop++;
720 | $cd = new CacheData($k);
721 | $cd->setLifetime($lifetime ? $lifetime : $this->lifetime);
722 | $this->loopdata[$this->inloop] = $cd;
723 |
724 | if ($this->inloop > 1) {
725 | // we are recursive. push whatever we have so far in the previous cache
726 | $data = ob_get_contents();
727 | ob_clean();
728 |
729 | foreach ($this->loopdata as $l) {
730 | if ($l == $cd) { // don't depend on itself
731 | continue;
732 | }
733 | /* @var $l CacheData */
734 | $l->addDependency($k);
735 | }
736 | $this->loopdata[$this->inloop - 1]->appendData($data);
737 | $this->loopdata[$this->inloop - 1]->appendRecursionData($cd);
738 | }
739 | else {
740 | // something was not cached below. We invalidated all cache
741 | // dependencies
742 | }
743 |
744 | ob_start();
745 | ob_implicit_flush(false);
746 |
747 | return false;
748 | }
749 |
750 | /**
751 | *
752 | * @param boolean $print
753 | * @throws \Cachearium\Exceptions\CacheStoreFailure
754 | * @return string The string. If $print == true the string is printed as well.
755 | */
756 | public function recursiveEnd($print = true) {
757 | // @codeCoverageIgnoreStart
758 | if (!$this->enabled) {
759 | return '';
760 | }
761 | // @codeCoverageIgnoreEnd
762 |
763 | $data = ob_get_clean();
764 |
765 | /* @var $cachedata CacheData */
766 | $cachedata = $this->loopdata[$this->inloop];
767 | $cachedata->appendData($data);
768 |
769 | try {
770 | $cachedata->generateDependenciesHash($this);
771 | }
772 | catch (\Cachearium\Exceptions\CacheUnsupportedOperation $e) {
773 | // not much we can do here, so just keep on going
774 | }
775 | $mainkey = $this->keyFromDeps($cachedata->getKey(), $cachedata->dependencies);
776 | if (!$this->storeP($cachedata, 'cacherecursive', 0, $mainkey)) {
777 | throw new \Cachearium\Exceptions\CacheStoreFailure("Storing key");
778 | }
779 | if (!$this->storeData($cachedata)) {
780 | throw new \Cachearium\Exceptions\CacheStoreFailure("Storing data");
781 | }
782 |
783 | // if recursive
784 | unset($this->loopdata[$this->inloop]);
785 | $this->inloop--;
786 | if ($this->inloop > 0) {
787 | return '';
788 | }
789 |
790 | if ($print) {
791 | $key = "cache-" . rand();
792 | // @codeCoverageIgnoreStart
793 | if (static::$debugOnPage) {
794 | $this->printProbeStart($key, $cachedata, 'save');
795 | }
796 | // @codeCoverageIgnoreEnd
797 |
798 | $str = $cachedata->stringify($this);
799 | echo $str;
800 |
801 | // @codeCoverageIgnoreStart
802 | if (static::$debugOnPage) {
803 | $this->printProbeEnd($key, $cachedata);
804 | }
805 | // @codeCoverageIgnoreEnd
806 | return $str;
807 | }
808 |
809 | return $cachedata->stringify($this);
810 | }
811 |
812 | /**
813 | * Ends the cache start().
814 | * @see recursiveEnd()
815 | */
816 | public function end($print = true) {
817 | return $this->recursiveEnd($print);
818 | }
819 |
820 | /**
821 | * Cancels something started by recursiveStart() if you don't want to call recursiveEnd()
822 | *
823 | */
824 | public function recursiveAbort() {
825 | // @codeCoverageIgnoreStart
826 | if (!$this->enabled) {
827 | return;
828 | }
829 | // @codeCoverageIgnoreEnd
830 |
831 | ob_end_clean();
832 |
833 | // if recursive
834 | unset($this->loopdata[$this->inloop]);
835 | $this->inloop--;
836 |
837 | return;
838 | }
839 |
840 | /**
841 | * Alias for recursiveAbort()
842 | */
843 | public function abort() {
844 | $this->recursiveAbort();
845 | }
846 |
847 | /*
848 | * DEBUG
849 | */
850 |
851 | /**
852 | * High level log for testing and debugging
853 | *
854 | * @codeCoverageIgnore
855 | */
856 | public static function logHigh($message) {
857 | if (static::$debugLogFile) {
858 | file_put_contents(static::$debugLogFile, $message, FILE_APPEND);
859 | }
860 | }
861 |
862 | /**
863 | * Logs cache accesses for debugging
864 | *
865 | * @param string $status CacheLogEnum constant
866 | * @param CacheKey $k The message to print.
867 | * @param integer $lifetime
868 | * @codeCoverageIgnore
869 | */
870 | protected function log($status, CacheKey $k, $lifetime = 0) {
871 | static::$summary[$status]++;
872 |
873 | if ($this->should_log == false) {
874 | return;
875 | }
876 |
877 | $bt = debug_backtrace();
878 | foreach ($bt as $i => $d) {
879 | if (strpos($d['file'], '/Cache') === false) {
880 | // TODO: if() may not work well if user has a file called Cache
881 | $trace = $d['function'] . ' at ' . $d['file'] . ':' . $d['line'];
882 | $this->cache_log[] = array(
883 | 'status' => $status,
884 | 'message' => "(" . $k->debug() . ", $lifetime) by " . $trace
885 | );
886 | break;
887 | }
888 | }
889 | }
890 |
891 | /**
892 | * Dumps a short HTML summary of the cache hits/misses
893 | * @codeCoverageIgnore
894 | */
895 | public static function dumpSummary() {
896 | echo 'Cache Summary (non-ajax): ';
897 | foreach (static::getLogSummary() as $key => $val) {
898 | echo $key . '=>' . $val . ' / ';
899 | }
900 | echo '
';
901 | }
902 |
903 | /**
904 | * Renders CSS for live view debugging of cached data.
905 | * @codeCoverageIgnore
906 | */
907 | public static function cssDebug() {
908 | ?>
909 | [class^="cachearium-debug-probe"] {
910 | width: 10px;
911 | height: 10px;
912 | background-color: #f00;
913 | display: inline;
914 | /*visibility: hidden; */
915 | }
916 | .cachearium-debug-overview {
917 | position: absolute;
918 | left: 0;
919 | top: 0;
920 | background-color: rgba(255, 255, 255, 1);
921 | border: 1px solid grey;
922 | z-index: 5000;
923 | }
924 | .cachearium-debug-view {
925 | position: absolute;
926 | pointer-events: none;
927 | border: 1px solid black;
928 | }
929 |
930 | .cachearium-debug-view[data-type="hit"] {
931 | background-color: rgba(0, 255, 0, 0.1);
932 | }
933 | .cachearium-debug-view[data-type="save"] {
934 | background-color: rgba(255, 0, 0, 0.1);
935 | }
936 | .cachearium-debug-view .cachearium-debug-view-innerdata {
937 | float: right;
938 | color: #000;
939 | height: 10px;
940 | width: 10px;
941 | border: 1px solid grey;
942 | pointer-events: auto;
943 | overflow: hidden;
944 | background-color: rgba(255, 0, 0, 0.7);
945 | }
946 | .cachearium-debug-view .cachearium-debug-view-innerdata:hover {
947 | width: auto;
948 | height: auto;
949 | background-color: rgba(255, 255, 255, 0.9);
950 | border: 1px solid grey;
951 | }
952 |
966 |
1028 | setKey($ck);
47 |
48 | if ($data) {
49 | $this->appendData($data);
50 | }
51 | }
52 |
53 | /**
54 | *
55 | * @param CacheKey $ck
56 | * @return CacheData
57 | */
58 | public function setKey(CacheKey $ck) {
59 | $this->key = $ck;
60 | return $this;
61 | }
62 |
63 | /**
64 | *
65 | * @param unknown $callback
66 | * @return CacheData
67 | */
68 | public function appendCallback(callable $callback) {
69 | $this->data[] = array('type' => self::CACHEDATA_TYPE_CALLBACK, 'data' => $callback);
70 | return $this;
71 | }
72 |
73 | /**
74 | *
75 | * @param CacheData $cd
76 | * @return CacheData
77 | */
78 | public function mergeDependencies(CacheData $cd) {
79 | $this->dependencies = array_unique(array_merge($this->dependencies, $cd->dependencies));
80 | return $this;
81 | }
82 |
83 | public function clearDependenciesHash() {
84 | $this->dependenciesHash = '';
85 | }
86 |
87 | /**
88 | * Checks if dependencies are still fresh.
89 | * @param CacheAbstract $cache
90 | * @return boolean
91 | */
92 | public function checkUpdateToDate(CacheAbstract $cache) {
93 | // no deps? bail out
94 | if (!count($this->dependencies)) {
95 | return true;
96 | }
97 | if ($this->generateDependenciesHash($cache) == $this->dependenciesHash) {
98 | return true;
99 | }
100 | return false;
101 | }
102 |
103 | /**
104 | * Init dependencies. If increment is not supported by backend return 0.
105 | *
106 | * @param CacheAbstract $cache
107 | * @param CacheKey $k
108 | * @return integer
109 | */
110 | public function dependencyInit(CacheAbstract $cache, CacheKey $k) {
111 | try {
112 | return $cache->increment(0, $k, 0);
113 | }
114 | catch (\Cachearium\Exceptions\CacheUnsupportedOperation $e) {
115 | return 0;
116 | }
117 | }
118 |
119 | /**
120 | * Get a fresh hash based on dependencies. Does not update the current hash.
121 | * @param CacheAbstract $cache
122 | * @return string
123 | */
124 | public function generateDependenciesHash(CacheAbstract $cache) {
125 | if (!count($this->dependencies)) {
126 | return '';
127 | }
128 | $values = $cache->getMulti($this->dependencies, array($this, 'dependencyInit'));
129 | return md5(implode($values));
130 | }
131 |
132 | public function updateDependenciesHash(CacheAbstract $cache) {
133 | $this->dependenciesHash = $this->generateDependenciesHash($cache);
134 | return $this;
135 | }
136 |
137 | public function updateDependenciesHashIfNull(CacheAbstract $cache) {
138 | if (!$this->dependenciesHash) {
139 | $this->dependenciesHash = $this->generateDependenciesHash($cache);
140 | }
141 | return $this;
142 | }
143 |
144 | public function getKey() {
145 | return $this->key;
146 | }
147 |
148 | public function getDependenciesHash() {
149 | return $this->dependenciesHash;
150 | }
151 |
152 | public function getDependencies() {
153 | return $this->dependencies;
154 | }
155 |
156 | /**
157 | *
158 | * @param mixed $data Any kind of data you want to store. usually strings.
159 | * @return CacheData
160 | */
161 | public function appendData($data) {
162 | if ($data) {
163 | $this->data[] = array('type' => self::CACHEDATA_TYPE_DATA, 'data' => $data);
164 | }
165 | return $this;
166 | }
167 |
168 | /**
169 | * Convenience function. Returns the first data self::CACHEDATA_TYPE_DATA that you
170 | * stored. Returns null if there is none.
171 | *
172 | * @return any|NULL
173 | */
174 | public function getFirstData() {
175 | foreach ($this->data as $d) {
176 | if ($d['type'] == self::CACHEDATA_TYPE_DATA) {
177 | return $d['data'];
178 | }
179 | }
180 | return null;
181 | }
182 |
183 | public function appendRecursion(CacheKey $k) {
184 | $this->addDependency($k);
185 | $this->data[] = array(
186 | 'type' => self::CACHEDATA_TYPE_RECURSION,
187 | 'data' => $k
188 | );
189 | return $this;
190 | }
191 |
192 | public function appendRecursionData(CacheData $d) {
193 | if (!$d->getKey()) {
194 | throw new Exceptions\CacheInvalidDataException();
195 | }
196 | $this->addDependency($d->getKey());
197 | $this->data[] = array(
198 | 'type' => self::CACHEDATA_TYPE_RECURSION_DATA,
199 | 'data' => $d->getKey()
200 | );
201 | return $this;
202 | }
203 |
204 | /**
205 | * Adds a dependency
206 | * @param CacheKey $k
207 | * @return CacheData This
208 | */
209 | public function addDependency(CacheKey $k) {
210 | $this->dependencies[] = $k;
211 | $this->clearDependenciesHash();
212 | return $this;
213 | }
214 |
215 | /**
216 | * Adds a dependency
217 | * @param array $k
218 | * @return CacheData This
219 | */
220 | public function addDependencies(array $deps) {
221 | foreach ($deps as $k) {
222 | $this->addDependency($k);
223 | }
224 | return $this;
225 | }
226 |
227 | /**
228 | * Sets the list of dependencies and updates the dependency hash
229 | *
230 | * @param array $deps
231 | * @param CacheAbstract $cache
232 | * @return CacheData
233 | */
234 | public function setDependencies(array $deps, CacheAbstract $cache) {
235 | $this->dependencies = [];
236 | foreach ($deps as $k) {
237 | $this->addDependency($k);
238 | }
239 | $this->generateDependenciesHash($cache);
240 | return $this;
241 | }
242 |
243 | /**
244 | *
245 | * @param integer $lifetime
246 | * @return CacheData
247 | */
248 | public function setLifetime($lifetime) {
249 | $this->lifetime = $lifetime;
250 | return $this;
251 | }
252 |
253 | /**
254 | * Checks if a set of keys clashes with the ones used here.
255 | * @param CacheKey $k
256 | * @return boolean True if they match and there is a clash
257 | */
258 | public function checkClash(CacheKey $k) {
259 | return ($this->key == $k);
260 | }
261 |
262 | /**
263 | * Converts this data to a string that can output. This is not a hash
264 | * key or a serialization, but an actual render for humans.
265 | *
266 | * @throws Exceptions\NotCachedException
267 | */
268 | public function stringify(CacheAbstract $c, $recurse = true) {
269 | $retval = [];
270 | foreach ($this->data as $item) {
271 | if ($item['type'] == self::CACHEDATA_TYPE_CALLBACK) {
272 | $callback = $item['data'];
273 | if (is_callable($callback)) {
274 | $retval[] = call_user_func($callback);
275 | }
276 | else {
277 | // throw?
278 | }
279 | }
280 | else if ($item['type'] == self::CACHEDATA_TYPE_RECURSION) {
281 | if ($recurse) {
282 | $retval[] = $c->get($item['data']);
283 | }
284 | }
285 | else if ($item['type'] == self::CACHEDATA_TYPE_RECURSION_DATA) {
286 | if ($recurse) {
287 | $data = $c->getData($item['data']);
288 | $retval[] = $data->stringify($c);
289 | }
290 | }
291 | else {
292 | $retval[] = $item['data'];
293 | }
294 | }
295 |
296 | return implode('', $retval);
297 | }
298 |
299 | /**
300 | * Serialize this object to a string so we can easily store.
301 | *
302 | * @return string
303 | */
304 | public function serialize() {
305 | return serialize($this);
306 | }
307 |
308 | /**
309 | * Unserializes data and returns a new CacheData. This is what you
310 | * should use to get the object data from the storage.
311 | *
312 | * @param string $data
313 | * @return CacheData
314 | */
315 | static public function unserialize($data) {
316 | return unserialize($data);
317 | }
318 | }
319 |
--------------------------------------------------------------------------------
/src/CacheKey.php:
--------------------------------------------------------------------------------
1 | base = $base;
37 | $this->id = $id;
38 | $this->sub = $sub;
39 | }
40 |
41 | public function getBase() {
42 | return $this->base;
43 | }
44 |
45 | public function getId() {
46 | return $this->id;
47 | }
48 |
49 | public function getSub() {
50 | return $this->sub;
51 | }
52 |
53 | public function setBase($base) {
54 | $this->base = $base;
55 | return $this;
56 | }
57 |
58 | public function setId($id) {
59 | $this->id = $id;
60 | return $this;
61 | }
62 |
63 | public function setSub($sub) {
64 | $this->sub = $sub;
65 | return $this;
66 | }
67 |
68 | /**
69 | * Returns a hash for key.
70 | * @return string
71 | */
72 | public function getHash() {
73 | return md5($this->base . $this->id . $this->sub);
74 | }
75 |
76 | /**
77 | * Prints as a pretty string for debugging
78 | * @return string
79 | * @codeCoverageIgnore
80 | */
81 | public function debug() {
82 | return $this->base . ", " . $this->id . ", " . print_r($this->sub, true);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/CacheLogEnum.php:
--------------------------------------------------------------------------------
1 | 'Accessed',
22 | self::MISSED => 'Missed',
23 | self::DELETED => 'Deleted',
24 | self::CLEANED => 'Cleaned',
25 | self::SAVED => 'Saved',
26 | self::PREFETCHED => 'Prefetched'
27 | );
28 | }
29 |
30 | /**
31 | * Returns an array with all enum values.
32 | * @return array
33 | * @codeCoverageIgnore
34 | */
35 | static public function getAll() {
36 | return array_keys(static::getNames());
37 | }
38 |
39 | /**
40 | * Checks if a value is a valid grant
41 | *
42 | * @param string $value
43 | * @return boolean true if valid
44 | * @codeCoverageIgnore
45 | */
46 | static public function valid($value) {
47 | return array_key_exists($value, static::getNames());
48 | }
49 |
50 | /**
51 | * Given a name, returns its value or a string saying it is invalid.
52 | *
53 | * @param string $value
54 | * @return string
55 | * @codeCoverageIgnore
56 | */
57 | static public function getName($value) {
58 | if (static::valid($value)) {
59 | $x = static::getNames();
60 | return $x[$value];
61 | }
62 | return 'Invalid: ' . htmlspecialchars($value);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Cached.php:
--------------------------------------------------------------------------------
1 | getId(), $atts);
11 | *
12 | * @return CacheKey
13 | */
14 | public function getCacheKey($atts = null);
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/src/Exceptions/CacheInvalidBackendException.php:
--------------------------------------------------------------------------------
1 | addServers([['localhost', 11211]]); // init server
16 | CacheAbstract::clearAll();
17 | }
18 |
19 | static public function tearDownAfterClass() {
20 | }
21 |
22 | public function testFactory() {
23 | try {
24 | CacheAbstract::factory("invalidbackend");
25 | $this->assertTrue(false);
26 | }
27 | catch (Cachearium\Exceptions\CacheInvalidBackendException $e) {
28 | $this->assertTrue(true);
29 | }
30 | }
31 |
32 | private function setGetClean(CacheAbstract $cache) {
33 | $base = 'base';
34 |
35 | // enable
36 | $this->assertTrue($cache->isEnabled());
37 | $cache->setEnabled(false);
38 | $this->assertFalse($cache->isEnabled());
39 | $cache->setEnabled(true);
40 | $this->assertTrue($cache->isEnabled());
41 | $cache->disable();
42 | $this->assertFalse($cache->isEnabled());
43 | $cache->enable();
44 | $this->assertTrue($cache->isEnabled());
45 |
46 | $cache->setDefaultLifetime(3600);
47 | $this->assertEquals(3600, $cache->getDefaultLifetime());
48 |
49 | $key1 = new CacheKey($base, 1);
50 | $cache->clean($key1);
51 |
52 | try {
53 | $data = $cache->get($key1);
54 | $this->fail();
55 | }
56 | catch (Cachearium\Exceptions\NotCachedException $e) {
57 | $this->assertTrue(true);
58 | }
59 |
60 | $retval = $cache->store(234, $key1);
61 | $this->assertTrue($retval);
62 |
63 | try {
64 | $data = $cache->get($key1);
65 | $this->assertEquals(234, $data);
66 | }
67 | catch (Cachearium\Exceptions\NotCachedException $e) {
68 | $this->fail();
69 | }
70 |
71 | // sleep(1);
72 |
73 | try {
74 | $data = $cache->get($key1);
75 | $this->assertEquals(234, $data);
76 | }
77 | catch (Cachearium\Exceptions\NotCachedException $e) {
78 | $this->fail();
79 | }
80 |
81 | $cache->clean($key1);
82 | try {
83 | $data = $cache->get($key1);
84 | $this->fail();
85 | }
86 | catch (Cachearium\Exceptions\NotCachedException $e) {
87 | $this->assertTrue(true);
88 | }
89 |
90 | $key2 = new CacheKey($base, 2, 'a');
91 | $key3 = new CacheKey($base, 3, 'a');
92 | // now change again and delete
93 | $retval = $cache->store(234, $key2);
94 | $this->assertEquals(true, $retval);
95 | try {
96 | $data = $cache->get($key2);
97 | $this->assertEquals(234, $data);
98 | }
99 | catch (Cachearium\Exceptions\NotCachedException $e) {
100 | $this->fail();
101 | }
102 | $this->assertTrue($cache->delete($key2));
103 |
104 | // test null
105 | $retval = $cache->store(null, $key3);
106 | $this->assertEquals(true, $retval);
107 | try {
108 | $data = $cache->get($key3);
109 | $this->assertEquals(null, $data);
110 | }
111 | catch (Cachearium\Exceptions\NotCachedException $e) {
112 | $this->fail();
113 | }
114 | $this->assertTrue($cache->delete($key3));
115 |
116 | $this->assertArrayHasKey(CacheLogEnum::ACCESSED, $cache->getLogSummary());
117 | $this->assertGreaterThan(0, $cache->getLogSummary()[CacheLogEnum::ACCESSED]);
118 | }
119 |
120 | public function testSetGetCleanRAM() {
121 | $cache = CacheRAM::singleton();
122 | if ($cache->isEnabled()) {
123 | $this->setGetClean($cache);
124 | }
125 | }
126 |
127 | public function testSetGetCleanFS() {
128 | $cache = CacheFilesystem::singleton();
129 | if ($cache->isEnabled()) {
130 | $this->setGetClean($cache);
131 | }
132 | }
133 |
134 | public function testSetGetCleanMemcached() {
135 | $cache = CacheMemcached::singleton();
136 | if ($cache->isEnabled()) {
137 | $this->setGetClean($cache);
138 | }
139 | }
140 |
141 | private function getStoreData(CacheAbstract $cache) {
142 | $base = 'base';
143 |
144 | $this->assertTrue($cache->isEnabled());
145 | $cache->setDefaultLifetime(3600);
146 | $this->assertEquals(3600, $cache->getDefaultLifetime());
147 |
148 | // clean
149 | $key1 = new CacheKey($base, 1);
150 | $cache->clean($key1);
151 |
152 | // nothing there
153 | try {
154 | $data = $cache->get($key1);
155 | $this->fail();
156 | }
157 | catch (Cachearium\Exceptions\NotCachedException $e) {
158 | $this->assertTrue(true);
159 | }
160 |
161 | // store
162 | $cd = new CacheData($key1, 234);
163 | $retval = $cache->storeData($cd);
164 | $this->assertTrue($retval);
165 |
166 | // get
167 | try {
168 | $data = $cache->getData($key1);
169 | $this->assertInstanceOf('Cachearium\CacheData', $data);
170 | $this->assertEquals(234, $data->getFirstData());
171 | }
172 | catch (Cachearium\Exceptions\NotCachedException $e) {
173 | $this->fail();
174 | }
175 |
176 | // ivalid
177 | try {
178 | $data2 = $cache->getDataP("some", "random", "stuff");
179 | $this->fail();
180 | }
181 | catch (Cachearium\Exceptions\NotCachedException $e) {
182 | $this->assertTrue(true);
183 | }
184 |
185 | sleep(1);
186 |
187 | try {
188 | $data = $cache->getData($key1);
189 | $this->assertInstanceOf('Cachearium\CacheData', $data);
190 | $this->assertEquals(234, $data->getFirstData());
191 | }
192 | catch (Cachearium\Exceptions\NotCachedException $e) {
193 | $this->fail();
194 | }
195 |
196 | // clean
197 | $cache->clean($key1);
198 | try {
199 | $data = $cache->getData($key1);
200 | $this->fail();
201 | }
202 | catch (Cachearium\Exceptions\NotCachedException $e) {
203 | $this->assertTrue(true);
204 | }
205 |
206 | // check conflicts
207 | $key2 = new CacheKey($base, 2, 'a');
208 | $key3 = new CacheKey($base, 3, 'a');
209 | // now change again and delete
210 | $retval = $cache->storeData(new CacheData($key2, 234));
211 | $this->assertEquals(true, $retval);
212 | try {
213 | $data = $cache->getData($key2);
214 | $this->assertInstanceOf('Cachearium\CacheData', $data);
215 | $this->assertEquals(234, $data->getFirstData());
216 | }
217 | catch (Cachearium\Exceptions\NotCachedException $e) {
218 | $this->fail();
219 | }
220 | $this->assertTrue($cache->delete($key2));
221 |
222 | // test null
223 | $retval = $cache->storeData(new CacheData($key3), null);
224 | $this->assertEquals(true, $retval);
225 | try {
226 | $data = $cache->getData($key3);
227 | $this->assertInstanceOf('Cachearium\CacheData', $data);
228 | $this->assertEquals(null, $data->getFirstData());
229 | }
230 | catch (Cachearium\Exceptions\NotCachedException $e) {
231 | $this->fail();
232 | }
233 | $this->assertTrue($cache->delete($key3));
234 | }
235 |
236 | public function testgetStoreDataRAM() {
237 | $cache = CacheRAM::singleton();
238 | if ($cache->isEnabled()) {
239 | $this->getStoreData($cache);
240 | }
241 | }
242 |
243 | public function testgetStoreDataMemcached() {
244 | $cache = CacheMemcached::singleton();
245 | if ($cache->isEnabled()) {
246 | $this->getStoreData($cache);
247 | }
248 | }
249 |
250 | public function testgetStoreDataFS() {
251 | $cache = CacheFilesystem::singleton();
252 | if ($cache->isEnabled()) {
253 | $this->getStoreData($cache);
254 | }
255 | }
256 |
257 | private function dependency(CacheAbstract $cache) {
258 | // store
259 | $key1 = new CacheKey('Namespace', 'Subname');
260 | $cd = new CacheData($key1, 'xxxx');
261 | $depkey = new CacheKey('Namespace', 'SomeDep');
262 | $cd->addDependency($depkey);
263 | $cache->storeData($cd);
264 |
265 | // check if it is cached
266 | try {
267 | $data = $cache->getData($key1);
268 | $this->assertInstanceOf('Cachearium\CacheData', $data);
269 | $this->assertEquals('xxxx', $data->getFirstData());
270 | }
271 | catch (Cachearium\Exceptions\NotCachedException $e) {
272 | $this->fail();
273 | }
274 |
275 | // invalidate a dependency
276 | $cache->invalidate($depkey);
277 |
278 | // get the original and it should be uncached
279 | try {
280 | $data = $cache->getData($key1);
281 | $this->fail();
282 | }
283 | catch (Cachearium\Exceptions\NotCachedException $e) {
284 | $this->assertTrue(true);
285 | }
286 | }
287 |
288 | public function testdependencyRAM() {
289 | $cache = CacheRAM::singleton();
290 | if ($cache->isEnabled()) {
291 | $this->dependency($cache);
292 | }
293 | }
294 |
295 | public function testdependencyMemcached() {
296 | $cache = CacheMemcached::singleton();
297 | if ($cache->isEnabled()) {
298 | $this->dependency($cache);
299 | }
300 | }
301 |
302 | public function testdependencyFS() {
303 | $this->markTestSkipped();
304 | $cache = CacheFilesystem::singleton();
305 | if ($cache->isEnabled()) {
306 | $this->dependency($cache);
307 | }
308 | }
309 |
310 | public function clear(CacheAbstract $cache) {
311 | $key = new CacheKey('clear', 'it');
312 | $cd = new CacheData($key, 789);
313 | $this->assertTrue($cache->storeData($cd));
314 | $cache->clear();
315 | try {
316 | $cache->get($key);
317 | $this->fail();
318 | }
319 | catch (Cachearium\Exceptions\NotCachedException $e) {
320 | $this->assertTrue(true);
321 | }
322 | }
323 |
324 | public function testClearRAM() {
325 | $cache = CacheRAM::singleton();
326 | if ($cache->isEnabled()) {
327 | $this->clear($cache);
328 | }
329 | }
330 |
331 | public function testClearMemcached() {
332 | $cache = CacheMemcached::singleton();
333 | if ($cache->isEnabled()) {
334 | $this->clear($cache);
335 | }
336 | }
337 |
338 | public function testClearFS() {
339 | $cache = CacheFilesystem::singleton();
340 | if ($cache->isEnabled()) {
341 | $this->clear($cache);
342 | }
343 | }
344 | public function increment(CacheAbstract $cache) {
345 | $key = new CacheKey('increment', 'it');
346 | $cache->delete($key);
347 | $this->assertEquals(5, $cache->increment(1, $key, 5));
348 | $this->assertEquals(6, $cache->increment(1, $key, 5));
349 | }
350 |
351 | public function testIncrementRAM() {
352 | $cache = CacheRAM::singleton();
353 | if ($cache->isEnabled()) {
354 | $this->increment($cache);
355 | }
356 | }
357 |
358 | public function testIncrementMemcached() {
359 | $cache = CacheMemcached::singleton();
360 | if ($cache->isEnabled()) {
361 | $this->increment($cache);
362 | }
363 | }
364 |
365 | public function testIncrementFS() {
366 | $cache = CacheFilesystem::singleton();
367 | if ($cache->isEnabled()) {
368 | try {
369 | $this->increment($cache);
370 | $this->fail();
371 | }
372 | catch (Cachearium\Exceptions\CacheUnsupportedOperation $e) {
373 | $this->assertTrue(true);
374 | }
375 | }
376 | }
377 |
378 | public function testClearAll() {
379 | $cacheKey = new CacheKey('test', 1);
380 | $cacheData = new CacheData($cacheKey, 'test');
381 |
382 | $caches = [CacheRAM::singleton(), CacheFilesystem::singleton()];
383 | if (CacheMemcached::hasMemcachedExt()) {
384 | $caches[] = CacheMemcached::singleton();
385 | }
386 | foreach($caches as $cacheInst) {
387 | $cacheInst->enable()->storeData($cacheData);
388 | $retrievedData = $cacheInst->getData($cacheKey);
389 | $this->assertEquals($cacheData->getFirstData(), $retrievedData->getFirstData());
390 | }
391 |
392 | CacheAbstract::clearAll();
393 |
394 | foreach($caches as $cacheInst) {
395 | try {
396 | $retrievedData = $cacheInst->getData($cacheKey);
397 | $this->fail('Cache should be empty after a clearAll call !');
398 | }
399 | catch(\Cachearium\Exceptions\NotCachedException $e) {
400 | $this->assertTrue(true, 'All cache was cleaned');
401 | }
402 | }
403 | }
404 | }
405 |
--------------------------------------------------------------------------------
/test/CacheCallbackTest.php:
--------------------------------------------------------------------------------
1 | addServers([['localhost', 11211]]); // init server
26 | CacheAbstract::clearAll();
27 | }
28 |
29 | protected function _callback(CacheAbstract $cache) {
30 | $base = 'callback';
31 |
32 | $key1 = new CacheKey($base, 1);
33 | $cache->clean($key1);
34 |
35 | $this->assertEquals(CALLBACKVALUE, $cache->startCallback($key1, 'callbackTester'));
36 |
37 | try {
38 | $data = $cache->getData($key1);
39 | $this->assertEquals(CALLBACKVALUE, $data->stringify($cache));
40 | }
41 | catch (Cachearium\Exceptions\NotCachedException $e) {
42 | $this->fail();
43 | }
44 | }
45 |
46 | public function testcallbackRAM() {
47 | $cache = CacheRAM::singleton();
48 | if ($cache->isEnabled()) {
49 | $this->_callback($cache);
50 | }
51 | }
52 |
53 | public function testcallbackMemcached() {
54 | $cache = CacheMemcached::singleton();
55 | if ($cache->isEnabled()) {
56 | $this->_callback($cache);
57 | }
58 | }
59 |
60 | public function testcallbackFS() {
61 | $cache = CacheFilesystem::singleton();
62 | if ($cache->isEnabled()) {
63 | $this->_callback($cache);
64 | }
65 | }
66 |
67 | public function testCacheError() {
68 | $cache = CacheRAM::singleton();
69 | $this->assertFalse($cache->appendCallback('callbackTesterStart'));
70 | }
71 |
72 | protected function _startcallback(CacheAbstract $cache) {
73 | $key = new CacheKey("startcallback", 1);
74 | $cache->clean($key);
75 |
76 | $this->assertFalse($cache->start($key));
77 | echo "something ";
78 | $this->assertTrue($cache->appendCallback('callbackTesterStart'));
79 | echo " otherthing";
80 | $output = $cache->end(false);
81 |
82 | $this->assertContains(CALLBACKVALUE, $output);
83 |
84 | // run again, we should have another value
85 | $second = $cache->start($key, null, false);
86 | $this->assertNotFalse($second);
87 | $this->assertContains(CALLBACKVALUE, $second);
88 | $this->assertNotEquals($second, $output);
89 | }
90 |
91 | public function teststartCallbackRAM() {
92 | $cache = CacheRAM::singleton();
93 | if ($cache->isEnabled()) {
94 | $this->_startcallback($cache);
95 | }
96 | }
97 |
98 | public function teststartCallbackMemcached() {
99 | $cache = CacheMemcached::singleton();
100 | if ($cache->isEnabled()) {
101 | $this->_startcallback($cache);
102 | }
103 | }
104 |
105 | public function teststartCallbackFS() {
106 | $cache = CacheFilesystem::singleton();
107 | if ($cache->isEnabled()) {
108 | $this->_startcallback($cache);
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/test/CacheDataTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($cd->key, $ck);
25 |
26 | $ck2 = new CacheKey('base', 'id', 'sub2');
27 | $cd->setKey($ck2);
28 | $this->assertEquals($cd->key, $ck2);
29 | }
30 |
31 | public function testMultiData() {
32 | $ck1 = new CacheKey('data', 1, 'sub');
33 | $ck2 = new CacheKey('data', 2, 'sub');
34 | $ck3 = new CacheKey('data', 3, 'sub');
35 | $this->assertNotNull($ck1);
36 | $this->assertNotNull($ck2);
37 | $this->assertNotNull($ck3);
38 | $cd2 = new CacheData($ck2, 'is');
39 | $cd3 = new CacheData($ck3, 'data');
40 | $this->assertNotNull($cd2);
41 | $this->assertNotNull($cd3);
42 | $this->markTestIncomplete();
43 | }
44 |
45 | public function testCallback() {
46 | $cache = Cachearium\Backend\CacheRAM::singleton();
47 | $ck1 = new CacheKey('callback', 1, 'sub');
48 | $ck2 = new CacheKey('callback', 2, 'sub');
49 | $cd1 = new CacheData($ck1, null);
50 | $cd2 = new CacheData($ck2, null);
51 |
52 | $cd2->appendCallback('callbackDataTester');
53 | $cd1->appendData('something');
54 | $cd1->appendRecursionData($cd2);
55 | $this->assertTrue($cache->storeData($cd2));
56 | $this->assertTrue($cache->storeData($cd1));
57 | $this->assertNotFalse($cache->getData($ck2));
58 | $this->assertNotFalse($cache->getData($ck1));
59 |
60 | $this->assertEquals('something' . CALLBACKDATATESTERVALUE, $cd1->stringify($cache));
61 | }
62 |
63 | public function testDependencies() {
64 | $cache = Cachearium\Backend\CacheRAM::singleton();
65 |
66 | $ck1 = new CacheKey('recursion', 1, 'sub');
67 | $ck2 = new CacheKey('recursion', 2, 'sub');
68 | $ck3 = new CacheKey('recursion', 3, 'sub');
69 | $cd1 = new CacheData($ck1, 'this');
70 | $cd2 = new CacheData($ck2, 'is');
71 | $cd3 = new CacheData($ck3, 'recursion');
72 | $cd2->appendRecursionData($cd3);
73 | $cd1->appendRecursionData($cd2);
74 | $this->assertTrue($cache->storeData($cd3));
75 | $this->assertTrue($cache->storeData($cd2));
76 | $this->assertTrue($cache->storeData($cd1));
77 | $this->assertNotFalse($cache->getData($ck1));
78 | $this->assertNotFalse($cache->getData($ck2));
79 | $this->assertNotFalse($cache->getData($ck3));
80 |
81 | $this->assertEquals('thisisrecursion', $cd1->stringify($cache));
82 |
83 | $cd2 = new CacheData($ck2, 'breaks');
84 | $this->assertTrue($cache->storeData($cd2));
85 | try {
86 | $cache->getData($ck1);
87 | $this->fail();
88 | }
89 | catch (Cachearium\Exceptions\NotCachedException $e) {
90 | $this->assertTrue(true);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/test/CacheInterfaceTest.php:
--------------------------------------------------------------------------------
1 | cacheClean();
22 |
23 | $k = $c->getCacheKey();
24 | $this->assertInstanceOf('Cachearium\CacheKey', $k);
25 | $this->assertEquals('MockCachedClass', $k->getBase());
26 | $this->assertEquals(1, $k->getId());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test/CacheKeyTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('base', $ck->getBase());
11 | $this->assertEquals('id', $ck->getId());
12 | $this->assertEquals('sub', $ck->getSub());
13 |
14 | $ck = new CacheKey(null, null);
15 | $ck->setBase('base')
16 | ->setId('id')
17 | ->setSub('sub');
18 | $this->assertEquals('base', $ck->getBase());
19 | $this->assertEquals('id', $ck->getId());
20 | $this->assertEquals('sub', $ck->getSub());
21 | $this->assertNotNull($ck->getHash());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test/CacheLogEnumTest.php:
--------------------------------------------------------------------------------
1 | assertGreaterThan(0, count(Cachearium\CacheLogEnum::getNames()));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/CacheMemcachedTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('The Memcached extension is not available.');
13 | }
14 | // ob_start();
15 | }
16 |
17 | public function testNamespace() {
18 | $cache = CacheMemcached::singleton();
19 | $this->assertEquals($cache, $cache->setNamespace("testmem"));
20 | $this->assertEquals("testmem", $cache->getNamespace());
21 |
22 | $key = new CacheKey('namespace', 1);
23 | $cache->store(333, $key);
24 | try {
25 | $data = $cache->get($key);
26 | }
27 | catch(Cachearium\Exceptions\NotCachedException $e) {
28 | $this->fail();
29 | }
30 |
31 | $this->assertEquals($cache, $cache->setNamespace("other"));
32 | try {
33 | $data = $cache->get($key);
34 | $this->fail();
35 | }
36 | catch(Cachearium\Exceptions\NotCachedException $e) {
37 | $this->assertTrue(true);
38 | }
39 |
40 | }
41 | }
--------------------------------------------------------------------------------
/test/CacheNullTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf('Cachearium\Backend\CacheNull', $cache);
16 |
17 | $cache = CacheAbstract::factory('Null');
18 | $this->assertInstanceOf('Cachearium\Backend\CacheNull', $cache);
19 |
20 | try {
21 | $cache->get($key);
22 | $this->assertTrue(false);
23 | }
24 | catch (Cachearium\Exceptions\NotCachedException $e) {
25 | $this->assertTrue(true);
26 | }
27 |
28 | $this->assertEquals(5, $cache->increment(1, $key, 5));
29 | $this->assertTrue($cache->store(10, $key));
30 | $this->assertTrue($cache->delete($key));
31 | $this->assertTrue($cache->clean($key));
32 | $this->assertTrue($cache->clear());
33 | $this->assertFalse($cache->start($key));
34 | $cache->end();
35 | $cache->prefetch(array());
36 | $cache->enable(true);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/CacheRamTest.php:
--------------------------------------------------------------------------------
1 | start($key)) {
23 | $this->assertTrue(true);
24 | echo $expected;
25 | }
26 | else {
27 | $this->assertTrue(false);
28 | }
29 | $data = $cache->end(false);
30 |
31 | // check if we have the correct data
32 | $this->assertEquals($expected, $data);
33 |
34 | // now it should be cached
35 | $this->assertEquals($expected, $cache->start($key, null, false));
36 | }
37 |
38 | public function testStartBasicRAM() {
39 | $cache = CacheRAM::singleton();
40 | if ($cache->isEnabled()) {
41 | $this->_startBasic($cache);
42 | }
43 | }
44 |
45 | private function _startMultiLevel(CacheAbstract $cache) {
46 | $expected = ["first level", "second level 1", "second level 2", "second level 3"];
47 | $i = 0;
48 | if (!$cache->start(new CacheKey('startmultilevel', 'first', 0), null, false)) {
49 | $this->assertTrue(true);
50 | echo $expected[$i++];
51 | if (!$cache->start(new CacheKey('startmultilevel', 'second', $i), null, false)) {
52 | echo $expected[$i++];
53 | $cache->end();
54 | }
55 | if (!$cache->start(new CacheKey('startmultilevel', 'second', $i), null, false)) {
56 | echo $expected[$i++];
57 | $cache->end();
58 | }
59 | if (!$cache->start(new CacheKey('startmultilevel', 'second', $i), null, false)) {
60 | echo $expected[$i++];
61 | $cache->end();
62 | }
63 | }
64 | else {
65 | $this->assertTrue(false);
66 | }
67 | $data = $cache->end(false);
68 | $this->assertEquals(implode('', $expected), $data);
69 | }
70 |
71 | public function testStartMultiLevel() {
72 | $cache = CacheRAM::singleton();
73 | if ($cache->isEnabled()) {
74 | $this->_startMultiLevel($cache);
75 | }
76 | }
77 |
78 | private function _startNested(CacheAbstract $cache) {
79 | $expected = ["first level", "second level 1", "second level 2", "second level 3"];
80 | $i = 0;
81 | $data = '';
82 | if (!($data = $cache->start(new CacheKey('startnested', 'first', 0), null, false))) {
83 | $this->assertTrue(true);
84 | echo $expected[$i++];
85 | if (!$cache->start(new CacheKey('startnested', 'second', $i), null, false)) {
86 | echo $expected[$i++];
87 | if (!$cache->start(new CacheKey('startnested', 'second', $i), null, false)) {
88 | echo $expected[$i++];
89 | if (!$cache->start(new CacheKey('startnested', 'second', $i), null, false)) {
90 | echo $expected[$i++];
91 | $cache->end();
92 | }
93 | $cache->end();
94 | }
95 | $cache->end();
96 | }
97 | $data = $cache->end(false);
98 | }
99 | else {
100 | $this->assertTrue(false);
101 | }
102 | $this->assertEquals(implode('', $expected), $data);
103 | }
104 |
105 | public function testStartNested() {
106 | $cache = CacheRAM::singleton();
107 | if ($cache->isEnabled()) {
108 | $this->_startNested($cache);
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/test/CacheTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('The Memcached extension is not available.');
47 | }
48 | // ob_start();
49 | }
50 |
51 | protected function tearDown() {
52 | // ob_end_clean();
53 | }
54 |
55 | private function setGetClean(CacheAbstract $cache) {
56 | $base = 'base';
57 |
58 | $this->assertTrue($cache->isEnabled());
59 | $cache->setDefaultLifetime(3600);
60 | $this->assertEquals(3600, $cache->getDefaultLifetime());
61 | $cache->cleanP($base, 1);
62 |
63 | try {
64 | $data = $cache->getP($base, 1);
65 | $this->fail();
66 | }
67 | catch(Cachearium\Exceptions\NotCachedException $e) {
68 | $this->assertTrue(true);
69 | }
70 |
71 | $retval = $cache->storeP(234, $base, 1);
72 | $this->assertTrue($retval);
73 |
74 | try {
75 | $data = $cache->getP($base, 1);
76 | $this->assertEquals(234, $data);
77 | }
78 | catch(Cachearium\Exceptions\NotCachedException $e) {
79 | $this->fail();
80 | }
81 |
82 | sleep(1);
83 |
84 | try {
85 | $data = $cache->getP($base, 1);
86 | $this->assertEquals(234, $data);
87 | }
88 | catch(Cachearium\Exceptions\NotCachedException $e) {
89 | $this->fail();
90 | }
91 |
92 | $cache->cleanP($base, 1);
93 | try {
94 | $data = $cache->getP($base, 1);
95 | $this->fail();
96 | }
97 | catch(Cachearium\Exceptions\NotCachedException $e) {
98 | $this->assertTrue(true);
99 | }
100 |
101 | // now change again and delete
102 | $retval = $cache->storeP(234, $base, 2, 'a');
103 | $this->assertEquals(true, $retval);
104 | try {
105 | $data = $cache->getP($base, 2, 'a');
106 | $this->assertEquals(234, $data);
107 | }
108 | catch(Cachearium\Exceptions\NotCachedException $e) {
109 | $this->fail();
110 | }
111 | $this->assertTrue($cache->deleteP($base, 2, 'a'));
112 |
113 | // test null
114 | $retval = $cache->storeP(null, $base, 3, 'a');
115 | $this->assertEquals(true, $retval);
116 | try {
117 | $data = $cache->getP($base, 3, 'a');
118 | $this->assertEquals(null, $data);
119 | }
120 | catch(Cachearium\Exceptions\NotCachedException $e) {
121 | $this->fail();
122 | }
123 | $this->assertTrue($cache->deleteP($base, 3, 'a'));
124 | }
125 |
126 | public function testSetGetCleanRAM() {
127 | $cache = CacheRAM::singleton();
128 | $this->setGetClean($cache);
129 | }
130 |
131 | public function testSetGetCleanFS() {
132 | $cache = CacheFilesystem::singleton();
133 | $this->setGetClean($cache);
134 | }
135 |
136 | public function testSetGetCleanMem() {
137 | if (self::$testMemoryCache) {
138 | $cache = CacheMemcached::singleton([['localhost', 11211]]);
139 | $this->setGetClean($cache);
140 | }
141 | }
142 |
143 | public function testPrefetch() {
144 | if (!self::$testMemoryCache) {
145 | return;
146 | }
147 |
148 | $key = new CacheKey('baseprefetch', 'idprefetch', 'subprefetch');
149 | $data = '92348ijasd2q3r';
150 |
151 | $cache = CacheMemcached::singleton([['localhost', 11211]]);
152 |
153 | // save.
154 | $retval = $cache->store($data, $key);
155 | $this->assertEquals(true, $retval);
156 |
157 | // get. We should have a local cache.
158 | $startfetches = $cache->getFetches();
159 | $data2 = $cache->get($key);
160 | $this->assertEquals($data, $data2);
161 | $this->assertEquals($startfetches, $cache->getFetches());
162 |
163 | // now clear the cache. and prefetch
164 | $cache->prefetchClear();
165 | $cache->prefetch(
166 | array($key)
167 | );
168 |
169 | $this->markTestSkipped();
170 | // get. We should have a local cache.
171 | $startfetches = $cache->getFetches();
172 | $data2 = $cache->get($key);
173 | $this->assertEquals($data, $data2);
174 | $this->assertEquals($startfetches, $cache->getFetches());
175 | }
176 |
177 | private function startEnd(CacheAbstract $cache) {
178 | $base = 'startend';
179 | $cache->cleanP($base, 1);
180 |
181 | $this->assertFalse($cache->recursiveStartP($base, 1));
182 | echo 'start!';
183 | $cache->recursiveend(false);
184 |
185 | ob_start();
186 | ob_implicit_flush(false);
187 | $this->assertTrue(($cache->recursiveStartP($base, 1) !== false));
188 | $data = ob_get_contents();
189 | ob_end_clean();
190 |
191 | $this->assertEquals('start!', $data);
192 | $cache->cleanP($base, 1);
193 | }
194 |
195 | public function testStartEndRAM() {
196 | $cache = CacheRAM::singleton();
197 | $this->startEnd($cache);
198 | }
199 |
200 | public function testStartEndFS() {
201 | $cache = CacheFilesystem::singleton();
202 | $this->startEnd($cache);
203 | }
204 |
205 | public function testStartEndMem() {
206 | if (self::$testMemoryCache) {
207 | $cache = CacheMemcached::singleton([['localhost', 11211]]);
208 | $this->startEnd($cache);
209 | }
210 | }
211 |
212 | private function serialize(CacheAbstract $cache) {
213 | $base = 'serialize';
214 |
215 | $cache->cleanP($base, 1);
216 |
217 | $data = array('awer' => 132,
218 | array('awerawer' => 23423,
219 | 'cvbxxcv' => 234,
220 | )
221 | );
222 | $retval = $cache->storeP($data, $base, 1);
223 | $this->assertEquals(true, $retval);
224 |
225 | $data2 = $cache->getP($base, 1);
226 | $this->assertEquals($data2, $data);
227 | }
228 |
229 | public function testSerializeRAM() {
230 | $cache = CacheRAM::singleton();
231 | $this->serialize($cache);
232 | }
233 |
234 | public function testSerializeFS() {
235 | $cache = CacheFilesystem::singleton();
236 | $this->serialize($cache);
237 | }
238 |
239 | public function testSerializeMem() {
240 | if (self::$testMemoryCache) {
241 | $cache = CacheMemcached::singleton([['localhost', 11211]]);
242 | $this->serialize($cache);
243 | }
244 | }
245 |
246 | private function setBigClean(CacheAbstract $cache) {
247 | $id = '2';
248 | $base = 'bigclean';
249 | $otherid = '3';
250 |
251 | $retval = $cache->storeP(111, $base, $id, 'a');
252 | $this->assertEquals(true, $retval);
253 | $retval = $cache->storeP(222, $base, $id, 'b');
254 | $this->assertEquals(true, $retval);
255 | $retval = $cache->storeP(333, $base, $otherid, 'a');
256 | $this->assertEquals(true, $retval);
257 |
258 | $data = $cache->getP($base, $id, 'a');
259 | $this->assertEquals(111, $data);
260 | $data = $cache->getP($base, $id, 'b');
261 | $this->assertEquals(222, $data);
262 | $data = $cache->getP($base, $otherid, 'a');
263 | $this->assertEquals(333, $data);
264 |
265 | $cache->cleanP($base, $id);
266 |
267 | try {
268 | $data = $cache->getP($base, $id, 'a');
269 | $this->fail();
270 | }
271 | catch(Cachearium\Exceptions\NotCachedException $e) {
272 | $this->assertTrue(true);
273 | }
274 | try {
275 | $data = $cache->getP($base, $otherid, 'a');
276 | $this->assertEquals(333, $data);
277 | }
278 | catch(Cachearium\Exceptions\NotCachedException $e) {
279 | $this->fail();
280 | }
281 |
282 | try {
283 | $data = $cache->getP($base, $id, 'b');
284 | $this->fail();
285 | }
286 | catch(Cachearium\Exceptions\NotCachedException $e) {
287 | $this->assertTrue(true);
288 | }
289 | }
290 |
291 | private function setBigClear(CacheAbstract $cache) {
292 | $id = '2';
293 | $base = 'bigclean';
294 | $otherid = '3';
295 | $otherbase = 'bigfoo';
296 |
297 | $retval = $cache->storeP(111, $base, $id, 'a');
298 | $this->assertEquals(true, $retval);
299 | $retval = $cache->storeP(222, $base, $id, 'b');
300 | $this->assertEquals(true, $retval);
301 | $retval = $cache->storeP(333, $base, $otherid, 'a');
302 | $this->assertEquals(true, $retval);
303 | $retval = $cache->storeP(444, $otherbase, $otherid, 'a');
304 | $this->assertEquals(true, $retval);
305 |
306 | $data = $cache->getP($base, $id, 'a');
307 | $this->assertEquals(111, $data);
308 | $data = $cache->getP($base, $id, 'b');
309 | $this->assertEquals(222, $data);
310 | $data = $cache->getP($base, $otherid, 'a');
311 | $this->assertEquals(333, $data);
312 | $data = $cache->getP($otherbase, $otherid, 'a');
313 | $this->assertEquals(444, $data);
314 |
315 | $cache->clear();
316 |
317 | try {
318 | $data = $cache->getP($base, $id, 'a');
319 | $this->fail();
320 | }
321 | catch(Cachearium\Exceptions\NotCachedException $e) {
322 | $this->assertTrue(true);
323 | }
324 | try {
325 | $data = $cache->getP($base, $id, 'b');
326 | $this->fail();
327 | }
328 | catch(Cachearium\Exceptions\NotCachedException $e) {
329 | $this->assertTrue(true);
330 | }
331 | try {
332 | $data = $cache->getP($base, $otherid, 'a');
333 | $this->fail();
334 | }
335 | catch(Cachearium\Exceptions\NotCachedException $e) {
336 | $this->assertTrue(true);
337 | }
338 | try {
339 | $data = $cache->getP($otherbase, $otherid, 'a');
340 | $this->fail();
341 | }
342 | catch(Cachearium\Exceptions\NotCachedException $e) {
343 | $this->assertTrue(true);
344 | }
345 | }
346 |
347 | public function testCleanRAM() {
348 | $cache = CacheRAM::singleton();
349 | $this->setBigClean($cache);
350 | $this->setBigClear($cache);
351 | }
352 |
353 | public function testCleanFS() {
354 | $cache = CacheFilesystem::singleton();
355 | $this->setBigClean($cache);
356 | $this->setBigClear($cache);
357 | }
358 |
359 | public function testCleanMem() {
360 | if (self::$testMemoryCache) {
361 | $cache = CacheMemcached::singleton([['localhost', 11211]]);
362 | $this->setBigClean($cache);
363 | $this->setBigClear($cache);
364 | }
365 | }
366 |
367 | private function dependencies(CacheAbstract $cache) {
368 | $this->assertFalse($cache->appendCallback('cacheCallback')); // not in loop
369 |
370 | $cache->recursivestart('parent', 1);
371 | echo 'parent start/';
372 | $this->assertFalse($cache->appendCallback('nawreanaweroi')); // invalid
373 | $cache->appendCallback('cacheCallback');
374 | $cache->recursivestart('child', 2);
375 | echo '|child first|';
376 | $cache->recursiveend(false);
377 | echo 'parent end/';
378 | $cache->recursiveend(false);
379 |
380 | $data = $cache->get('child', 2);
381 | $cachedata = CacheData::unserialize($data);
382 | $this->assertEquals('|child first|', $cachedata->stringify($cache));
383 | $data = $cache->get('parent', 1);
384 | $cachedata = CacheData::unserialize($data);
385 | $this->assertEquals('parent start/aaaa|child first|parent end/', $cachedata->stringify($cache));
386 |
387 | $cache->delete('child', 2);
388 | $cache->recursivestart('child', 2);
389 | echo '|child second|';
390 | $cache->recursiveend(false);
391 |
392 | $data = $cache->get('child', 2);
393 | $cachedata = CacheData::unserialize($data);
394 | $this->assertEquals('|child second|', $cachedata->stringify($cache));
395 | $data = $cache->get('parent', 1);
396 | $cachedata = CacheData::unserialize($data);
397 | $this->assertEquals('parent start/bbbb|child second|parent end/', $cachedata->stringify($cache));
398 | }
399 |
400 | public function testDependenciesRAM() {
401 | $this->markTestIncomplete();
402 | return;
403 | $cache = CacheRAM::singleton();
404 | $this->dependencies($cache);
405 | }
406 |
407 | public function _dependencies(CacheAbstract $cache) {
408 | $cache->start('parent', 1);
409 | $cache->addDependency();
410 | echo 'parent start/';
411 | $this->assertFalse($cache->appendCallback('nawreanaweroi')); // invalid
412 | $cache->appendCallback('cacheCallback');
413 | $cache->start('child', 2);
414 | echo '|child first|';
415 | $cache->end(false);
416 | echo 'parent end/';
417 | $cache->end(false);
418 | }
419 |
420 | public function testClearRAM() {
421 | $cache = CacheRAM::singleton();
422 | $this->setBigClean($cache);
423 | }
424 |
425 | public function testClearFS() {
426 | $cache = CacheFilesystem::singleton();
427 | $this->setBigClean($cache);
428 | }
429 |
430 | public function testClearMem() {
431 | if (self::$testMemoryCache) {
432 | $cache = CacheMemcached::singleton([['localhost', 11211]]);
433 | $this->setBigClean($cache);
434 | }
435 | }
436 |
437 | public function testClash() {
438 | $cache = CacheMemcached::singleton([['localhost', 11211]]);
439 |
440 | try {
441 | ob_start();
442 | if (!$cache->startP('testClash', 0)) {
443 | if (!$cache->startP('testClash', 0)) {
444 | $cache->end();
445 | }
446 | $cache->end();
447 | }
448 | $this->assertFalse(true);
449 | }
450 | catch(Cachearium\Exceptions\CacheKeyClashException $e) {
451 | $this->assertTrue(true);
452 | }
453 | }
454 | }
455 |
--------------------------------------------------------------------------------