Completed
Push — master ( 9ba802...edbcad )
by Lars
02:46
created

Cache::cleanStoreKey()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 26
ccs 21
cts 21
cp 1
rs 8.8571
cc 1
eloc 21
nc 1
nop 1
crap 1
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
58
   *                                         user)
59
   */
60 39
  public function __construct($adapter = null, $serializer = null, $checkForUser = true, $cacheEnabled = true, $isAdminSession = false)
61
  {
62 39
    $this->isAdminSession = $isAdminSession;
63
64
    // check for active-cache
65 39
    $this->setActive($cacheEnabled);
66 39
    if ($this->isActive === true && $checkForUser === true) {
67
      $this->setActive($this->isCacheActiveForTheCurrentUser());
68
    }
69
70 39
    if ($this->isActive === true) {
71
72 39
      $this->setPrefix($this->getTheDefaultPrefix());
73
74
      if (
75
          $adapter === null
76 39
          ||
77 39
          !is_object($adapter)
78 39
          ||
79
          !$adapter instanceof iAdapter
80 39
      ) {
81
        $adapter = $this->autoConnectToAvailableCacheSystem();
82
      }
83
84
      // Memcache(d) has his own "serializer", but it seems to be working different as the php-implementation.
85 39
      if (!is_object($serializer) && $serializer === null) {
86
        $serializer = new SerializerIgbinary();
87
      }
88 39
    }
89
90
    // check if we will use the cache
91 1
    if (
92
        $serializer instanceof iSerializer
93 39
        &&
94
        $adapter instanceof iAdapter
95 39
    ) {
96 39
      $this->setCacheIsReady(true);
97
98 39
      $this->adapter = $adapter;
99 39
      $this->serializer = $serializer;
100 39
    }
101 39
  }
102
103
  /**
104
   * enable / disable the cache
105
   *
106
   * @param boolean $isActive
107
   */
108 39
  public function setActive($isActive)
109
  {
110 39
    $this->isActive = (boolean)$isActive;
111 39
  }
112
113
  /**
114
   * check if the current use is a admin || dev || server == client
115
   *
116
   * @return bool
117
   */
118
  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...
119
  {
120
    $active = true;
121
122
    // test the cache, with this GET-parameter
123
    $testCache = isset($_GET['testCache']) ? (int)$_GET['testCache'] : 0;
124
125
    if ($testCache != 1) {
126
      if (
127
        // server == client
128
          (
129
              isset($_SERVER['SERVER_ADDR'])
130
              &&
131
              $_SERVER['SERVER_ADDR'] == $this->getClientIp()
132
          )
133
          ||
134
          // admin is logged-in
135
          $this->isAdminSession
136
          ||
137
          // user is a dev
138
          $this->checkForDev() === true
139
      ) {
140
        $active = false;
141
      }
142
    }
143
144
    return $active;
145
  }
146
147
  /**
148
   * returns the IP address of the client
149
   *
150
   * @param   bool $trust_proxy_headers   Whether or not to trust the
151
   *                                      proxy headers HTTP_CLIENT_IP
152
   *                                      and HTTP_X_FORWARDED_FOR. ONLY
153
   *                                      use if your $_SERVER is behind a
154
   *                                      proxy that sets these values
155
   *
156
   * @return  string
157
   */
158
  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...
159
  {
160
    $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'NO_REMOTE_ADDR';
161
162
    if ($trust_proxy_headers) {
163
      return $remoteAddr;
164
    }
165
166
    if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP']) {
167
      $ip = $_SERVER['HTTP_CLIENT_IP'];
168
    } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR']) {
169
      $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
170
    } else {
171
      $ip = $remoteAddr;
172
    }
173
174
    return $ip;
175
  }
176
177
  /**
178
   * check for developer
179
   *
180
   * @return bool
181
   */
182
  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...
183
  {
184
    $return = false;
185
186
    if (function_exists('checkForDev')) {
187
      $return = checkForDev();
188
    } else {
189
190
      // for testing with dev-address
191
      $noDev = isset($_GET['noDev']) ? (int)$_GET['noDev'] : 0;
192
      $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'NO_REMOTE_ADDR';
193
194
      if (
195
          $noDev != 1
196
          &&
197
          (
198
              $remoteAddr === '127.0.0.1'
199
              ||
200
              $remoteAddr === '::1'
201
              ||
202
              PHP_SAPI === 'cli'
203
          )
204
      ) {
205
        $return = true;
206
      }
207
    }
208
209
    return $return;
210
  }
211
212
  /**
213
   * set the default-prefix via "SERVER"-var + "SESSION"-language
214
   */
215 39
  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...
216
  {
217 39
    return (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '') . '_' .
218 39
           (isset($_SERVER['THEME']) ? $_SERVER['THEME'] : '') . '_' .
219 39
           (isset($_SERVER['STAGE']) ? $_SERVER['STAGE'] : '') . '_' .
220 39
           (isset($_SESSION['language']) ? $_SESSION['language'] : '') . '_' .
221 39
           (isset($_SESSION['language_extra']) ? $_SESSION['language_extra'] : '');
222
  }
223
224
  /**
225
   * auto-connect to the available cache-system on the server
226
   *
227
   * @return iAdapter
228
   */
229
  protected function autoConnectToAvailableCacheSystem()
230
  {
231
    static $adapterCache;
232
233
    if (is_object($adapterCache) && $adapterCache instanceof iAdapter) {
234
      return $adapterCache;
235
    } else {
236
237
      $memcached = null;
238
      $isMemcachedAvailable = false;
239
      if (extension_loaded('memcached')) {
240
        $memcached = new \Memcached();
241
        $isMemcachedAvailable = $memcached->addServer('127.0.0.1', '11211');
242
      }
243
244
      if ($isMemcachedAvailable === false) {
245
        $memcached = null;
246
      }
247
248
      $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...
249
      if ($adapterMemcached->installed() === true) {
250
251
        // fallback to Memcached
252
        $adapter = $adapterMemcached;
253
254
      } else {
255
256
        $memcache = null;
257
        $isMemcacheAvailable = false;
258
        if (class_exists('\Memcache')) {
259
          $memcache = new \Memcache;
260
          /** @noinspection PhpUsageOfSilenceOperatorInspection */
261
          $isMemcacheAvailable = @$memcache->connect('127.0.0.1', 11211);
262
        }
263
264
        if ($isMemcacheAvailable === false) {
265
          $memcache = null;
266
        }
267
268
        $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...
269
        if ($adapterMemcache->installed() === true) {
270
271
          // fallback to Memcache
272
          $adapter = $adapterMemcache;
273
274
        } else {
275
276
          $redis = null;
277
          $isRedisAvailable = false;
278
          if (extension_loaded('redis')) {
279
            if (class_exists('\Predis\Client')) {
280
              /** @noinspection PhpUndefinedNamespaceInspection */
281
              $redis = new \Predis\Client(
282
                  array(
283
                      'scheme'  => 'tcp',
284
                      'host'    => '127.0.0.1',
285
                      'port'    => 6379,
286
                      'timeout' => '2.0',
287
                  )
288
              );
289
              try {
290
                $redis->connect();
291
                $isRedisAvailable = $redis->getConnection()->isConnected();
292
              } catch (\Exception $e) {
293
                // nothing
294
              }
295
            }
296
          }
297
298
          if ($isRedisAvailable === false) {
299
            $redis = null;
300
          }
301
302
          $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...
303
          if ($adapterRedis->installed() === true) {
304
305
            // fallback to Redis
306
            $adapter = $adapterRedis;
307
308
          } else {
309
310
            $adapterXcache = new AdapterXcache();
311
            if ($adapterXcache->installed() === true) {
312
313
              // fallback to Xcache
314
              $adapter = $adapterXcache;
315
316
            } else {
317
318
              $adapterApc = new AdapterApc();
319
              if ($adapterApc->installed() === true) {
320
321
                // fallback to APC || APCu
322
                $adapter = $adapterApc;
323
324
              } else {
325
326
                $adapterFile = new AdapterFile();
327
                if ($adapterFile->installed() === true) {
328
329
                  // fallback to File-Cache
330
                  $adapter = $adapterFile;
331
332
                } else {
333
                  // no cache-adapter available -> use a array
334
                  $adapter = new AdapterArray();
335
                }
336
              }
337
            }
338
          }
339
        }
340
      }
341
342
      // save to static cache
343
      $adapterCache = $adapter;
344
    }
345
346
    return $adapter;
347
  }
348
349
  /**
350
   * set cacheIsReady state
351
   *
352
   * @param boolean $isReady
353
   */
354 39
  private function setCacheIsReady($isReady)
355
  {
356 39
    $this->isReady = (boolean)$isReady;
357 39
  }
358
359
  /**
360
   * get the cacheIsReady state
361
   *
362
   * @return boolean
363
   */
364 5
  public function getCacheIsReady()
365
  {
366 5
    return $this->isReady;
367
  }
368
369
  /**
370
   * get cached-item by key
371
   *
372
   * @param string $key
373
   *
374
   * @return mixed
375
   */
376 18
  public function getItem($key)
377
  {
378 18
    if ($this->adapter instanceof iAdapter) {
379 18
      $storeKey = $this->calculateStoreKey($key);
380 18
      $serialized = $this->adapter->get($storeKey);
381 18
      $value = $serialized ? $this->serializer->unserialize($serialized) : null;
382 18
    } else {
383
      return null;
384
    }
385
386 18
    return $value;
387
  }
388
389
  /**
390
   * calculate store-key (prefix + $rawKey)
391
   *
392
   * @param String $rawKey
393
   *
394
   * @return string
395
   */
396 33
  private function calculateStoreKey($rawKey)
397
  {
398 33
    $str = $this->getPrefix() . $rawKey;
399
400 33
    if ($this->adapter instanceof AdapterFile) {
401 5
      $str = $this->cleanStoreKey($str);
402 5
    }
403
404 33
    return $str;
405
  }
406
407
  /**
408
   * @param $str
409
   *
410
   * @return string
411
   */
412 5
  private function cleanStoreKey($str)
413
  {
414 5
    $str = preg_replace("/[\r\n\t ]+/", ' ', $str);
415 5
    $str = str_replace(
416 5
        array('"', '*', ':', '<', '>', '?', "'", '|'),
417
        array(
418 5
            '-+-',
419 5
            '-+-+-',
420 5
            '-+-+-+-',
421 5
            '-+-+-+-+-',
422 5
            '-+-+-+-+-+-',
423 5
            '-+-+-+-+-+-+-',
424 5
            '-+-+-+-+-+-+-+-',
425 5
            '-+-+-+-+-+-+-+-+-',
426 5
        ),
427
        $str
428 5
    );
429 5
    $str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
430 5
    $str = htmlentities($str, ENT_QUOTES, 'UTF-8');
431 5
    $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
432 5
    $str = str_replace(' ', '-', $str);
433 5
    $str = rawurlencode($str);
434 5
    $str = str_replace('%', '-', $str);
435
436 5
    return $str;
437
  }
438
439
  /**
440
   * @return mixed
441
   */
442 33
  public function getPrefix()
443
  {
444 33
    return $this->prefix;
445
  }
446
447
  /**
448
   * set prefix [WARNING: do not use if you don't know what you do]
449
   *
450
   * @param string $prefix
451
   */
452 39
  public function setPrefix($prefix)
453
  {
454 39
    $this->prefix = (string)$prefix;
455 39
  }
456
457
  /**
458
   * set cache-item by key => value + date
459
   *
460
   * @param string    $key
461
   * @param mixed     $value
462
   * @param \DateTime $date
463
   *
464
   * @return mixed|void
465
   * @throws \Exception
466
   */
467 7
  public function setItemToDate($key, $value, \DateTime $date)
468
  {
469 7
    $ttl = $date->getTimestamp() - time();
470
471 7
    if ($ttl <= 0) {
472 1
      throw new \Exception('Date in the past.');
473
    }
474
475 6
    $storeKey = $this->calculateStoreKey($key);
476
477 6
    return $this->setItem($storeKey, $value, $ttl);
478
  }
479
480
  /**
481
   * set cache-item by key => value + ttl
482
   *
483
   * @param string $key
484
   * @param mixed  $value
485
   * @param int    $ttl
486
   *
487
   * @return bool
488
   */
489 18
  public function setItem($key, $value, $ttl = 0)
490
  {
491
    if (
492 18
        $this->adapter instanceof iAdapter
493 18
        &&
494 18
        $this->serializer instanceof iSerializer
495 18
    ) {
496 18
      $storeKey = $this->calculateStoreKey($key);
497 18
      $serialized = $this->serializer->serialize($value);
498
499 18
      if ($ttl) {
500 8
        return $this->adapter->setExpired($storeKey, $serialized, $ttl);
501
      } else {
502 10
        return $this->adapter->set($storeKey, $serialized);
503
      }
504
    } else {
505
      return false;
506
    }
507
  }
508
509
  /**
510
   * remove cached-item
511
   *
512
   * @param string $key
513
   *
514
   * @return bool
515
   */
516 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...
517
  {
518 1
    if ($this->adapter instanceof iAdapter) {
519 1
      $storeKey = $this->calculateStoreKey($key);
520
521 1
      return $this->adapter->remove($storeKey);
522
    } else {
523
      return false;
524
    }
525
  }
526
527
  /**
528
   * check if cached-item exists
529
   *
530
   * @param string $key
531
   *
532
   * @return boolean
533
   */
534 6 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...
535
  {
536 6
    if ($this->adapter instanceof iAdapter) {
537 6
      $storeKey = $this->calculateStoreKey($key);
538
539 6
      return $this->adapter->exists($storeKey);
540
    } else {
541
      return false;
542
    }
543
  }
544
545
}
546