Completed
Push — master ( 18312a...f64dc4 )
by Lars
03:45
created

Cache   C

Complexity

Total Complexity 75

Size/Duplication

Total Lines 496
Duplicated Lines 4.03 %

Coupling/Cohesion

Components 2
Dependencies 11

Test Coverage

Coverage 35.42%

Importance

Changes 20
Bugs 9 Features 2
Metric Value
wmc 75
c 20
b 9
f 2
lcom 2
cbo 11
dl 20
loc 496
ccs 68
cts 192
cp 0.3542
rs 5.4482

17 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 51 13
A setActive() 0 4 1
C isCacheActiveForTheCurrentUser() 0 28 7
B getClientIp() 0 18 7
C checkForDev() 0 29 8
A getTheDefaultPrefix() 0 4 4
F autoConnectToAvailableCacheSystem() 0 118 17
A setCacheIsReady() 0 4 1
A getCacheIsReady() 0 4 1
A getItem() 0 13 3
A calculateStoreKey() 0 4 1
A getPrefix() 0 4 1
A setPrefix() 0 4 1
A setItemToDate() 0 12 2
A setItem() 0 20 4
A removeItem() 10 10 2
A existsItem() 10 10 2

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
2
3
namespace voku\cache;
4
5
/**
6
 * Cache: global-cache class
7
 *
8
 * can use different cache-adapter:
9
 * - Redis
10
 * - Memcache / Memcached
11
 * - APC / APCu
12
 * - Xcache
13
 * - Array
14
 *
15
 * @package   voku\cache
16
 */
17
class Cache implements iCache
18
{
19
20
  /**
21
   * @var iAdapter
22
   */
23
  private $adapter;
24
25
  /**
26
   * @var iSerializer
27
   */
28
  private $serializer;
29
30
  /**
31
   * @var string
32
   */
33
  private $prefix = '';
34
35
  /**
36
   * @var bool
37
   */
38
  private $isReady = false;
39
40
  /**
41
   * @var bool
42
   */
43
  private $isActive = true;
44
45
  /**
46
   * @var mixed no cache, if admin-session is set
47
   */
48
  private $isAdminSession = false;
49
50
  /**
51
   * __construct
52
   *
53
   * @param null|iAdapter    $adapter
54
   * @param null|iSerializer $serializer
55
   * @param boolean          $checkForUser check for dev-ip or if cms-user is logged-in
56
   * @param boolean          $cacheEnabled false will disable the cache (use it e.g. for global settings)
57
   * @param string|boolean   $isAdminSession set a user-id, if the user is a admin (so we can disable cache for this user)
58
   */
59 33
  public function __construct($adapter = null, $serializer = null, $checkForUser = true, $cacheEnabled = true, $isAdminSession = false)
60
  {
61 33
    $this->isAdminSession = $isAdminSession;
62
63
    // check for active-cache
64 33
    $this->setActive($cacheEnabled);
65 33
    if ($this->isActive === true && $checkForUser === true) {
66
      $this->setActive($this->isCacheActiveForTheCurrentUser());
67
    }
68
69 33
    if ($this->isActive === true) {
70
71 33
      $this->setPrefix($this->getTheDefaultPrefix());
72
73
      if (
74
          $adapter === null
75 33
          ||
76 33
          !is_object($adapter)
77 33
          ||
78
          !$adapter instanceof iAdapter
79 33
      ) {
80
        $adapter = $this->autoConnectToAvailableCacheSystem();
81
      }
82
83
      // Memcache(d) has his own "serializer", so don't use it twice
84 33
      if (!is_object($serializer) && $serializer === null) {
85
        if (
86
            $adapter instanceof AdapterMemcached
87
            ||
88
            $adapter instanceof AdapterMemcache
89
        ) {
90
          $serializer = new SerializerNo();
91 1
        } else {
92
          // set serializer as default
93
          $serializer = new SerializerIgbinary();
94
        }
95
      }
96 33
    }
97
98
    // check if we will use the cache
99
    if (
100
        $serializer instanceof iSerializer
101 33
        &&
102
        $adapter instanceof iAdapter
103 33
    ) {
104 33
      $this->setCacheIsReady(true);
105
106 33
      $this->adapter = $adapter;
107 33
      $this->serializer = $serializer;
108 33
    }
109 33
  }
110
111
  /**
112
   * enable / disable the cache
113
   *
114
   * @param boolean $isActive
115
   */
116 33
  public function setActive($isActive)
117
  {
118 33
    $this->isActive = (boolean)$isActive;
119 33
  }
120
121
  /**
122
   * check if the current use is a admin || dev || server == client
123
   *
124
   * @return bool
125
   */
126
  public function isCacheActiveForTheCurrentUser()
0 ignored issues
show
Coding Style introduced by
isCacheActiveForTheCurrentUser uses the super-global variable $_GET 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...
Coding Style introduced by
isCacheActiveForTheCurrentUser uses the super-global variable $_SERVER 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...
127
  {
128
    $active = true;
129
130
    // test the cache, with this GET-parameter
131
    $testCache = isset($_GET['testCache']) ? (int)$_GET['testCache'] : 0;
132
133
    if ($testCache != 1) {
134
      if (
135
        // server == client
136
          (
137
              isset($_SERVER['SERVER_ADDR'])
138
              &&
139
              $_SERVER['SERVER_ADDR'] == $this->getClientIp()
140
          )
141
          ||
142
          // admin is logged-in
143
          $this->isAdminSession
144
          ||
145
          // user is a dev
146
          $this->checkForDev() === true
147
      ) {
148
        $active = false;
149
      }
150
    }
151
152
    return $active;
153
  }
154
155
  /**
156
   * returns the IP address of the client
157
   *
158
   * @param   bool $trust_proxy_headers   Whether or not to trust the
159
   *                                      proxy headers HTTP_CLIENT_IP
160
   *                                      and HTTP_X_FORWARDED_FOR. ONLY
161
   *                                      use if your $_SERVER is behind a
162
   *                                      proxy that sets these values
163
   *
164
   * @return  string
165
   */
166
  private function getClientIp($trust_proxy_headers = false)
0 ignored issues
show
Coding Style introduced by
getClientIp uses the super-global variable $_SERVER 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...
167
  {
168
    $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'NO_REMOTE_ADDR';
169
170
    if ($trust_proxy_headers) {
171
      return $remoteAddr;
172
    }
173
174
    if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP']) {
175
      $ip = $_SERVER['HTTP_CLIENT_IP'];
176
    } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR']) {
177
      $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
178
    } else {
179
      $ip = $remoteAddr;
180
    }
181
182
    return $ip;
183
  }
184
185
  /**
186
   * check for developer
187
   *
188
   * @return bool
189
   */
190
  private function checkForDev()
0 ignored issues
show
Coding Style introduced by
checkForDev uses the super-global variable $_GET 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...
Coding Style introduced by
checkForDev uses the super-global variable $_SERVER 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...
191
  {
192
    $return = false;
193
194
    if (function_exists('checkForDev')) {
195
      $return = checkForDev();
196
    } else {
197
198
      // for testing with dev-address
199
      $noDev = isset($_GET['noDev']) ? (int)$_GET['noDev'] : 0;
200
      $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'NO_REMOTE_ADDR';
201
202
      if (
203
          $noDev != 1
204
          &&
205
          (
206
              $remoteAddr == '127.0.0.1'
207
              ||
208
              $remoteAddr == '::1'
209
              ||
210
              PHP_SAPI == 'cli'
211
          )
212
      ) {
213
        $return = true;
214
      }
215
    }
216
217
    return $return;
218
  }
219
220
  /**
221
   * set the default-prefix via "SERVER_NAME" + "SESSION"-language
222
   */
223 33
  protected function getTheDefaultPrefix()
0 ignored issues
show
Coding Style introduced by
getTheDefaultPrefix uses the super-global variable $_SERVER 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...
Coding Style introduced by
getTheDefaultPrefix 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...
224
  {
225 33
    return (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '') . '_' . (isset($_SESSION['language']) ? $_SESSION['language'] : '') . '_' . (isset($_SESSION['language_extra']) ? $_SESSION['language_extra'] : '');
226
  }
227
228
  /**
229
   * auto-connect to the available cache-system on the server
230
   *
231
   * @return iAdapter
232
   */
233
  protected function autoConnectToAvailableCacheSystem()
234
  {
235
    static $adapterCache;
236
237
    if (is_object($adapterCache) && $adapterCache instanceof iAdapter) {
238
      return $adapterCache;
239
    } else {
240
241
      $memcached = null;
242
      $isMemcachedAvailable = false;
243
      if (extension_loaded('memcached')) {
244
        $memcached = new \Memcached();
245
        $isMemcachedAvailable = $memcached->addServer('127.0.0.1', '11211');
246
      }
247
248
      if ($isMemcachedAvailable === false) {
249
        $memcached = null;
250
      }
251
252
      $adapterMemcached = new AdapterMemcached($memcached);
0 ignored issues
show
Bug introduced by
It seems like $memcached can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
253
      if ($adapterMemcached->installed() === true) {
254
255
        // fallback to Memcached
256
        $adapter = $adapterMemcached;
257
258
      } else {
259
260
        $memcache = null;
261
        $isMemcacheAvailable = false;
262
        if (class_exists('\Memcache')) {
263
          $memcache = new \Memcache;
264
          /** @noinspection PhpUsageOfSilenceOperatorInspection */
265
          $isMemcacheAvailable = @$memcache->connect('127.0.0.1', 11211);
266
        }
267
268
        if ($isMemcacheAvailable === false) {
269
          $memcache = null;
270
        }
271
272
        $adapterMemcache = new AdapterMemcache($memcache);
0 ignored issues
show
Bug introduced by
It seems like $memcache can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
273
        if ($adapterMemcache->installed() === true) {
274
275
          // fallback to Memcache
276
          $adapter = $adapterMemcache;
277
278
        } else {
279
280
          $redis = null;
281
          $isRedisAvailable = false;
282
          if (extension_loaded('redis')) {
283
            if (class_exists('\Predis\Client')) {
284
              $redis = new \Predis\Client(
285
                  array(
286
                      'scheme'  => 'tcp',
287
                      'host'    => '127.0.0.1',
288
                      'port'    => 6379,
289
                      'timeout' => '2.0',
290
                  )
291
              );
292
              try {
293
                $redis->connect();
294
                $isRedisAvailable = $redis->getConnection()->isConnected();
295
              } catch (\Exception $e) {
296
                // nothing
297
              }
298
            }
299
          }
300
301
          if ($isRedisAvailable === false) {
302
            $redis = null;
303
          }
304
305
          $adapterRedis = new AdapterPredis($redis);
0 ignored issues
show
Bug introduced by
It seems like $redis can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
306
          if ($adapterRedis->installed() === true) {
307
308
            // fallback to Redis
309
            $adapter = $adapterRedis;
310
311
          } else {
312
313
            $adapterXcache = new AdapterXcache();
314
            if ($adapterXcache->installed() === true) {
315
316
              // fallback to Xcache
317
              $adapter = $adapterXcache;
318
319
            } else {
320
321
              $adapterApc = new AdapterApc();
322
              if ($adapterApc->installed() === true) {
323
324
                // fallback to APC || APCu
325
                $adapter = $adapterApc;
326
327
              } else {
328
329
                $adapterFile = new AdapterFile();
330
                if ($adapterFile->installed() === true) {
331
332
                  // fallback to File-Cache
333
                  $adapter = $adapterFile;
334
335
                } else {
336
                  // no cache-adapter available -> use a array
337
                  $adapter = new AdapterArray();
338
                }
339
              }
340
            }
341
          }
342
        }
343
      }
344
345
      // save to static cache
346
      $adapterCache = $adapter;
347
    }
348
349
    return $adapter;
350
  }
351
352
  /**
353
   * set cacheIsReady state
354
   *
355
   * @param boolean $isReady
356
   */
357 33
  private function setCacheIsReady($isReady)
358
  {
359 33
    $this->isReady = (boolean)$isReady;
360 33
  }
361
362
  /**
363
   * get the cacheIsReady state
364
   *
365
   * @return boolean
366
   */
367 4
  public function getCacheIsReady()
368
  {
369 4
    return $this->isReady;
370
  }
371
372
  /**
373
   * get cached-item by key
374
   *
375
   * @param string $key
376
   *
377
   * @return mixed
378
   */
379 15
  public function getItem($key)
380
  {
381 15
    $storeKey = $this->calculateStoreKey($key);
382
383 15
    if ($this->adapter instanceof iAdapter) {
384 15
      $serialized = $this->adapter->get($storeKey);
385 15
      $value = $serialized ? $this->serializer->unserialize($serialized) : null;
386 15
    } else {
387
      return null;
388
    }
389
390 15
    return $value;
391
  }
392
393
  /**
394
   * calculate store-key (prefix + $rawKey)
395
   *
396
   * @param String $rawKey
397
   *
398
   * @return string
399
   */
400 28
  private function calculateStoreKey($rawKey)
401
  {
402 28
    return $this->getPrefix() . $rawKey;
403
  }
404
405
  /**
406
   * @return mixed
407
   */
408 28
  public function getPrefix()
409
  {
410 28
    return $this->prefix;
411
  }
412
413
  /**
414
   * set prefix [WARNING: do not use if you don't know what you do]
415
   *
416
   * @param string $prefix
417
   */
418 33
  public function setPrefix($prefix)
419
  {
420 33
    $this->prefix = (string)$prefix;
421 33
  }
422
423
  /**
424
   * set cache-item by key => value + date
425
   *
426
   * @param string    $key
427
   * @param mixed     $value
428
   * @param \DateTime $date
429
   *
430
   * @return mixed|void
431
   * @throws \Exception
432
   */
433 6
  public function setItemToDate($key, $value, \DateTime $date)
434
  {
435 6
    $ttl = $date->getTimestamp() - time();
436
437 6
    if ($ttl <= 0) {
438 1
      throw new \Exception('Date in the past.');
439
    }
440
441 5
    $storeKey = $this->calculateStoreKey($key);
442
443 5
    return $this->setItem($storeKey, $value, $ttl);
444
  }
445
446
  /**
447
   * set cache-item by key => value + ttl
448
   *
449
   * @param string $key
450
   * @param mixed  $value
451
   * @param int    $ttl
452
   *
453
   * @return bool
454
   */
455 15
  public function setItem($key, $value, $ttl = 0)
456
  {
457 15
    $storeKey = $this->calculateStoreKey($key);
458
459
    if (
460 15
        $this->adapter instanceof iAdapter
461 15
        &&
462 15
        $this->serializer instanceof iSerializer
463 15
    ) {
464 15
      $serialized = $this->serializer->serialize($value);
465
466 15
      if ($ttl) {
467 7
        return $this->adapter->setExpired($storeKey, $serialized, $ttl);
468
      } else {
469 8
        return $this->adapter->set($storeKey, $serialized);
470
      }
471
    } else {
472
      return false;
473
    }
474
  }
475
476
  /**
477
   * remove cached-item
478
   *
479
   * @param string $key
480
   *
481
   * @return bool
482
   */
483 1 View Code Duplication
  public function removeItem($key)
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...
484
  {
485 1
    if ($this->adapter instanceof iAdapter) {
486 1
      $storeKey = $this->calculateStoreKey($key);
487
488 1
      return $this->adapter->remove($storeKey);
489
    } else {
490
      return false;
491
    }
492
  }
493
494
  /**
495
   * check if cached-item exists
496
   *
497
   * @param string $key
498
   *
499
   * @return boolean
500
   */
501 5 View Code Duplication
  public function existsItem($key)
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...
502
  {
503 5
    if ($this->adapter instanceof iAdapter) {
504 5
      $storeKey = $this->calculateStoreKey($key);
505
506 5
      return $this->adapter->exists($storeKey);
507
    } else {
508
      return false;
509
    }
510
  }
511
512
}
513