PhpFileCache   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 3
dl 0
loc 190
ccs 80
cts 80
cp 1
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A fetch() 0 11 2
A __construct() 0 13 1
A __destruct() 0 8 2
A toArray() 0 4 1
A pack() 0 20 4
B unpack() 0 20 5
A deleteAllStale() 0 8 3
A isStale() 0 7 2
A contains() 0 8 3
A save() 0 12 1
A delete() 0 9 2
A getStats() 0 15 2
1
<?php
2
3
namespace Hgraca\Cache\PhpFile;
4
5
use Hgraca\Cache\CacheInterface;
6
use Hgraca\Cache\Exception\CacheItemNotFoundException;
7
use Hgraca\Cache\PhpFile\Adapter\FileSystem\FileSystemAdapter;
8
use Hgraca\Cache\PhpFile\Port\FileSystem\FileSystemInterface;
9
use InvalidArgumentException;
10
11
final class PhpFileCache implements CacheInterface
12
{
13
    const TYPE_PERSISTENT = true;
14
    const TYPE_NOT_PERSISTENT = false;
15
16
    const KEY_LIFETIME = 'lifetime';
17
    const KEY_CREATION_TIME = 'creationTime';
18
    const KEY_DATA = 'data';
19
20
    /** @var bool */
21
    private $contentHasChanged = false;
22
23
    /** @var array */
24
    private $cache;
25
26
    /** @var string */
27
    private $cacheFilePath;
28
29
    /** @var int */
30
    private $mode;
31
32
    /** @var int */
33
    private $hits;
34
35
    /** @var int */
36
    private $misses;
37
38
    /** @var bool */
39
    private $persistent;
40
41
    /**
42
     * @var FileSystemInterface
43
     */
44
    private $fileSystem;
45
46 13
    public function __construct(
47
        string $cacheFileName,
48
        int $mode = self::MODE_VAR_EXPORT,
49
        bool $persistent = self::TYPE_PERSISTENT,
50
        FileSystemInterface $fileSystem = null
51
    ) {
52 13
        $this->cacheFilePath = $cacheFileName;
53 13
        $this->mode = $mode;
54 13
        $this->persistent = $persistent;
55 13
        $this->fileSystem = $fileSystem ?? new FileSystemAdapter();
56
57 13
        $this->cache = $this->unpack();
58 13
    }
59
60
    /**
61
     * Saves its data in the cache file at destruction time, if it has changed
62
     */
63 13
    public function __destruct()
64
    {
65 13
        $this->deleteAllStale();
66
67 13
        if ($this->contentHasChanged) {
68 10
            $this->pack();
69
        }
70 12
    }
71
72
    /**
73
     * @throws CacheItemNotFoundException
74
     *
75
     * @return mixed
76
     */
77 4
    public function fetch(string $id)
78
    {
79 4
        if (!$this->contains($id)) {
80 1
            ++$this->misses;
81
82 1
            throw new CacheItemNotFoundException();
83
        }
84 3
        ++$this->hits;
85
86 3
        return $this->cache[$id][self::KEY_DATA];
87
    }
88
89 7
    public function contains(string $id): bool
90
    {
91 7
        if (!array_key_exists($id, $this->cache) || $this->isStale($id)) {
92 4
            return false;
93
        }
94
95 5
        return true;
96
    }
97
98 10
    public function save(string $id, $data, int $lifeTime = 0, int $creationTime = null): bool
99
    {
100 10
        $this->cache[$id] = [
101 10
            self::KEY_DATA => $data,
102 10
            self::KEY_LIFETIME => $lifeTime,
103 10
            self::KEY_CREATION_TIME => $creationTime ?? time(),
104
        ];
105
106 10
        $this->contentHasChanged = true;
107
108 10
        return true;
109
    }
110
111 3
    public function delete(string $id): bool
112
    {
113 3
        if (isset($this->cache[$id])) {
114 3
            unset($this->cache[$id]);
115 3
            $this->contentHasChanged = true;
116
        }
117
118 3
        return true;
119
    }
120
121 7
    public function getStats(): array
122
    {
123 7
        $this->deleteAllStale();
124
125
        return [
126 7
            static::STATS_HITS => $this->hits,
127 7
            static::STATS_MISSES => $this->misses,
128 7
            static::STATS_UPTIME => $this->fileSystem->fileExists($this->cacheFilePath)
129 1
                ? $this->fileSystem->getFileCreationTimestamp($this->cacheFilePath) - time()
130 7
                : 0,
131 7
            static::STATS_MEMORY_USAGE => memory_get_usage(),
132 7
            static::STATS_MEMORY_AVAILABLE => ini_get('memory_limit') - memory_get_usage(),
133 7
            static::STATS_ITEM_COUNT => count($this->cache),
134
        ];
135
    }
136
137 4
    private function toArray(): array
138
    {
139 4
        return $this->cache;
140
    }
141
142 10
    private function pack()
143
    {
144 10
        if (!$this->persistent) {
145 5
            return null;
146
        }
147
148 5
        switch ($this->mode) {
149 5
            case self::MODE_VAR_EXPORT:
150 2
                $this->fileSystem->writeFile(
151 2
                    $this->cacheFilePath,
152 2
                    '<?php $array = ' . var_export($this->toArray(), true) . ';'
153
                );
154 2
                break;
155 3
            case self::MODE_SERIALIZER:
156 2
                $this->fileSystem->writeFile($this->cacheFilePath, serialize($this->toArray()));
157 2
                break;
158
            default:
159 1
                throw new InvalidArgumentException('Serialization mode unknown: ' . $this->mode);
160
        }
161 4
    }
162
163 13
    private function unpack(): array
164
    {
165 13
        if (!$this->persistent || !$this->fileSystem->fileExists($this->cacheFilePath)) {
166 13
            return [];
167
        }
168
169 4
        switch ($this->mode) {
170 4
            case self::MODE_VAR_EXPORT:
171 2
                $array = [];
172 2
                include $this->cacheFilePath;
173
174 2
                return $array;
175
176 2
            case self::MODE_SERIALIZER:
177 1
                return unserialize($this->fileSystem->readFile($this->cacheFilePath));
178
179
            default:
180 1
                throw new InvalidArgumentException('Serialization mode unknown: ' . $this->mode);
181
        }
182
    }
183
184 13
    private function deleteAllStale()
185
    {
186 13
        foreach ($this->cache as $id => $item) {
187 9
            if ($this->isStale($id)) {
188 9
                $this->delete($id);
189
            }
190
        }
191 13
    }
192
193 10
    private function isStale(string $id)
194
    {
195 10
        $item = $this->cache[$id];
196
197 10
        return (0 !== $item[self::KEY_LIFETIME]) &&
198 10
            (time() - $item[self::KEY_CREATION_TIME] >= $item[self::KEY_LIFETIME]);
199
    }
200
}
201