Completed
Push — 2.0 ( aed067...b26523 )
by Marco
04:31
created

Cache   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 375
Duplicated Lines 13.87 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 66.67%

Importance

Changes 6
Bugs 2 Features 1
Metric Value
wmc 57
c 6
b 2
f 1
lcom 1
cbo 5
dl 52
loc 375
ccs 102
cts 153
cp 0.6667
rs 6.433

22 Methods

Rating   Name   Duplication   Size   Complexity  
A removeProvider() 0 5 1
A getProviders() 0 5 2
A getSelectedProvider() 0 5 1
A __construct() 0 15 1
A getAutoSetTime() 0 5 1
A setAutoSetTime() 0 11 1
A setFlapInterval() 0 12 1
A getFlapInterval() 0 4 1
B addProvider() 0 28 3
A getProvider() 0 5 1
A setNamespace() 0 9 2
A setTime() 9 9 2
A setTtl() 9 9 2
A setLogger() 0 9 2
B set() 0 19 5
B get() 0 23 5
A delete() 17 17 4
A flush() 17 17 4
B status() 0 26 4
B getFromSingleProvider() 0 26 6
A getFromAllProviders() 0 20 4
A getTraverse() 0 18 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Cache often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Cache, and based on these observations, apply Extract Interface, too.

1
<?php namespace Comodojo\Cache;
2
3
use \Comodojo\Cache\Components\StackManager;
4
use \Comodojo\Cache\Providers\AbstractProvider;
5
use \Comodojo\Cache\Providers\ProviderInterface;
6
use \Psr\Log\LoggerInterface;
7
use \Comodojo\Exception\CacheException;
8
use \Exception;
9
10
/**
11
 * Cache manager
12
 *
13
 * @package     Comodojo Spare Parts
14
 * @author      Marco Giovinazzi <[email protected]>
15
 * @license     MIT
16
 *
17
 * LICENSE:
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
 * THE SOFTWARE.
26
 */
27
28
class Cache extends AbstractProvider {
29
30
    /**
31
     * Select the first (enabled) provider in queue, do not traverse the queue.
32
     */
33
    const PICK_FIRST = 1;
34
35
    /**
36
     * Select the last (enabled) provider in queue, do not traverse the queue.
37
     */
38
    const PICK_LAST = 2;
39
40
    /**
41
     * Select a random (enabled) provider in queue, do not traverse the queue.
42
     */
43
    const PICK_RANDOM = 3;
44
45
    /**
46
     * Select by weight, stop at first enabled provider.
47
     */
48
    const PICK_BYWEIGHT = 4;
49
50
    /**
51
     * Ask to all (enabled) providers and match responses.
52
     */
53
    const PICK_ALL = 5;
54
55
    /**
56
     * Select the first (enabled) provider, in case of null response traverse
57
     * the queue.
58
     */
59
    const PICK_TRAVERSE = 6;
60
61
    protected $selector;
62
63
    protected $stack;
64
65
    protected $provider;
66
67
    protected $auto_set_time = false;
68
69
    public function __construct($select_mode = null, LoggerInterface $logger = null, $default_ttl = 3600, $flap_interval = 600) {
0 ignored issues
show
Unused Code introduced by
The parameter $default_ttl is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
70
71
        $this->selector = filter_var($select_mode, FILTER_VALIDATE_INT, array(
72
            'options' => array(
73
                'min_range' => 1,
74
                'max_range' => 6,
75
                'default'   => 1
76
            )
77
        ));
78
79
        $this->stack = new StackManager($flap_interval);
80
81
        parent::__construct($logger);
82
83
    }
84
85
    public function getAutoSetTime() {
86
87
        return $this->auto_set_time;
88
89
    }
90
91
    public function setAutoSetTime($mode=true) {
92
93
        $this->auto_set_time = filter_var($mode, FILTER_VALIDATE_BOOLEAN, array(
94
            'options' => array(
95
                'default'   => true
96
            )
97
        ));
98
99
        return $this;
100
101
    }
102
103
    public function setFlapInterval($ttl) {
104
105
        $ttl = filter_var($ttl, FILTER_VALIDATE_INT, array(
106
            'options' => array(
107
                'min_range' => 1,
108
                'default'   => 700
109
            )
110
        ));
111
112
        return $this->stack->setFlapInterval($ttl);
113
114
    }
115
116
    public function getFlapInterval() {
117
118
        return $this->stack->getFlapInterval();
119
    }
120
121 1
    public function addProvider(ProviderInterface $provider, $weight = 0) {
122
123
        try {
124
125 1
            if ( $provider->isEnabled() === false ) {
126
127
                $id = $provider->getCacheId();
128
                $type = get_class($provider);
129
130
                $this->logger->warning("Adding provider $type [$id] as disabled: it will never serve cache requests");
131
132
            }
133
134 1
            $provider->setTime($this->getTime())
135 1
                ->setTtl($this->getTtl())
136 1
                ->setLogger($this->getLogger());
137
138 1
            $id = $this->stack->add($provider, $weight);
139
140 1
        } catch (CacheException $ce) {
141
142
            throw $ce;
143
144
        }
145
146 1
        return $id;
147
148
    }
149
150 1
    public function removeProvider($id) {
151
152 1
        $this->stack->remove($id);
153
154 1
    }
155
156
    public function getProvider($id) {
157
158
        return $this->stack->get($id);
159
160
    }
161
162 3
    public function getProviders($enabled=false) {
163
164 3
        return $enabled ? $this->stack->getAll() : $this->stack->getAll(false);
165
166
    }
167
168 4
    public function getSelectedProvider() {
169
170 4
        return $this->provider;
171
172
    }
173
174
    /* Following methods implement ProviderInterface */
175
176
    // public function getNamespace();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
177
178 12
    public function setNamespace($namespace = null) {
179
180 12
        parent::setNamespace($namespace);
181
182 12
        foreach ( $this->stack->getAll(false) as $provider ) $provider->setNamespace($namespace);
183
184 12
        return $this;
185
186
    }
187
188 47 View Code Duplication
    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...
189
190 47
        parent::setTime($time);
191
192 47
        foreach ( $this->stack->getAll(false) as $provider ) $provider->setTime($time);
193
194 47
        return $this;
195
196
    }
197
198 View Code Duplication
    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...
199
200
        parent::setTtl($ttl);
201
202
        foreach ( $this->stack->getAll(false) as $provider ) $provider->setTtl($ttl);
203
204
        return $this;
205
206
    }
207
208
    public function setLogger(LoggerInterface $logger = null) {
209
210
        parent::setLogger($logger);
211
212
        foreach ( $this->stack->getAll(false) as $provider ) $provider->setLogger($logger);
213
214
        return $this;
215
216
    }
217
218 29
    public function set($name, $data, $ttl = null) {
219
220 29
        if ( !$this->isEnabled() ) return false;
221
222 29
        if ( $this->auto_set_time ) $this->setTime();
223
224 29
        $set = array();
225
226 29
        foreach ($this->stack->getAll() as $id => $provider) {
227
228 29
            $set[] = $provider->set($name, $data, $ttl);
229
230 29
            if ( $provider->getErrorState() ) $this->stack->disable($id);
231
232 29
        };
233
234 29
        return !in_array(false, $set);
235
236
    }
237
238 41
    public function get($name) {
239
240 41
        if ( !$this->isEnabled() ) return null;
241
242 41
        if ( $this->auto_set_time ) $this->setTime();
243
244 41
        if ( $this->selector < 5 ) {
245
246 28
            $result = $this->getFromSingleProvider($this->selector, $name);
247
248 41
        } else if ( $this->selector == 5 ) {
249
250 6
            $result = $this->getFromAllProviders($name);
251
252 6
        } else {
253
254 7
            $result = $this->getTraverse($name);
255
256
        }
257
258 41
        return $result;
259
260
    }
261
262 10 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...
263
264 10
        if ( !$this->isEnabled() ) return false;
265
266 10
        $delete = array();
267
268 10
        foreach ($this->stack->getAll() as $id => $provider) {
269
270 10
            $delete[] = $provider->delete($name);
271
272 10
            if ( $provider->getErrorState() ) $this->stack->disable($id);
273
274 10
        };
275
276 10
        return !in_array(false, $delete);
277
278
    }
279
280 6 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...
281
282 6
        if ( !$this->isEnabled() ) return false;
283
284 6
        $flush = array();
285
286 6
        foreach ($this->stack->getAll() as $id => $provider) {
287
288 6
            $flush[] = $provider->flush();
289
290 6
            if ( $provider->getErrorState() ) $this->stack->disable($id);
291
292 6
        };
293
294 6
        return !in_array(false, $flush);
295
296
    }
297
298 6
    public function status() {
299
300 6
        $return = array();
301
302 6
        if ( !$this->isEnabled() ) {
303
304
            foreach ($this->stack->getAll(false) as $id => $provider) {
305
                $return[] = array(
306
                    "provider"  => get_class($provider),
307
                    "enabled"   => false,
308
                    "objects"   => null,
309
                    "options"   => array()
310
                );
311
            }
312
313
        } else {
314
315 6
            foreach ($this->stack->getAll(false) as $id => $provider) {
316 6
                $return[] = $provider->status();
317 6
            }
318
319
        }
320
321 6
        return $return;
322
323
    }
324
325
    // public function getCacheId();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
326
327
    // public function getLogger();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
328
329
    // public function setErrorState($message = null);
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
330
    // public function resetErrorState();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
331
    // public function getErrorState();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
332
    // public function getErrorMessage();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
333
334
335 28
    private function getFromSingleProvider($selector, $name) {
0 ignored issues
show
Unused Code introduced by
The parameter $selector is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
336
337 28
        switch ($this->selector) {
338
339 28
            case 1:
340 7
                $this->provider = $this->stack->getFirst();
341 7
                break;
342 21
            case 2:
343 7
                $this->provider = $this->stack->getLast();
344 7
                break;
345 14
            case 3:
346 7
                $this->provider = $this->stack->getRandom();
347 7
                break;
348 7
            case 4:
349 7
                $this->provider = $this->stack->getByWeight();
350 7
                break;
351
352 28
        }
353
354 28
        $result = $this->provider->get($name);
355
356 28
        if ( $this->provider->getErrorState() ) $this->stack->disable($this->provider->getCacheId());
357
358 28
        return $result;
359
360
    }
361
362 6
    private function getFromAllProviders($name) {
363
364 6
        $data = array();
365
366 6
        $providers = $this->stack->getAll();
367
368 6
        foreach ($providers as $id => $provider) {
369 6
            $data[] = $provider->get($name);
370 6
            if ( $provider->getErrorState() ) $this->stack->disable($provider->getCacheId());
371 6
        }
372
373 6
        $values = array_unique(array_map('serialize', $data));
374
375 6
        if (count($values) == 1) {
376 6
            return $data[0];
377
        }
378
379
        return null;
380
381
    }
382
383 7
    private function getTraverse($name) {
384
385 7
        $data = null;
386
387 7
        $providers = $this->stack->getAll();
388
389 7
        foreach ($providers as $id => $provider) {
390 7
            $data = $provider->get($name);
391 7
            if ( $provider->getErrorState() ) {
392
                $this->stack->disable($provider->getCacheId());
393
            } else {
394 7
                if ( $data != null ) return $data;
395
            }
396 5
        }
397
398 4
        return $data;
399
400
    }
401
402
}
403