ShmopClient::has()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 2
Metric Value
cc 3
eloc 7
c 2
b 0
f 2
nc 2
nop 2
dl 0
loc 10
ccs 8
cts 8
cp 1
crap 3
rs 10
1
<?php
2
/*
3
 * This file is part of the Koded package.
4
 *
5
 * (c) Mihail Binev <[email protected]>
6
 *
7
 * Please view the LICENSE distributed with this source code
8
 * for the full copyright and license information.
9
 */
10
11
namespace Koded\Caching\Client;
12
13
use Koded\Caching\{Cache, CacheException};
14
use function chmod;
15
use function file_get_contents;
16
use function file_put_contents;
17
use function fileinode;
18
use function glob;
19
use function is_dir;
20
use function Koded\Caching\verify_key;
21
use function mkdir;
22
use function rtrim;
23
use function serialize;
24
use function sha1;
25
use function shmop_delete;
26
use function shmop_open;
27
use function shmop_read;
28
use function shmop_size;
29
use function shmop_write;
30
use function strlen;
31
use function sys_get_temp_dir;
32
use function time;
33
use function touch;
34
use function umask;
35
use function unlink;
36
use function unserialize;
37
38
/**
39
 * @property ShmopClient client
40
 *
41
 */
42
final class ShmopClient implements Cache
43
{
44
    use ClientTrait, MultiplesTrait;
45
46
    private string $dir;
47
48 61
    public function __construct(string $dir, ?int $ttl)
49
    {
50 61
        $this->dir = $dir;
51 61
        $this->ttl = $ttl;
52 61
        $this->setDirectory($dir);
53
    }
54
55 43
    public function get(string $key, mixed $default = null): mixed
56
    {
57 43
        if (false === $this->has($key, $filename)) {
58 12
            return $default;
59
        }
60 36
        $resource = shmop_open(fileinode($filename), 'a', 0, 0);
61 36
        return unserialize(shmop_read($resource, 0, shmop_size($resource)));
62
    }
63
64 42
    public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool
65
    {
66 42
        verify_key($key);
67 42
        if (1 > $expiration = $this->timestampWithGlobalTtl($ttl, Cache::DATE_FAR_FAR_AWAY)) {
68
            // The item is considered expired and must be deleted
69 1
            return $this->delete($key);
70
        }
71 42
        $value = serialize($value);
72 42
        $size = strlen($value);
73 42
        $filename = $this->filename($key, true);
74 42
        if (false === $resource = @shmop_open(fileinode($filename), 'n', 0666, $size)) {
75 1
            $resource = shmop_open(fileinode($filename), 'w', 0666, $size);
76
        }
77 42
        return shmop_write($resource, $value, 0) === $size
78 42
            && false !== file_put_contents($filename . '-ttl', $expiration);
79
    }
80
81 6
    public function delete(string $key): bool
82
    {
83 6
        if (false === $this->has($key, $filename)) {
84 5
            return true;
85
        }
86 5
        return $this->expire($filename);
87
    }
88
89 61
    public function clear(): bool
90
    {
91 61
        foreach ((glob($this->dir . 'shmop-*.cache*') ?: []) as $filename) {
92 61
            $this->expire($filename);
93
        }
94 61
        return true;
95
    }
96
97 44
    public function has(string $key, &$filename = ''): bool
98
    {
99 44
        verify_key($key);
100 44
        $filename = $this->filename($key, false);
101 44
        $expiration = (int)(@file_get_contents($filename . '-ttl') ?: 0);
102 44
        if ($expiration <= time()) {
103 14
            $this->expire($filename);
104 14
            return false;
105
        }
106 41
        return true;
107
    }
108
109 44
    private function filename(string $key, bool $create): string
110
    {
111 44
        $filename = $this->dir . 'shmop-' . sha1($key) . '.cache';
112 44
        if ($create) {
113 42
            touch($filename);
114 42
            touch($filename . '-ttl');
115 42
            chmod($filename, 0666);
116 42
            chmod($filename . '-ttl', 0666);
117
        }
118 44
        return $filename;
119
    }
120
121
    /**
122
     * Prepares the cache directory.
123
     *
124
     * @param string $directory
125
     *
126
     * @throws CacheException
127
     */
128 61
    private function setDirectory(string $directory): void
129
    {
130
        // Overrule shell misconfiguration or the web server
131 61
        umask(umask() | 0002);
132 61
        $dir = $directory ?: sys_get_temp_dir();
133 61
        $dir = rtrim($dir, '/') . '/';
134
135 61
        if (false === is_dir($dir) && false === mkdir($dir, 0775, true)) {
136
            throw CacheException::forCreatingDirectory($dir);
137
        }
138 61
        $this->dir = $dir;
139
    }
140
141 61
    private function expire(string $filename): bool
142
    {
143 61
        if (false === $resource = @shmop_open(fileinode($filename), 'w', 0, 0)) {
144 61
            return false;
145
        }
146 42
        unlink($filename . '-ttl');
147 42
        return shmop_delete($resource);
148
    }
149
}
150