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

FileSystemCacheEngine   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 207
Duplicated Lines 2.9 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 2
dl 6
loc 207
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
B get() 0 32 6
C set() 0 33 7
A delete() 0 5 1
A lock() 0 12 2
A unlock() 0 11 2
A isAvailable() 0 4 1
A fixKey() 0 7 1
A clear() 0 9 2
B has() 6 20 5

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 ByJG\Cache\CacheLockInterface;
6
use Exception;
7
use Psr\Log\NullLogger;
8
9
class FileSystemCacheEngine extends BaseCacheEngine implements CacheLockInterface
10
{
11
12
    protected $logger = null;
13
14
    protected $prefix = null;
15
16
    public function __construct($prefix = 'cache', $logger = null)
17
    {
18
        $this->prefix = $prefix;
19
20
        $this->logger = $logger;
21
        if (is_null($logger)) {
22
            $this->logger = new NullLogger();
23
        }
24
    }
25
26
    /**
27
     * @param string $key The object KEY
28
     * @param mixed $default IGNORED IN MEMCACHED.
29
     * @return mixed Description
30
     */
31
    public function get($key, $default = null)
32
    {
33
        // Check if file is Locked
34
        $fileKey = $this->fixKey($key);
35
        $lockFile = $fileKey . ".lock";
36
        if (file_exists($lockFile)) {
37
            $this->logger->info("[Filesystem cache] Locked! $key. Waiting...");
38
            $lockTime = filemtime($lockFile);
39
40
            while (true) {
41
                if (!file_exists($lockFile)) {
42
                    $this->logger->info("[Filesystem cache] Lock released for '$key'");
43
                    break;
44
                }
45
                if (intval(time() - $lockTime) > 20) {  // Wait for 10 seconds
46
                    $this->logger->info("[Filesystem cache] Gave up to wait unlock. Release lock for '$key'");
47
                    $this->unlock($key);
48
                    return $default;
49
                }
50
                sleep(1); // 1 second
51
            }
52
        }
53
54
        // Check if file exists
55
        if ($this->has($key)) {
56
            $this->logger->info("[Filesystem cache] Get '$key'");
57
            return unserialize(file_get_contents($fileKey));
58
        } else {
59
            $this->logger->info("[Filesystem cache] Not found '$key'");
60
            return $default;
61
        }
62
    }
63
64
    /**
65
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
66
     *
67
     * @param string                $key   The key of the item to store.
68
     * @param mixed                 $value The value of the item to store, must be serializable.
69
     * @param null|int|\DateInterval $ttl   Optional. The TTL value of this item. If no value is sent and
70
     *                                     the driver supports TTL then the library may set a default value
71
     *                                     for it or let the driver take care of that.
72
     *
73
     * @return bool True on success and false on failure.
74
     *
75
     * @throws \Psr\SimpleCache\InvalidArgumentException
76
     *   MUST be thrown if the $key string is not a legal value.
77
     */
78
    public function set($key, $value, $ttl = null)
79
    {
80
        $fileKey = $this->fixKey($key);
81
82
        $this->logger->info("[Filesystem cache] Set '$key' in FileSystem");
83
84
        try {
85
            if (file_exists($fileKey)) {
86
                unlink($fileKey);
87
                unlink("$fileKey.ttl");
88
            }
89
90
            if (is_null($value)) {
91
                return false;
92
            }
93
94
            if (is_string($value) && (strlen($value) === 0)) {
95
                touch($fileKey);
96
            } else {
97
                file_put_contents($fileKey, serialize($value));
98
            }
99
100
            $validUntil = $this->addToNow($ttl);
101
            if (!empty($validUntil)) {
102
                file_put_contents($fileKey . ".ttl", $validUntil);
103
            }
104
        } catch (Exception $ex) {
105
            $this->logger->warning("[Filesystem cache] I could not write to cache on file '" . basename($key) . "'. Switching to nocache=true mode.");
106
            return false;
107
        }
108
109
        return true;
110
    }
111
112
    /**
113
     * @param string $key
114
     * @return bool
115
     */
116
    public function delete($key)
117
    {
118
        $this->set($key, null);
119
        return true;
120
    }
121
122
    /**
123
     * Lock resource before set it.
124
     * @param string $key
125
     */
126
    public function lock($key)
127
    {
128
        $this->logger->info("[Filesystem cache] Lock '$key'");
129
130
        $lockFile = $this->fixKey($key) . ".lock";
131
132
        try {
133
            file_put_contents($lockFile, date('c'));
134
        } catch (Exception $ex) {
135
            // Ignoring... Set will cause an error
136
        }
137
    }
138
139
    /**
140
     * UnLock resource after set it.
141
     * @param string $key
142
     */
143
    public function unlock($key)
144
    {
145
        
146
        $this->logger->info("[Filesystem cache] Unlock '$key'");
147
148
        $lockFile = $this->fixKey($key) . ".lock";
149
150
        if (file_exists($lockFile)) {
151
            unlink($lockFile);
152
        }
153
    }
154
155
    public function isAvailable()
156
    {
157
        return is_writable(dirname($this->fixKey('test')));
158
    }
159
160
    protected function fixKey($key)
161
    {
162
        return sys_get_temp_dir() . '/'
163
            . $this->prefix
164
            . '-' . preg_replace("/[\/\\\]/", "#", $key)
165
            . '.cache';
166
    }
167
168
    /**
169
     * Wipes clean the entire cache's keys.
170
     *
171
     * @return bool True on success and false on failure.
172
     */
173
    public function clear()
174
    {
175
        $patternKey = $this->fixKey('*');
176
        $list = glob($patternKey);
177
        foreach ($list as $file) {
178
            unlink($file);
179
        }
180
        return true;
181
    }
182
183
    /**
184
     * Determines whether an item is present in the cache.
185
     * NOTE: It is recommended that has() is only to be used for cache warming type purposes
186
     * and not to be used within your live applications operations for get/set, as this method
187
     * is subject to a race condition where your has() will return true and immediately after,
188
     * another script can remove it making the state of your app out of date.
189
     *
190
     * @param string $key The cache item key.
191
     * @return bool
192
     * @throws \Psr\SimpleCache\InvalidArgumentException
193
     *   MUST be thrown if the $key string is not a legal value.
194
     */
195
    public function has($key)
196
    {
197
        $fileKey = $this->fixKey($key);
198
        if (file_exists($fileKey)) {
199
            if (file_exists("$fileKey.ttl")) {
200
                $fileTtl = intval(file_get_contents("$fileKey.ttl"));
201
            }
202
203 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...
204
                $this->logger->info("[Filesystem cache] File too old. Ignoring '$key'");
205
                $this->delete($key);
206
207
                return false;
208
            }
209
210
            return true;
211
        }
212
213
        return false;
214
    }
215
}
216