Completed
Push — master ( 45756a...26da3a )
by Marco
03:41
created

CacheManager::getRandomCache()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 45
Code Lines 19

Duplication

Lines 23
Ratio 51.11 %

Code Coverage

Tests 15
CRAP Score 5.2331

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 23
loc 45
ccs 15
cts 19
cp 0.7895
rs 8.439
cc 5
eloc 19
nc 8
nop 2
crap 5.2331
1
<?php namespace Comodojo\Cache;
2
3
use \Comodojo\Cache\CacheInterface\CacheInterface;
4
use \Comodojo\Cache\CacheObject\CacheObject;
5
use \Comodojo\Exception\CacheException;
6
7
/**
8
 * Cache manager
9
 * 
10
 * @package     Comodojo Spare Parts
11
 * @author      Marco Giovinazzi <[email protected]>
12
 * @license     MIT
13
 *
14
 * LICENSE:
15
 * 
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
25
class CacheManager {
26
27
    const PICK_FIRST   = 1;
28
    const PICK_LAST    = 2;
29
    const PICK_RANDOM  = 3;
30
    const PICK_BYWEIGHT = 4;
31
    const PICK_ALL     = 5;
32
33
    private $caches = array();
34
35
    private $cache_weights = array();
36
37
    private $selector = null;
38
39
    private $selected_cache = null;
40
41
    /**
42
     * Determine the current cache scope (default: GLOBAL)
43
     *
44
     * @var string
45
     */
46
    protected $namespace = "GLOBAL";
47
48
    /**
49
     * current time (in sec)
50
     *
51
     * @var int
52
     */
53
    protected $current_time = null;
54
    
55
    /**
56
     * Current instance of \Monolog\Logger
57
     *
58
     * @var \Monolog\Logger
59
     */
60
    protected $logger = null;
61
    
62
    /**
63
     * Cache ttl
64
     *
65
     * @var int
66
     */
67
    protected $ttl = null;
68
69 130
    public function __construct($select_mode = null, \Monolog\Logger $logger = null) {
70
71 130
        $this->selector = filter_var($select_mode, FILTER_VALIDATE_INT, array(
72
            'options' => array(
73 90
                'min_range' => 1, 
74 90
                'max_range' => 4,
75
                'default'   => 3
76 90
            )
77 130
        ));
78
79
        try {
80
            
81 130
            $this->setTime();
82
            
83 130
            $this->setTtl();
84
85 130
            if ( !is_null($logger) ) $this->setLogger($logger);
86
            
87 90
        } catch (CacheException $ce) {
88
            
89
            throw $ce;
90
            
91
        }
92
93 130
    }
94
95
    /**
96
     * Get current time
97
     *
98
     * @return float
99
     */
100 130
    final public function getTime() {
101
        
102 130
        return $this->current_time;
103
        
104
    }
105
106
    /**
107
     * Get current ttl
108
     *
109
     * @return int
110
     */
111 130
    final public function getTtl() {
112
        
113 130
        return $this->ttl;
114
        
115
    }
116
117
    /**
118
     * Get current namespace
119
     *
120
     * @return int
121
     */
122
    final public function getNamespace() {
123
124
        return $this->namespace;
125
126
    }
127
128
    /**
129
     * Get current logger
130
     *
131
     * @return \Monolog\Logger
132
     */
133
    final public function getLogger() {
134
        
135
        return $this->logger;
136
        
137
    }
138
139
    public function raiseError($message, $parameters = array()) {
140
141
        if ( $this->logger instanceof \Monolog\Logger ) $this->logger->addError($message, $parameters);
142
143
    }
144
145
    /**
146
     * Set current time
147
     *
148
     * @param   int    $time    Set current time (in msec - float)
149
     * 
150
     * @return  \Comodojo\Cache\CacheObject\CacheObject
151
     * @throws  \Comodojo\Exception\CacheException
152
     */
153 130 View Code Duplication
    final public function setTime($time = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
154
        
155 130
        if ( is_null($time) ) $this->current_time = time();
156
157
        else if ( preg_match('/^[0-9]{10}$/', $time) ) $this->current_time = $time;
158
        
159
        else {
160
            
161
            throw new CacheException("Invalid time");
162
            
163
        }
164
165 130
        foreach ( $this->caches as $cache ) $cache->setTime($time);
166
167 130
        return $this;
168
        
169
    }
170
171
    /**
172
     * Set time to live for cache
173
     *
174
     * @param    int    $ttl    Time to live (in secs)
175
     * 
176
     * @return \Comodojo\Cache\CacheObject\CacheObject
177
     * @throws \Comodojo\Exception\CacheException
178
     */
179 130 View Code Duplication
    final public function setTtl($ttl = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
        
181 130
        if ( is_null($ttl) ) {
182
            
183 130
            $this->ttl = defined('COMODOJO_CACHE_DEFAULT_TTL') ? COMODOJO_CACHE_DEFAULT_TTL : 3600;
184
            
185 90
        } else if ( is_int($ttl) ) {
186
            
187
            $this->ttl = $ttl;
188
            
189
        } else {
190
191
            throw new CacheException("Invalid time to live");
192
            
193
        }
194
195 130
        foreach ( $this->caches as $cache ) $cache->setTtl($ttl);
196
197 130
        return $this;
198
        
199
    }
200
201
    /**
202
     * Set namespace for cache
203
     *
204
     * @param    string    $namespace    Selected namespace (64 chars limited)
205
     * 
206
     * @return \Comodojo\Cache\CacheObject\CacheObject
207
     * @throws \Comodojo\Exception\CacheException
208
     */
209 15
    final public function setNamespace($namespace) {
210
211 15 View Code Duplication
        if ( preg_match('/^[0-9a-zA-Z]+$/', $namespace) && strlen($namespace) <= 64 ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
212
            
213 15
            $this->namespace = strtoupper($namespace);
214
            
215 10
        } else {
216
            
217
            throw new CacheException("Invalid namespace");
218
            
219
        }
220
221 15
        foreach ( $this->caches as $cache ) $cache->setNamespace($namespace);
222
223 15
        return $this;
224
225
    }
226
227
    /**
228
     * Set the monolog instance
229
     *
230
     * @param    \Monolog\Logger    $logger
231
     * 
232
     * @return \Comodojo\Cache\CacheObject\CacheObject
233
     */
234
    final public function setLogger(\Monolog\Logger $logger) {
235
236
        $this->logger = $logger;
237
238
        foreach ( $this->caches as $cache ) $cache->setLogger($logger);
239
240
        return $this;
241
242
    }
243
244 130
    public function addProvider(CacheInterface $cache_provider, $weight = 0) {
245
246 130
        $corrected_weight = filter_var($weight, FILTER_VALIDATE_INT, array(
247
            'options' => array(
248 90
                'min_range' => 0, 
249 90
                'max_range' => 100,
250
                'default'   => 0
251 90
            )
252 130
        ));
253
254 130
        $cache_id = $cache_provider->getCacheId();
255
256 130
        if ( array_key_exists($cache_id, $this->caches) ) throw new CacheException("Cache provider already registered");
257
258 130
        $cache_provider->setTime($this->getTime())->setTtl($this->getTtl());
259
260 130
        if ( $this->logger instanceof \Monolog\Logger ) $cache_provider->setLogger($this->logger);
261
262 130
        $this->caches[$cache_id] = $cache_provider;
263
264 130
        $this->cache_weights[$cache_id] = $corrected_weight;
265
266 130
        return $cache_id;
267
268
    }
269
270
    public function removeProvider($cache_id) {
271
272
        if ( array_key_exists($cache_id, $this->caches) && array_key_exists($cache_id, $this->cache_weights) ) {
273
274
            unset($this->caches[$cache_id]);
275
276
            unset($this->cache_weights[$cache_id]);
277
278
        } else {
279
280
            throw new CacheException("Cache not registered");
281
282
        }
283
284
        return true;
285
286
    }
287
288
    public function getProviders($type = null) {
289
290
        $providers = array();
291
        
292
        if ( is_null($type) ) {
293
        
294
            foreach ( $this->caches as $id => $cache ) $providers[$id] = get_class($cache); 
295
            
296
        } else {
297
            
298
            foreach ( $this->caches as $id => $cache ) {
299
                
300
                $provider_class = get_class($cache);
301
                
302
                if ( $provider_class == $type ) $providers[$id] = get_class($cache); 
303
304
            }
305
            
306
        }
307
        
308
        return $providers;
309
        
310
    }
311
312 60
    public function set($name, $data, $ttl = null) {
313
314 60
        $set = array();
315
316
        try {
317
        
318 60
            foreach ( $this->caches as $cache_id => $cache ) {
319
320 60
                $set[$cache_id] = $cache->set($name, $data, $ttl);
321
322 40
            }
323
324 40
        } catch (CacheException $ce) {
325
326
            throw $ce;
327
            
328
        }
329
330 60
        return $set;
331
332
    }
333
334 70
    public function get($name) {
335
336 70
        reset($this->caches);
337
        
338 70
        $this->selected_cache = null;
339
340 70
        $result = null;
341
342
        try {
343
        
344 70
            switch ( $this->selector ) {
345
            
346 70
                case 1:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
347
348 14
                    $result = $this->getCacheByLoop($this->caches, $name);
349
350 14
                    break;
351
352 56
                case 2:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
353
354 14
                    $result = $this->getCacheByLoop(array_reverse($this->caches, true), $name);
355
                    
356 14
                    break;
357
358 42
                case 3:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
359
360 28
                    $result = $this->getRandomCache($this->caches, $name);
361
                    
362 28
                    break;
363
364 14
                case 4:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
365
366 14
                    $result = $this->getCacheByWeight($this->caches, $this->cache_weights, $name);
367
368 14
                    break;
369
370
                case 5:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
371
372
                    $values = array();
373
374
                    foreach ( $this->caches as $cache ) {
375
                        
376
                        $values[] = $cache->get($name);
377
378
                    }
379
380
                    if ( count(array_unique($values)) === 1 ) {
381
382
                        $result = $values[0];
383
                        
384
                    } else {
385
386
                        $this->raiseError("Inconsistent values in cache providers, exiting gracefully");
387
388
                        $result = null;
389
390
                    } 
391
392 20
                    break;
393
                
394 50
            }
395
396 50
        } catch (\Exception $e) {
397
            
398
            throw $ce;
399
400
        }
401
402 70
        return $result;
403
404
    }
405
406 15 View Code Duplication
    public function delete($name = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
407
408 15
        $delete = array();
409
410
        try {
411
412 15
            foreach ( $this->caches as $cache_id => $cache ) {
413
414 15
                $delete[$cache_id] = $cache->delete($name);
415
416 10
            }
417
            
418 10
        } catch (CacheException $ce) {
419
            
420
            throw $ce;
421
422
        }
423
424 15
        return $delete;
425
426
    }
427
428 15 View Code Duplication
    public function flush() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
430 15
        $flush = array();
431
432
        try {
433
434 15
            foreach ( $this->caches as $cache_id => $cache ) {
435
436 15
                $flush[$cache_id] = $cache->flush();
437
438 10
            }
439
            
440 10
        } catch (CacheException $ce) {
441
            
442
            throw $ce;
443
444
        }
445
446 15
        return $flush;
447
448
    }
449
450 15 View Code Duplication
    public function status() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
451
452 15
        $status = array();
453
454
        try {
455
456 15
            foreach ( $this->caches as $cache_id => $cache ) {
457
458 15
                $status[$cache_id] = $cache->status();
459
460 10
            }
461
            
462 10
        } catch (CacheException $ce) {
463
            
464
            throw $ce;
465
466
        }
467
468 15
        return $status;
469
470
    }
471
472
    public function getSelectedCache() {
473
474
        return $this->selected_cache;
475
476
    }
477
478 28
    private function getCacheByLoop($caches, $name) {
479
480 28
        $result = null;
481
482 28
        $active_cache = false;
483
484 28
        foreach ( $caches as $cache ) {
485
            
486 28
            if ( $cache->isEnabled() ) {
487
488 28
                $result = $cache->get($name);
489
490 28
                if ( $cache->getErrorState() === false ) {
491
492 28
                    $this->selected_cache = $cache->getCacheId();
493
494 28
                    $active_cache = true;
495
496 28
                    break;
497
498
                }
499
500 2
            }
501
502 20
        }
503
504 28
        if ( $active_cache === false ) $this->raiseError("Cannot find an active cache provider (Manager), exiting gracefully");
505
506 28
        return $result;
507
508
    }
509
510 28
    private function getRandomCache($caches, $name) {
511
512 28
        $result = null;
513
514 28
        $active_cache = false;
515
516 28
        $size = sizeof($caches);
517
518 28
        for ( $i = 0; $i < $size; $i++ ) { 
519
            
520 28
            $cache_id = array_rand($caches);
521
522 28
            $cache = $caches[$cache_id];
523
524 28 View Code Duplication
            if ( $cache->isEnabled() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
525
526 28
                $result = $cache->get($name);
527
528 28
                if ( $cache->getErrorState() === false ) {
529
530 28
                    $this->selected_cache = $cache_id;
531
532 28
                    $active_cache = true;
533
534 28
                    break;
535
536
                } else {
537
538
                    unset($caches[$cache_id]);
539
540
                }
541
542
            } else {
543
544
                unset($caches[$cache_id]);
545
546
            }
547
548
        }
549
550 28
        if ( $active_cache === false ) $this->raiseError("Cannot find an active cache provider (Manager), exiting gracefully");
551
552 28
        return $result;
553
554
    }
555
556 14
    private function getCacheByWeight($caches, $weights, $name) {
557
558 14
        $result = null;
559
560 14
        $active_cache = false;
561
562 14
        $size = sizeof($weights);
563
564 14
        for ( $i = 0; $i < $size; $i++ ) { 
565
            
566 14
            $cache_ids = array_keys($weights, max($weights));
567
568 14
            $cache_id = $cache_ids[0];
569
570 14
            $cache = $caches[$cache_id];
571
572 14 View Code Duplication
            if ( $cache->isEnabled() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
573
574 14
                $result = $cache->get($name);
575
576 14
                if ( $cache->getErrorState() === false ) {
577
578 14
                    $this->selected_cache = $cache_id;
579
580 14
                    $active_cache = true;
581
582 14
                    break;
583
584
                } else {
585
586
                    unset($weights[$cache_id]);
587
588
                }
589
590
            } else {
591
592
                unset($weights[$cache_id]);
593
594
            }
595
596
        }
597
598 14
        if ( $active_cache === false ) $this->raiseError("Cannot find an active cache provider (Manager), exiting gracefully");
599
600 14
        return $result;
601
602
    }
603
604
} 
605