Completed
Push — master ( 372722...0ff67c )
by Mihail
02:42
created

ShmopClient   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 60
c 1
b 0
f 1
dl 0
loc 139
ccs 0
cts 95
cp 0
rs 10
wmc 23

9 Methods

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