Completed
Pull Request — master (#1)
by Joao
02:45
created

ShmopCacheEngine::release()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
c 0
b 0
f 0
rs 8.5806
cc 4
eloc 14
nc 5
nop 1
1
<?php
2
3
namespace ByJG\Cache\Engine;
4
5
use ByJG\Cache\CacheEngineInterface;
6
use InvalidArgumentException;
7
use Psr\Log\NullLogger;
8
9
/**
10
 * Caching based on Unix Share Memory
11
 *
12
 * # ipcs -m
13
 * List all segments used
14
 *
15
 * # ipcs -lm
16
 * ------ Shared Memory Limits --------
17
 * max number of segments = 4096       <--- this is SHMMNI
18
 * max seg size (kbytes) = 67108864    <--- this is SHMMAX
19
 * max total shared memory (kbytes) = 17179869184<- this is SHMALL
20
 * min seg size (bytes) = 1
21
 *
22
 *
23
 */
24
class ShmopCacheEngine implements CacheEngineInterface
25
{
26
27
    protected $logger = null;
28
29
    protected $config = [];
30
31
    public function __construct($config = [], $logger = null)
32
    {
33
        $this->config = $config;
34
35
        if (!isset($this->config['max-size'])) {
36
            $this->config['max-size'] = 524288;
37
        }
38
        if (!isset($this->config['default-permission'])) {
39
            $this->config['default-permission'] = '0700';
40
        }
41
42
        $this->logger = $logger;
43
        if (is_null($logger)) {
44
            $this->logger = new NullLogger();
45
        }
46
    }
47
48
    protected function getFTok($key)
49
    {
50
        return sys_get_temp_dir() . '/' . sha1($key);
51
    }
52
    
53
    protected function getMaxSize()
54
    {
55
        return $this->config['max-size'];
56
    }
57
58
    protected function getDefaultPermission()
59
    {
60
        return $this->config['default-permission'];
61
    }
62
63
    protected function getKeyId($key)
64
    {
65
        $file = $this->getFTok($key);
66
        if (!file_exists($file)) {
67
            touch($file);
68
        }
69
        return ftok($file, 'j');
70
    }
71
72
    /**
73
     * @param string $key The object KEY
74
     * @param int $ttl The time to live in seconds of the object. Depends on implementation.
75
     * @return object The Object
76
     */
77
    public function get($key, $ttl = 0)
78
    {
79
        
80
81
        if ($ttl === false) {
82
            $this->logger->info("[Shmop Cache] Ignored  $key because TTL=FALSE");
83
            return null;
84
        }
85
86
        $fileKey = $this->getKeyId($key);
87
88
        // Opened
89
        $shm_id = @shmop_open($fileKey, "a", 0, 0);
90
        if (!$shm_id) {
91
            $this->logger->info("[Shmop Cache] '$key' not exists");
92
            return null;
93
        }
94
95
        $fileAge = filemtime($this->getFTok($key));
96
97
        // Check
98
        if (($ttl > 0) && (intval(time() - $fileAge) > $ttl)) {
99
            $this->logger->info("[Shmop Cache] File too old. Ignoring '$key'");
100
101
            // Close old descriptor
102
            shmop_close($shm_id);
103
104
            // delete old memory segment
105
            $shm_id = shmop_open($fileKey, "w", $this->getDefaultPermission(), $this->getMaxSize());
106
            shmop_delete($shm_id);
107
            shmop_close($shm_id);
108
            return null;
109
        }
110
111
        $this->logger->info("[Shmop Cache] Get '$key'");
112
113
        $serialized = shmop_read($shm_id, 0, shmop_size($shm_id));
114
        shmop_close($shm_id);
115
116
        return unserialize($serialized);
117
    }
118
119
    /**
120
     * @param string $key The object Key
121
     * @param object $object The object to be cached
122
     * @param int $ttl The time to live in seconds of the object. Depends on implementation.
123
     * @return bool If the object is successfully posted
124
     * @throws \Exception
125
     */
126
    public function set($key, $object, $ttl = 0)
127
    {
128
        $this->logger->info("[Shmop Cache] set '$key'");
129
130
        $this->release($key);
131
132
        $serialized = serialize($object);
133
        $size = strlen($serialized);
134
135
        if ($size > $this->getMaxSize()) {
136
            throw new \Exception('Object is greater than the max size allowed: ' . $this->getMaxSize());
137
        }
138
139
        $shmKey = $this->getKeyId($key);
140
        $shm_id = shmop_open($shmKey, "c", 0777, $size);
141
        if (!$shm_id) {
142
            $message = "Couldn't create shared memory segment";
143
            $lastError = error_get_last();
144
            if (isset($lastError['message'])) {
145
                $message = $lastError['message'];
146
            }
147
            throw new \Exception($message);
148
        }
149
150
        $shm_bytes_written = shmop_write($shm_id, $serialized, 0);
151
        $this->logger->info("[Shmop Cache] set '$key' confirmed write $shm_bytes_written bytes of $size bytes");
152
        if ($shm_bytes_written != $size) {
153
            $this->logger->warning("Couldn't write the entire length of data");
154
        }
155
        shmop_close($shm_id);
156
157
        return true;
158
    }
159
160
    /**
161
     * Append only will work with strings.
162
     *
163
     * @param string $key
164
     * @param string $str
165
     * @return bool
166
     */
167
    public function append($key, $str)
168
    {
169
        $old = $this->get($key);
170
        if ($old === false) {
171
            $this->set($key, $str);
0 ignored issues
show
Documentation introduced by
$str is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
172
        } else {
173
            $oldUn = unserialize($old);
174
            if (is_string($oldUn)) {
175
                $this->release($key);
176
                $this->set($key, $oldUn . $str);
0 ignored issues
show
Documentation introduced by
$oldUn . $str is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
177
            } else {
178
                throw new InvalidArgumentException('Only is possible append string types');
179
            }
180
        }
181
182
        return true;
183
    }
184
185
    /**
186
     * Lock resource before set it.
187
     * @param string $key
188
     */
189
    public function lock($key)
190
    {
191
        
192
    }
193
194
    /**
195
     * Unlock resource
196
     * @param string $key
197
     */
198
    public function unlock($key)
199
    {
200
        
201
    }
202
203
    /**
204
     * Release the object
205
     * @param string $key
206
     */
207
    public function release($key)
208
    {
209
210
211
        $this->logger->info("[Shmop Cache] release '$key'");
212
213
        if ($this->get($key) === false) {
214
            $this->logger->info("[Shmop Cache] release '$key' does not exists.");
215
            return;
216
        }
217
218
        $filekey = $this->getKeyId($key);
219
        $shm_id = @shmop_open($filekey, "w", 0, 0);
220
221
        $file = $this->getFTok($key);
222
        if (file_exists($file)) {
223
            unlink($file);
224
        }
225
226
        if ($shm_id) {
227
            shmop_delete($shm_id);
228
            shmop_close($shm_id);
229
230
            $this->logger->info("[Shmop Cache] release '$key' confirmed.");
231
        }
232
    }
233
234
    public function isAvailable()
235
    {
236
        return function_exists('shmop_open');
237
    }
238
}
239