Completed
Push — master ( 386972...cf64d5 )
by Lars
02:26 queued 46s
created

Cache::removeItem()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.2596

Importance

Changes 3
Bugs 1 Features 1
Metric Value
dl 0
loc 23
ccs 8
cts 14
cp 0.5714
rs 8.7972
c 3
b 1
f 1
cc 4
eloc 13
nc 3
nop 1
crap 5.2596
1
<?php
2
3
namespace voku\cache;
4
5
use voku\cache\Exception\InvalidArgumentException;
6
7
/**
8
 * Cache: global-cache class
9
 *
10
 * can use different cache-adapter:
11
 * - Redis
12
 * - Memcache / Memcached
13
 * - APC / APCu
14
 * - Xcache
15
 * - Array
16
 * - File
17
 *
18
 * @package   voku\cache
19
 */
20
class Cache implements iCache
21
{
22
23
  /**
24
   * @var iAdapter
25
   */
26
  protected $adapter;
27
28
  /**
29
   * @var iSerializer
30
   */
31
  protected $serializer;
32
33
  /**
34
   * @var string
35
   */
36
  protected $prefix = '';
37
38
  /**
39
   * @var bool
40
   */
41
  protected $isReady = false;
42
43
  /**
44
   * @var bool
45
   */
46
  protected $isActive = true;
47
48
  /**
49
   * @var mixed no cache, if admin-session is set
50
   */
51
  protected $isAdminSession = false;
52
53
  /**
54
   * @var array
55
   */
56
  protected static $STATIC_CACHE = array();
57
58
  /**
59
   * @var array
60
   */
61
  protected static $STATIC_CACHE_EXPIRE = array();
62
63
  /**
64
   * @var array
65
   */
66
  protected static $STATIC_CACHE_COUNTER = array();
67
68
  /**
69
   * @var int
70
   */
71
  protected $staticCacheHitCounter = 10;
72
73
  /**
74
   * __construct
75
   *
76
   * @param null|iAdapter    $adapter
77
   * @param null|iSerializer $serializer
78
   * @param boolean          $checkForUser   check for dev-ip or if cms-user is logged-in
79
   * @param boolean          $cacheEnabled   false will disable the cache (use it e.g. for global settings)
80
   * @param string|boolean   $isAdminSession set a user-id, if the user is a admin (so we can disable cache for this
81
   *                                         user)
82
   */
83 51
  public function __construct(iAdapter $adapter = null, iSerializer $serializer = null, $checkForUser = true, $cacheEnabled = true, $isAdminSession = false)
84
  {
85 51
    $this->isAdminSession = $isAdminSession;
86
87
    // First check if the cache is active at all.
88 51
    $this->setActive($cacheEnabled);
89
    if (
90 51
        $this->isActive === true
91 51
        &&
92
        $checkForUser === true
93 51
    ) {
94
      $this->setActive($this->isCacheActiveForTheCurrentUser());
95
    }
96
97
    // If the cache is active, then try to auto-connect to the best possible cache-system.
98 51
    if ($this->isActive === true) {
99
100 51
      $this->setPrefix($this->getTheDefaultPrefix());
101
102
      if (
103
          $adapter === null
104 51
          ||
105 51
          !is_object($adapter)
106 51
          ||
107
          !$adapter instanceof iAdapter
108 51
      ) {
109
        $adapter = $this->autoConnectToAvailableCacheSystem();
110
      }
111
112
      // INFO: Memcache(d) has his own "serializer", so don't use it twice
113 51
      if (!is_object($serializer) && $serializer === null) {
114
        if (
115
            $adapter instanceof AdapterMemcached
116
            ||
117
            $adapter instanceof AdapterMemcache
118
        ) {
119
          $serializer = new SerializerNo();
120
        } else {
121
          // set default serializer
122
          $serializer = new SerializerIgbinary();
123
        }
124
      }
125 51
    }
126
127
    // Final checks ...
128
    if (
129
        $serializer instanceof iSerializer
130 51
        &&
131
        $adapter instanceof iAdapter
132 51
    ) {
133 51
      $this->setCacheIsReady(true);
134
135 51
      $this->adapter = $adapter;
136 51
      $this->serializer = $serializer;
137 51
    }
138 51
  }
139
140
  /**
141
   * enable / disable the cache
142
   *
143
   * @param boolean $isActive
144
   */
145 51
  public function setActive($isActive)
146
  {
147 51
    $this->isActive = (boolean)$isActive;
148 51
  }
149
150
  /**
151
   * check if the current use is a admin || dev || server == client
152
   *
153
   * @return bool
154
   */
155
  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...
156
  {
157
    $active = true;
158
159
    // test the cache, with this GET-parameter
160
    $testCache = isset($_GET['testCache']) ? (int)$_GET['testCache'] : 0;
161
162
    if ($testCache != 1) {
163
      if (
164
          // admin is logged-in
165
          $this->isAdminSession
166
          ||
167
          // server == client
168
          (
169
              isset($_SERVER['SERVER_ADDR'])
170
              &&
171
              $_SERVER['SERVER_ADDR'] == $this->getClientIp()
172
          )
173
          ||
174
          // user is a dev
175
          $this->checkForDev() === true
176
      ) {
177
        $active = false;
178
      }
179
    }
180
181
    return $active;
182
  }
183
184
  /**
185
   * returns the IP address of the client
186
   *
187
   * @param   bool $trust_proxy_headers   Whether or not to trust the
188
   *                                      proxy headers HTTP_CLIENT_IP
189
   *                                      and HTTP_X_FORWARDED_FOR. ONLY
190
   *                                      use if your $_SERVER is behind a
191
   *                                      proxy that sets these values
192
   *
193
   * @return  string
194
   */
195
  protected 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...
196
  {
197
    $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'NO_REMOTE_ADDR';
198
199
    if ($trust_proxy_headers) {
200
      return $remoteAddr;
201
    }
202
203
    if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP']) {
204
      $ip = $_SERVER['HTTP_CLIENT_IP'];
205
    } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR']) {
206
      $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
207
    } else {
208
      $ip = $remoteAddr;
209
    }
210
211
    return $ip;
212
  }
213
214
  /**
215
   * Check for local developer.
216
   *
217
   * @return bool
218
   */
219
  protected 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...
220
  {
221
    $return = false;
222
223
    if (function_exists('checkForDev')) {
224
      $return = checkForDev();
225
    } else {
226
227
      // for testing with dev-address
228
      $noDev = isset($_GET['noDev']) ? (int)$_GET['noDev'] : 0;
229
      $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'NO_REMOTE_ADDR';
230
231
      if (
232
          $noDev != 1
233
          &&
234
          (
235
              $remoteAddr === '127.0.0.1'
236
              ||
237
              $remoteAddr === '::1'
238
              ||
239
              PHP_SAPI === 'cli'
240
          )
241
      ) {
242
        $return = true;
243
      }
244
    }
245
246
    return $return;
247
  }
248
249
  /**
250
   * Set the default-prefix via "SERVER"-var + "SESSION"-language.
251
   */
252 51
  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...
253
  {
254 51
    return (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '') . '_' .
255 51
           (isset($_SERVER['THEME']) ? $_SERVER['THEME'] : '') . '_' .
256 51
           (isset($_SERVER['STAGE']) ? $_SERVER['STAGE'] : '') . '_' .
257 51
           (isset($_SESSION['language']) ? $_SESSION['language'] : '') . '_' .
258 51
           (isset($_SESSION['language_extra']) ? $_SESSION['language_extra'] : '');
259
  }
260
261
  /**
262
   * Auto-connect to the available cache-system on the server.
263
   *
264
   * @return iAdapter
265
   */
266
  protected function autoConnectToAvailableCacheSystem()
267
  {
268
    static $adapterCache;
269
270
    if (is_object($adapterCache) && $adapterCache instanceof iAdapter) {
271
      return $adapterCache;
272
    }
273
274
    $memcached = null;
275
    $isMemcachedAvailable = false;
276
    if (extension_loaded('memcached')) {
277
      $memcached = new \Memcached();
278
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
279
      $isMemcachedAvailable = @$memcached->addServer('127.0.0.1', 11211);
280
    }
281
282
    if ($isMemcachedAvailable === false) {
283
      $memcached = null;
284
    }
285
286
    $adapterMemcached = new AdapterMemcached($memcached);
287
    if ($adapterMemcached->installed() === true) {
288
289
      // -------------------------------------------------------------
290
      // "Memcached"
291
      // -------------------------------------------------------------
292
      $adapter = $adapterMemcached;
293
294
    } else {
295
296
      $memcache = null;
297
      $isMemcacheAvailable = false;
298
      if (class_exists('\Memcache')) {
299
        $memcache = new \Memcache;
300
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
301
        $isMemcacheAvailable = @$memcache->connect('127.0.0.1', 11211);
302
      }
303
304
      if ($isMemcacheAvailable === false) {
305
        $memcache = null;
306
      }
307
308
      $adapterMemcache = new AdapterMemcache($memcache);
309
      if ($adapterMemcache->installed() === true) {
310
311
        // -------------------------------------------------------------
312
        // "Memcache"
313
        // -------------------------------------------------------------
314
        $adapter = $adapterMemcache;
315
316
      } else {
317
318
        $redis = null;
319
        $isRedisAvailable = false;
320
        if (
321
            extension_loaded('redis')
322
            &&
323
            class_exists('\Predis\Client')
324
        ) {
325
          /** @noinspection PhpUndefinedNamespaceInspection */
326
          /** @noinspection PhpUndefinedClassInspection */
327
          $redis = new \Predis\Client(
328
              array(
329
                  'scheme'  => 'tcp',
330
                  'host'    => '127.0.0.1',
331
                  'port'    => 6379,
332
                  'timeout' => '2.0',
333
              )
334
          );
335
          try {
336
            $redis->connect();
337
            $isRedisAvailable = $redis->getConnection()->isConnected();
338
          } catch (\Exception $e) {
339
            // nothing
340
          }
341
        }
342
343
        if ($isRedisAvailable === false) {
344
          $redis = null;
345
        }
346
347
        $adapterRedis = new AdapterPredis($redis);
348
        if ($adapterRedis->installed() === true) {
349
350
          // -------------------------------------------------------------
351
          // Redis
352
          // -------------------------------------------------------------
353
          $adapter = $adapterRedis;
354
355
        } else {
356
357
          $adapterXcache = new AdapterXcache();
358
          if ($adapterXcache->installed() === true) {
359
360
            // -------------------------------------------------------------
361
            // "Xcache"
362
            // -------------------------------------------------------------
363
            $adapter = $adapterXcache;
364
365
          } else {
366
367
            $adapterApc = new AdapterApc();
368
            if ($adapterApc->installed() === true) {
369
370
              // -------------------------------------------------------------
371
              // "APC"
372
              // -------------------------------------------------------------
373
              $adapter = $adapterApc;
374
375
            } else {
376
377
              $adapterApcu = new AdapterApcu();
378
              if ($adapterApcu->installed() === true) {
379
380
                // -------------------------------------------------------------
381
                // "APCu"
382
                // -------------------------------------------------------------
383
                $adapter = $adapterApcu;
384
385
              } else {
386
387
                $adapterFile = new AdapterFile();
388
                if ($adapterFile->installed() === true) {
389
390
                  // -------------------------------------------------------------
391
                  // File-Cache
392
                  // -------------------------------------------------------------
393
                  $adapter = $adapterFile;
394
395
                } else {
396
397
                  // -------------------------------------------------------------
398
                  // Static-PHP-Cache
399
                  // -------------------------------------------------------------
400
                  $adapter = new AdapterArray();
401
                }
402
              }
403
            }
404
          }
405
        }
406
      }
407
    }
408
409
    // save to static cache
410
    $adapterCache = $adapter;
411
412
    return $adapter;
413
  }
414
415
  /**
416
   * Set "isReady" state.
417
   *
418
   * @param boolean $isReady
419
   */
420 51
  protected function setCacheIsReady($isReady)
421
  {
422 51
    $this->isReady = (boolean)$isReady;
423 51
  }
424
425
  /**
426
   * Get the "isReady" state.
427
   *
428
   * @return boolean
429
   */
430 4
  public function getCacheIsReady()
431
  {
432 4
    return $this->isReady;
433
  }
434
435
  /**
436
   * Get cached-item by key.
437
   *
438
   * @param string $key
439
   * @param int    $forceStaticCacheHitCounter
440
   *
441
   * @return mixed
442
   */
443 22
  public function getItem($key, $forceStaticCacheHitCounter = 0)
444
  {
445
    // init
446 22
    $forceStaticCacheHitCounter = (int)$forceStaticCacheHitCounter;
447
448 22
    if (!$this->adapter instanceof iAdapter) {
449
      return null;
450
    }
451
452 22
    $storeKey = $this->calculateStoreKey($key);
453
454
    // check if we already using static-cache
455 22
    $useStaticCache = true;
456 22
    if ($this->adapter instanceof AdapterArray) {
457 6
      $useStaticCache = false;
458 6
    }
459
460 22
    if (!isset(self::$STATIC_CACHE_COUNTER[$storeKey])) {
461 13
      self::$STATIC_CACHE_COUNTER[$storeKey] = 0;
462 13
    }
463
464
    // get from static-cache
465
    if (
466
        $useStaticCache === true
467 22
        &&
468 16
        $this->checkForStaticCache($storeKey) === true
469 22
    ) {
470 4
      return self::$STATIC_CACHE[$storeKey];
471
    }
472
473 20
    $serialized = $this->adapter->get($storeKey);
474 20
    $value = $serialized ? $this->serializer->unserialize($serialized) : null;
475
476 20
    self::$STATIC_CACHE_COUNTER[$storeKey]++;
477
478
    // save into static-cache if needed
479
    if (
480
        $useStaticCache === true
481 20
        &&
482
        (
483
          (
484
            $forceStaticCacheHitCounter !== 0
485 14
            &&
486
            self::$STATIC_CACHE_COUNTER[$storeKey] >= $forceStaticCacheHitCounter
487
          )
488
          ||
489
          (
490 14
            $this->staticCacheHitCounter !== 0
491 14
            &&
492 14
            self::$STATIC_CACHE_COUNTER[$storeKey] >= $this->staticCacheHitCounter
493 14
          )
494 14
        )
495 20
    ) {
496 3
      self::$STATIC_CACHE[$storeKey] = $value;
497 3
    }
498
499 20
    return $value;
500
  }
501
502
  /**
503
   * Calculate store-key (prefix + $rawKey).
504
   *
505
   * @param string $rawKey
506
   *
507
   * @return string
508
   */
509 42
  protected function calculateStoreKey($rawKey)
510
  {
511 42
    $str = $this->getPrefix() . $rawKey;
512
513 42
    if ($this->adapter instanceof AdapterFile) {
514 8
      $str = $this->cleanStoreKey($str);
515 8
    }
516
517 42
    return $str;
518
  }
519
520
  /**
521
   * Clean store-key (required e.g. for the "File"-Adapter).
522
   *
523
   * @param string $str
524
   *
525
   * @return string
526
   */
527 8
  protected function cleanStoreKey($str)
528
  {
529 8
    $str = preg_replace("/[\r\n\t ]+/", ' ', $str);
530 8
    $str = str_replace(
531 8
        array('"', '*', ':', '<', '>', '?', "'", '|'),
532
        array(
533 8
            '-+-',
534 8
            '-+-+-',
535 8
            '-+-+-+-',
536 8
            '-+-+-+-+-',
537 8
            '-+-+-+-+-+-',
538 8
            '-+-+-+-+-+-+-',
539 8
            '-+-+-+-+-+-+-+-',
540 8
            '-+-+-+-+-+-+-+-+-',
541 8
        ),
542
        $str
543 8
    );
544 8
    $str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
545 8
    $str = htmlentities($str, ENT_QUOTES, 'UTF-8');
546 8
    $str = preg_replace('/(&)([a-z])([a-z]+;)/i', '$2', $str);
547 8
    $str = str_replace(' ', '-', $str);
548 8
    $str = rawurlencode($str);
549 8
    $str = str_replace('%', '-', $str);
550
551 8
    return $str;
552
  }
553
554
  /**
555
   * Get the prefix.
556
   *
557
   * @return string
558
   */
559 42
  public function getPrefix()
560
  {
561 42
    return $this->prefix;
562
  }
563
564
  /**
565
   * !!! Set the prefix. !!!
566
   *
567
   * WARNING: Do not use if you don't know what you do. Because this will overwrite the default prefix.
568
   *
569
   * @param string $prefix
570
   */
571 51
  public function setPrefix($prefix)
572
  {
573 51
    $this->prefix = (string)$prefix;
574 51
  }
575
576
  /**
577
   * Get the current value, when the static cache is used.
578
   *
579
   * @return int
580
   */
581
  public function getStaticCacheHitCounter()
582
  {
583
    return $this->staticCacheHitCounter;
584
  }
585
586
  /**
587
   * Set the static-hit-counter: Who often do we hit the cache, before we use static cache?
588
   *
589
   * @param int $staticCacheHitCounter
590
   */
591
  public function setStaticCacheHitCounter($staticCacheHitCounter)
592
  {
593
    $this->staticCacheHitCounter = (int)$staticCacheHitCounter;
594
  }
595
596
  /**
597
   * Set cache-item by key => value + date.
598
   *
599
   * @param string    $key
600
   * @param mixed     $value
601
   * @param \DateTime $date <p>If the date is in the past, we will remove the existing cache-item.</p>
602
   *
603
   * @return boolean
604
   * @throws \Exception
605
   */
606 8
  public function setItemToDate($key, $value, \DateTime $date)
607
  {
608 8
    $ttl = $date->getTimestamp() - time();
609
610 8
    if ($ttl <= 0) {
611 1
      throw new InvalidArgumentException('Date in the past.');
612
    }
613
614 7
    $storeKey = $this->calculateStoreKey($key);
615
616 7
    return $this->setItem($storeKey, $value, $ttl);
617
  }
618
619
  /**
620
   * Set cache-item by key => value + ttl.
621
   *
622
   * @param string $key
623
   * @param mixed  $value
624
   * @param null|int|\DateInterval $ttl
625
   *
626
   * @return bool
627
   */
628 23
  public function setItem($key, $value, $ttl = 0)
629
  {
630
    if (
631 23
        !$this->adapter instanceof iAdapter
632 23
        ||
633 23
        !$this->serializer instanceof iSerializer
634 23
    ) {
635
      return false;
636
    }
637
638 23
    $storeKey = $this->calculateStoreKey($key);
639 23
    $serialized = $this->serializer->serialize($value);
640
641
    // update static-cache, if it's exists
642 23
    if (array_key_exists($storeKey, self::$STATIC_CACHE) === true) {
643 3
      self::$STATIC_CACHE[$storeKey] = $value;
644 3
    }
645
646 23
    if ($ttl) {
647
648 11
      if ($ttl instanceof \DateInterval) {
649
        // Converting to a TTL in seconds
650 1
        $ttl = (new \DateTime('now'))->add($ttl)->getTimestamp() - time();
651 1
      }
652
653
      // always cache the TTL time, maybe we need this later ...
654 11
      self::$STATIC_CACHE_EXPIRE[$storeKey] = ($ttl ? (int)$ttl + time() : 0);
655
656 11
      return $this->adapter->setExpired($storeKey, $serialized, $ttl);
657
    }
658
659 12
    return $this->adapter->set($storeKey, $serialized);
660
  }
661
662
  /**
663
   * Remove a cached-item.
664
   *
665
   * @param string $key
666
   *
667
   * @return bool
668
   */
669 3
  public function removeItem($key)
670
  {
671 3
    if (!$this->adapter instanceof iAdapter) {
672
      return false;
673
    }
674
675 3
    $storeKey = $this->calculateStoreKey($key);
676
677
    // remove static-cache
678
    if (
679 3
        !empty(self::$STATIC_CACHE)
680 3
        &&
681 1
        array_key_exists($storeKey, self::$STATIC_CACHE) === true
682 3
    ) {
683
      unset(
684
          self::$STATIC_CACHE[$storeKey],
685
          self::$STATIC_CACHE_COUNTER[$storeKey],
686
          self::$STATIC_CACHE_EXPIRE[$storeKey]
687
      );
688
    }
689
690 3
    return $this->adapter->remove($storeKey);
691
  }
692
693
  /**
694
   * Remove all cached-items.
695
   *
696
   * @return bool
697
   */
698 1
  public function removeAll()
699
  {
700 1
    if (!$this->adapter instanceof iAdapter) {
701
      return false;
702
    }
703
704
    // remove static-cache
705 1
    if (!empty(self::$STATIC_CACHE)) {
706 1
      self::$STATIC_CACHE = array();
707 1
      self::$STATIC_CACHE_COUNTER = array();
708 1
      self::$STATIC_CACHE_EXPIRE = array();
709 1
    }
710
711 1
    return $this->adapter->removeAll();
712
  }
713
714
  /**
715
   * Check if cached-item exists.
716
   *
717
   * @param string $key
718
   *
719
   * @return boolean
720
   */
721 10
  public function existsItem($key)
722
  {
723 10
    if (!$this->adapter instanceof iAdapter) {
724
      return false;
725
    }
726
727 10
    $storeKey = $this->calculateStoreKey($key);
728
729
    // check static-cache
730 10
    if ($this->checkForStaticCache($storeKey) === true) {
731
      return true;
732
    }
733
734 10
    return $this->adapter->exists($storeKey);
735
  }
736
737
  /**
738
   * @param string $storeKey
739
   *
740
   * @return bool
741
   */
742 26
  protected function checkForStaticCache($storeKey)
743
  {
744
    if (
745 26
        !empty(self::$STATIC_CACHE)
746 26
        &&
747 17
        array_key_exists($storeKey, self::$STATIC_CACHE) === true
748 26
        &&
749 6
        array_key_exists($storeKey, self::$STATIC_CACHE_EXPIRE) === true
750 26
        &&
751 4
        time() <= self::$STATIC_CACHE_EXPIRE[$storeKey]
752 26
    ) {
753 4
      return true;
754
    }
755
756 24
    return false;
757
  }
758
759
  /**
760
   * Get the current adapter class-name.
761
   *
762
   * @return string
763
   */
764 2
  public function getUsedAdapterClassName()
765
  {
766 2
    return get_class($this->adapter);
767
  }
768
769
  /**
770
   * Get the current serializer class-name.
771
   *
772
   * @return string
773
   */
774 2
  public function getUsedSerializerClassName()
775
  {
776 2
    return get_class($this->serializer);
777
  }
778
}
779