StampedeCallbackAdapter::needsPregeneration()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
crap 3
1
<?php
2
namespace Genkgo\Cache\Adapter;
3
4
use DateInterval;
5
use DateTime;
6
use DateTimeImmutable;
7
use Exception;
8
use Genkgo\Cache\CacheAdapterInterface;
9
use Genkgo\Cache\CallbackCacheInterface;
10
11
/**
12
 * Class StampedeCallbackAdapter
13
 * @package Genkgo\Cache
14
 */
15
class StampedeCallbackAdapter implements CallbackCacheInterface
16
{
17
    /**
18
     * @var CacheAdapterInterface
19
     */
20
    private $cache;
21
22
    /**
23
     * @var int
24
     */
25
    private $pregenerateIn;
26
27
    /**
28
     * @var bool
29
     */
30
    private $useInvalidDataOnException = false;
31
32
    /**
33
     * @param CacheAdapterInterface $cache
34
     * @param $pregenerateInSeconds
35
     * @param bool $useInvalidDataOnException
36
     */
37 5
    public function __construct(
38
        CacheAdapterInterface $cache,
39
        $pregenerateInSeconds,
40
        $useInvalidDataOnException = false
41
    ) {
42 5
        $this->cache = $cache;
43 5
        $this->pregenerateIn  = $pregenerateInSeconds;
44 5
        $this->useInvalidDataOnException = $useInvalidDataOnException;
45 5
    }
46
47
    /**
48
     * @deprecated
49
     */
50 1
    public function useInvalidDataOnException()
51
    {
52 1
        $this->useInvalidDataOnException = true;
53 1
    }
54
55
    /**
56
     * @param $key
57
     * @param callable $cb
58
     * @return mixed|null
59
     * @throws Exception
60
     */
61 5
    public function get($key, callable $cb)
62
    {
63 5
        $currentItem = $this->cache->get($key);
64 5
        if ($currentItem === null || $this->needsPregeneration($key) === true) {
65 5
            $this->lock($key);
66 5
            $item = $this->pregenerate($key, $cb, $currentItem);
67 4
            $this->cache->set($key, $item);
68 4
            $this->unlock($key);
69 4
            return $item;
70
        } else {
71 1
            return $currentItem;
72
        }
73
    }
74
75
    /**
76
     * @param $key
77
     * @return bool
78
     */
79 3
    private function needsPregeneration($key)
80
    {
81 3
        $regenerateOn = $this->cache->get('sp' . $key);
82 3
        if ($regenerateOn === null) {
83 1
            return true;
84
        }
85
86 2
        if ($regenerateOn === 'locked') {
87 1
            return false;
88
        }
89
90 2
        $regenerateOn = DateTimeImmutable::createFromFormat(DateTime::ISO8601, $regenerateOn);
91 2
        return ($regenerateOn <= new DateTimeImmutable('now'));
92
    }
93
94
    /**
95
     * @param $key
96
     * @param callable $cb
97
     * @param $currentItem
98
     * @return mixed
99
     * @throws Exception
100
     */
101 5
    private function pregenerate($key, callable $cb, $currentItem)
102
    {
103
        try {
104 5
            $item = $cb();
105 5
        } catch (Exception $e) {
106 2
            if ($this->useInvalidDataOnException && $currentItem !== null) {
107 1
                $item = $currentItem;
108 1
            } else {
109 1
                $this->unlock($key, 0);
110 1
                throw $e;
111
            }
112
        }
113
114 4
        return $item;
115
    }
116
117
    /**
118
     * @param $key
119
     */
120 5
    private function lock($key)
121
    {
122 5
        $this->cache->set('sp' . $key, 'locked');
123 5
    }
124
125
    /**
126
     * @param $key
127
     * @param integer $pregenerateIn
128
     */
129 5
    private function unlock($key, $pregenerateIn = null)
130
    {
131 5
        if ($pregenerateIn === null) {
132 4
            $pregenerateIn = $this->pregenerateIn;
133 4
        }
134
135 5
        $interval = new DateInterval('PT' . $pregenerateIn . 'S');
136 5
        $regeneratedOn = (new DateTimeImmutable('now'))->add($interval)->format(DateTime::ISO8601);
137 5
        $this->cache->set('sp' . $key, $regeneratedOn);
138 5
    }
139
}
140