Test Failed
Pull Request — master (#2)
by Joao
02:20
created

ShmopCacheEngine   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 218
Duplicated Lines 2.29 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 5
loc 218
c 0
b 0
f 0
wmc 34
lcom 1
cbo 3
rs 9.2

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 4
A getFilenameToken() 0 4 1
A getMaxSize() 0 4 1
A getDefaultPermission() 0 4 1
A getFTok() 0 7 2
B get() 0 28 4
A isValidAge() 5 14 4
B set() 0 39 6
A delete() 0 13 2
A deleteFromFilenameToken() 0 20 4
A clear() 0 8 2
A has() 0 17 2
A isAvailable() 0 4 1

How to fix   Duplicated Code   

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:

1
<?php
2
3
namespace ByJG\Cache\Psr16;
4
5
use Psr\Cache\InvalidArgumentException;
6
use Psr\Log\NullLogger;
7
8
/**
9
 * Caching based on Unix Share Memory
10
 *
11
 * # ipcs -m
12
 * List all segments used
13
 *
14
 * # ipcs -lm
15
 * ------ Shared Memory Limits --------
16
 * max number of segments = 4096       <--- this is SHMMNI
17
 * max seg size (kbytes) = 67108864    <--- this is SHMMAX
18
 * max total shared memory (kbytes) = 17179869184<- this is SHMALL
19
 * min seg size (bytes) = 1
20
 *
21
 *
22
 */
23
class ShmopCacheEngine extends BaseCacheEngine
24
{
25
    protected $logger = null;
26
27
    protected $config = [];
28
29
    public function __construct($config = [], $logger = null)
30
    {
31
        $this->config = $config;
32
33
        if (!isset($this->config['max-size'])) {
34
            $this->config['max-size'] = 524288;
35
        }
36
        if (!isset($this->config['default-permission'])) {
37
            $this->config['default-permission'] = '0700';
38
        }
39
40
        $this->logger = $logger;
41
        if (is_null($logger)) {
42
            $this->logger = new NullLogger();
43
        }
44
    }
45
46
    protected function getFilenameToken($key)
47
    {
48
        return sys_get_temp_dir() . '/shmop-' . sha1($key) . '.cache';
49
    }
50
    
51
    protected function getMaxSize()
52
    {
53
        return $this->config['max-size'];
54
    }
55
56
    protected function getDefaultPermission()
57
    {
58
        return $this->config['default-permission'];
59
    }
60
61
    protected function getFTok($file)
62
    {
63
        if (!file_exists($file)) {
64
            touch($file);
65
        }
66
        return ftok($file, 'j');
67
    }
68
69
    /**
70
     * @param string $key The object KEY
71
     * @param mixed $default The time to live in seconds of the object. Depends on implementation.
72
     * @return mixed The Object
73
     */
74
    public function get($key, $default = null)
75
    {
76
       if ($default === false) {
77
            $this->logger->info("[Shmop Cache] Ignored  $key because TTL=FALSE");
78
            return $default;
79
        }
80
81
        $file = $this->getFilenameToken($key);
82
        $fileKey = $this->getFTok($file);
83
84
        // Opened
85
        $shm_id = @shmop_open($fileKey, "a", 0, 0);
86
        if (!$shm_id) {
87
            $this->logger->info("[Shmop Cache] '$key' not exists");
88
            return $default;
89
        }
90
91
        if (!$this->isValidAge($file)) {
92
            return $default;
93
        }
94
95
        $this->logger->info("[Shmop Cache] Get '$key'");
96
97
        $serialized = shmop_read($shm_id, 0, shmop_size($shm_id));
98
        shmop_close($shm_id);
99
100
        return unserialize($serialized);
101
    }
102
103
    protected function isValidAge($file)
104
    {
105
        if (file_exists("$file.ttl")) {
106
            $fileTtl = intval(file_get_contents("$file.ttl"));
107
        }
108
109 View Code Duplication
        if (!empty($fileTtl) && time() >= $fileTtl) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
110
            $this->logger->info("[Shmop Cache] File too old. Ignoring");
111
            $this->deleteFromFilenameToken($file);
112
            return false;
113
        }
114
115
        return true;
116
    }
117
118
    /**
119
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
120
     *
121
     * @param string $key The key of the item to store.
122
     * @param mixed $value The value of the item to store, must be serializable.
123
     * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
124
     *                                     the driver supports TTL then the library may set a default value
125
     *                                     for it or let the driver take care of that.
126
     * @return bool True on success and false on failure.
127
     * @throws InvalidArgumentException
128
     */
129
    public function set($key, $value, $ttl = null)
130
    {
131
        $this->logger->info("[Shmop Cache] set '$key'");
132
133
        $this->delete($key);
134
135
        $serialized = serialize($value);
136
        $size = strlen($serialized);
137
138
        if ($size > $this->getMaxSize()) {
139
            throw new \ByJG\Cache\InvalidArgumentException('Object is greater than the max size allowed: ' . $this->getMaxSize());
140
        }
141
142
        $file = $this->getFilenameToken($key);
143
        $shmKey = $this->getFTok($file);
144
        $shm_id = shmop_open($shmKey, "c", 0777, $size);
145
        if (!$shm_id) {
146
            $message = "Couldn't create shared memory segment";
147
            $lastError = error_get_last();
148
            if (isset($lastError['message'])) {
149
                $message = $lastError['message'];
150
            }
151
            throw new \ByJG\Cache\InvalidArgumentException($message);
152
        }
153
154
        $shm_bytes_written = shmop_write($shm_id, $serialized, 0);
155
        $this->logger->info("[Shmop Cache] set '$key' confirmed write $shm_bytes_written bytes of $size bytes");
156
        if ($shm_bytes_written != $size) {
157
            $this->logger->warning("Couldn't write the entire length of data");
158
        }
159
        shmop_close($shm_id);
160
161
        $validUntil = $this->addToNow($ttl);
162
        if (!empty($validUntil)) {
163
            file_put_contents("$file.ttl", $validUntil);
164
        }
165
166
        return true;
167
    }
168
169
    /**
170
     * @param string $key
171
     * @return bool
172
     */
173
    public function delete($key)
174
    {
175
        $this->logger->info("[Shmop Cache] release '$key'");
176
177
        if ($this->get($key) === false) {
178
            $this->logger->info("[Shmop Cache] release '$key' does not exists.");
179
            return false;
180
        }
181
182
        $file = $this->getFilenameToken($key);
183
        $this->deleteFromFilenameToken($file);
184
        return true;
185
    }
186
187
    private function deleteFromFilenameToken($file)
188
    {
189
        $filekey = $this->getFTok($file);
190
        $shm_id = @shmop_open($filekey, "w", 0, 0);
191
192
        if (file_exists($file)) {
193
            unlink($file);
194
        }
195
196
        if (file_exists("$file.ttl")) {
197
            unlink("$file.ttl");
198
        }
199
200
        if ($shm_id) {
201
            shmop_delete($shm_id);
202
            shmop_close($shm_id);
203
204
            $this->logger->info("[Shmop Cache] release confirmed.");
205
        }
206
    }
207
208
    public function clear()
209
    {
210
        $patternKey = sys_get_temp_dir() . '/shmop-*.cache';
211
        $list = glob($patternKey);
212
        foreach ($list as $file) {
213
            $this->deleteFromFilenameToken($file);
214
        }
215
    }
216
217
    public function has($key)
218
    {
219
        $file = $this->getFilenameToken($key);
220
        $fileKey = $this->getFTok($file);
221
222
        // Opened
223
        $shm_id = @shmop_open($fileKey, "a", 0, 0);
224
225
        $exists = !(!$shm_id);
226
227
        if ($exists) {
228
            shmop_close($shm_id);
229
            return $this->isValidAge($file);
230
        }
231
232
        return $exists;
233
    }
234
235
236
    public function isAvailable()
237
    {
238
        return function_exists('shmop_open');
239
    }
240
}
241