Completed
Push — master ( c25cb6...fef623 )
by Oskar
05:14
created

CCachePool   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 350
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 15.89%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 41
c 2
b 1
f 0
lcom 1
cbo 3
dl 0
loc 350
ccs 17
cts 107
cp 0.1589
rs 8.2769

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 4
A getItem() 0 18 3
A generateKey() 0 5 1
A checkKey() 0 14 3
A getItems() 0 16 3
A hasItem() 0 12 3
A clear() 0 16 2
A deleteItem() 0 19 4
A deleteItems() 0 14 4
A save() 0 12 2
A saveDeferred() 0 16 4
A commit() 0 13 3
A saveInSession() 0 7 2
A clearSession() 0 9 3

How to fix   Complexity   

Complex Class

Complex classes like CCachePool 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 CCachePool, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Anax\Cache;
4
5
/**
6
 *
7
 */
8
class CCachePool implements \Psr\Cache\CacheItemPoolInterface
9
{
10
    /**
11
     * An array with all cache items,
12
     * @var \Psr\Cache\CacheItemInterface[]
13
     */
14
    private $cacheItems;
15
16
    /**
17
     * An array of items to be saved later
18
     * @var \Psr\Cache\CacheItemInterface[]
19
     */
20
    private $defferedItems;
21
22
    /**
23
     * Inits one array with the cached items and one with the deffered
24
     *
25
     */
26 1
    public function __construct()
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
27
    {
28 1
        $this->cacheItems    = isset($_SESSION['cacheItems']) ? $_SESSION['cacheItems'] : [];
29 1
        $this->defferedItems = isset($_SESSION['defferedItems']) ? $_SESSION['defferedItems'] : [];
30
31
        // Creates a writable cache folder on the server
32 1
        if (!is_dir('cacheitems')) {
33 1
            mkdir('cacheitems', 0777, true);
34 1
        }
35 1
    }
36
37
    /**
38
     * Returns a Cache Item representing the specified key.
39
     *
40
     * This method must always return a CacheItemInterface object, even in case of
41
     * a cache miss. It MUST NOT return null.
42
     *
43
     * @param string $key
44
     *   The key for which to return the corresponding Cache Item.
45
     *
46
     * @throws InvalidArgumentException
47
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
48
     *   MUST be thrown.
49
     *
50
     * @return CacheItemInterface
51
     *   The corresponding Cache Item.
52
     */
53
    public function getItem($key)
54
    {
55
        // Checks if the key is valid
56
        $this->checkKey($key);
57
58
        // Iterate all items to find the right match
59
        foreach ($this->cacheItems as $cacheItem) {
60
            if ($cacheItem->getKey() == $key) {
61
                return $cacheItem;
62
            }
63
        }
64
65
        // If the item was not found in the current array, a new item is created and added to the array
66
        $item = new \Anax\Cache\CCacheFile($key);
67
        $this->cacheItems[] = $item;
68
69
        return $item;
70
    }
71
72
    /**
73
     * Generates a key from a string.
74
     *
75
     * @param string $str
76
     *   The value to convert to a key-string, must be unique
77
     *
78
     * @return string
79
     *   A generated key-string
80
     */
81
    public function generateKey($str)
82
    {
83
        $strEncoded = base64_encode($str);
84
        return 'ch_' . md5($strEncoded);
85
    }
86
87
    /**
88
     * Checks if a key string is valid, invalid values are {}()/\@:
89
     *
90
     * @param string $key
91
     * A string to ckeck, invalid values are {}()/\@:
92
     *
93
     * @throws InvalidArgumentException
94
     * If $key are not a legal value
95
     *
96
     * @return void
97
     */
98 1
    public function checkKey($key)
99
    {
100
        try {
101
            // The characters that are not legal/is invalid
102 1
            $invalidValues = "/[\/\{\}\(\)\@\:\.\\\]/";
103
104
            // Checks for matches in the key
105 1
            if (preg_match_all($invalidValues, $key, $matches)) {
106
                throw new \Anax\Cache\InvalidKeyException($matches);
107
            }
108 1
        } catch (\Anax\Cache\InvalidKeyException $e) {
109
            echo  "Exception cought: ", $e;
110
        }
111 1
    }
112
113
    /**
114
     * Returns a traversable set of cache items.
115
     *
116
     * @param array $keys
117
     * An indexed array of keys of items to retrieve.
118
     *
119
     * @throws InvalidArgumentException
120
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
121
     *   MUST be thrown.
122
     *
123
     * @return array|\Traversable
124
     *   A traversable collection of Cache Items keyed by the cache keys of
125
     *   each item. A Cache item will be returned for each key, even if that
126
     *   key is not found. However, if no keys are specified then an empty
127
     *   traversable MUST be returned instead.
128
     */
129
    public function getItems(array $keys = array())
130
    {
131
        // Validating the keys
132
        foreach ($keys as $key) {
133
            $this->checkKey($key);
134
        }
135
136
        // Iterates all keys and gets the associated item
137
        $items = array();
138
        $nKeys = count($keys);
139
        for ($i=0; $i < $nKeys; $i++) {
140
            $items[(string) $keys[$i]] = $this->getItem($keys[$i]);
141
        }
142
143
        return $items;
144
    }
145
146
    /**
147
     * Confirms if the cache contains specified cache item.
148
     *
149
     * Note: This method MAY avoid retrieving the cached value for performance reasons.
150
     * This could result in a race condition with CacheItemInterface::get(). To avoid
151
     * such situation use CacheItemInterface::isHit() instead.
152
     *
153
     * @param string $key
154
     *    The key for which to check existence.
155
     *
156
     * @throws InvalidArgumentException
157
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
158
     *   MUST be thrown.
159
     *
160
     * @return bool
161
     *  True if item exists in the cache, false otherwise.
162
     */
163 1
    public function hasItem($key)
164
    {
165
        //Validates the key
166 1
        $this->checkKey($key);
167
168 1
        foreach ($this->cacheItems as $cacheItem) {
169
            if ($cacheItem->getKey() == $key) {
170
                return true;
171
            }
172 1
        }
173 1
        return false;
174
    }
175
176
    /**
177
     * Deletes all items in the pool.
178
     *
179
     * @return bool
180
     *   True if the pool was successfully cleared. False if there was an error.
181
     */
182
    public function clear()
183
    {
184
        // Clears the current session and finds all files in the cache folder
185
        $this->clearSession();
186
187
        $this->cacheItems = array();
188
        $this->defferedItems = array();
189
190
        $files = glob('cacheitems' . '/*');
191
192
        if (!array_map('unlink', $files)) {
193
            return false;
194
        }
195
196
        return true;
197
    }
198
199
    /**
200
     * Removes the item from the pool.
201
     *
202
     * @param string $key
203
     *   The key for which to delete
204
     *
205
     * @throws InvalidArgumentException
206
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
207
     *   MUST be thrown.
208
     *
209
     * @return bool
210
     *   True if the item was successfully removed. False if there was an error.
211
     */
212
    public function deleteItem($key)
213
    {
214
        //Checks if the key is valid
215
        $this->checkKey($key);
216
217
        foreach ($this->cacheItems as $cacheItem) {
218
            if ($key == $cacheItem->getKey()) {
219
                $file = $cacheItem->filename($cacheItem->getKey());
220
                if (is_file($file)) {
221
                    @unlink($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
222
                    unset($cacheItem);
223
                } else {
224
                    return false;
225
                }
226
                return true;
227
            }
228
        }
229
        return false;
230
    }
231
232
    /**
233
     * Removes multiple items from the pool.
234
     *
235
     * @param array $keys
236
     *   An array of keys that should be removed from the pool.
237
238
     * @throws InvalidArgumentException
239
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
240
     *   MUST be thrown.
241
     *
242
     * @return bool
243
     *   True if the items were successfully removed. False if there was an error.
244
     */
245
    public function deleteItems(array $keys)
246
    {
247
        //Checks if the keys are valid
248
        foreach ($keys as $key) {
249
            $this->checkKey($key);
250
        }
251
252
        foreach ($keys as $key) {
253
            if (!$this->deleteItem($key)) {
254
                return false;
255
            }
256
        }
257
        return true;
258
    }
259
260
    /**
261
     * Persists a cache item immediately.
262
     *
263
     * @param CacheItemInterface $item
264
     *   The cache item to save.
265
     *
266
     * @return bool
267
     *   True if the item was successfully persisted. False if there was an error.
268
     */
269
    public function save(\Psr\Cache\CacheItemInterface $item)
270
    {
271
        // Gets the filename
272
        $file = $item->filename($item->getKey());
273
274
        // Checks if the file write failes and serializes the value
275
        if (!file_put_contents($file, serialize($item->getValue()))) {
276
            return false;
277
        }
278
279
        return true;
280
    }
281
282
    /**
283
     * Sets a cache item to be persisted later.
284
     *
285
     * @param CacheItemInterface $item
286
     *   The cache item to save.
287
     *
288
     * @return bool
289
     *   False if the item could not be queued or if a commit was attempted and failed. True otherwise.
290
     */
291
    public function saveDeferred(\Psr\Cache\CacheItemInterface $item)
292
    {
293
        // Checks if the item is already saved as deffered
294
        foreach ($this->defferedItems as $defferedItem) {
295
            if ($defferedItem->getKey() == $item->getKey()) {
296
                return false;
297
            }
298
        }
299
300
        // Adds the item to the array of deffered items and returns true if it was successfull
301
        if (array_push($this->defferedItems, $item)) {
302
            return true;
303
        }
304
305
        return false;
306
    }
307
308
    /**
309
     * Persists any deferred cache items.
310
     *
311
     * @return bool
312
     *   True if all not-yet-saved items were successfully saved or there were none. False otherwise.
313
     */
314
    public function commit()
315
    {
316
        // Saves all items in $defferedItems
317
        foreach ($this->defferedItems as $item) {
318
            if (!$this->save($item)) {
319
                return false;
320
            }
321
        }
322
323
        // Resets the deffered array
324
        $this->defferedItems = array();
325
        return true;
326
    }
327
328
    /**
329
     * Saves the current items/defferedItems in the session
330
     *
331
     * @param  bool $value If the items/defferedItems should be saved in the session
332
     *
333
     * @return void
334
     */
335
    public function saveInSession($value)
0 ignored issues
show
Coding Style introduced by
saveInSession uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
336
    {
337
        if ($value) {
338
            $_SESSION['cacheItems']    = $this->cacheItems;
339
            $_SESSION['defferedItems'] = $this->defferedItems;
340
        }
341
    }
342
343
    /**
344
     * Clears the current session
345
     *
346
     * @return void
347
     */
348
    public function clearSession()
0 ignored issues
show
Coding Style introduced by
clearSession uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
349
    {
350
        if (isset($_SESSION['cacheItems'])) {
351
            unset($_SESSION['cacheItems']);
352
        }
353
        if (isset($_SESSION['defferedItems'])) {
354
            unset($_SESSION['defferedItems']);
355
        }
356
    }
357
}
358