Completed
Push — master ( e2ef81...59820c )
by Franck
16:11
created

Files::getTtl()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 19
rs 9.2
cc 4
eloc 12
nc 3
nop 1
1
<?php
2
3
namespace Apix\Cache;
4
5
/**
6
 * Class Files
7
 * In files cache wrapper.
8
 * Expiration time and tags are stored in the cache file
9
 *
10
 * @package Apix\Cache
11
 * @author  MacFJA
12
 */
13
class Files extends AbstractCache
14
{
15
    /**
16
     * Constructor.
17
     *
18
     * @param array  $options Array of options.
19
     */
20
    public function __construct(array $options=null)
21
    {
22
        $options += array(
23
            'directory' => sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'apix-cache'
24
        );
25
        parent::__construct(null, $options);
26
        if (!file_exists($this->getOption('directory')) || !is_dir($this->getOption('directory'))) {
27
            mkdir($this->getOption('directory'), 0755, true);
28
        }
29
    }
30
31
    /**
32
     * Retrieves the cache content for the given key.
33
     *
34
     * @param  string $key The cache key to retrieve.
35
     * @return mixed|null Returns the cached data or null.
36
     */
37
    public function loadKey($key)
38
    {
39
        $key = $this->mapKey($key);
40
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
41
42
        if (!file_exists($path) || !is_file($path)) {
43
            return null;
44
        }
45
46
        $data = file_get_contents($path);
47
        if ('' === $data) {
48
            unlink($path);
49
            return null;
50
        }
51
        $pos = strpos($data, PHP_EOL, 0);
52
        $pos = strpos($data, PHP_EOL, $pos+1);
53
        if (false === $pos) {// Un-complete file
54
            unlink($path);
55
            return null;
56
        }
57
58
        $serialized = substr($data, $pos+1);
59
        return unserialize($serialized);
60
    }
61
62
    /**
63
     * Retrieves the cache keys for the given tag.
64
     *
65
     * @param  string $tag The cache tag to retrieve.
66
     * @return array|null Returns an array of cache keys or null.
67
     */
68
    public function loadTag($tag)
69
    {
70
        if (!$this->getOption('tag_enable')) {
71
            return null;
72
        }
73
74
        $encoded = base64_encode($this->mapTag($tag));
75
        $found = array();
76
        $files = scandir($this->getOption('directory'));
77
        foreach ($files as $file) {
78
            if (substr($file, 0, 1) === '.') {
79
                continue;
80
            }
81
            $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . $file;
82
            $handle = fopen($path, 'r');
83
            $fileTags = explode(' ', rtrim(fgets($handle), PHP_EOL));
84
            fclose($handle);
85
            if (in_array($encoded, $fileTags, true)) {
86
                $found[] = base64_decode($file);
87
            }
88
        }
89
90
        if (0 === count($found)) {
91
            return null;
92
        }
93
        return $found;
94
    }
95
96
    /**
97
     * Saves data to the cache.
98
     *
99
     * @param  mixed $data The data to cache.
100
     * @param  string $key The cache id to save.
101
     * @param  array $tags The cache tags for this cache entry.
102
     * @param  int $ttl The time-to-live in seconds, if set to null the
103
     *                       cache is valid forever.
104
     * @return boolean Returns True on success or False on failure.
105
     */
106
    public function save($data, $key, array $tags = null, $ttl = null)
107
    {
108
        $key = $this->mapKey($key);
109
        $expire = (null === $ttl) ? 0 : time() + $ttl;
110
111
        $tag = '';
112
        if (null !== $tags) {
113
            $baseTags = $tags;
114
            array_walk($baseTags, function (&$item, $key, Files $cache) {
115
                $item = base64_encode($cache->mapTag($item));
116
            }, $this);
117
            $tag = implode(' ', $baseTags);
118
        }
119
120
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
121
        file_put_contents($path, $tag . PHP_EOL . $expire . PHP_EOL . serialize($data));
122
        return true;
123
    }
124
125
    /**
126
     * Deletes the specified cache record.
127
     *
128
     * @param  string $key The cache id to remove.
129
     * @return boolean Returns True on success or False on failure.
130
     */
131
    public function delete($key)
132
    {
133
        $key = $this->mapKey($key);
134
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
135
        if (!file_exists($path)) {
136
            return false;
137
        }
138
139
        return unlink($path);
140
    }
141
142
    /**
143
     * Removes all the cached entries associated with the given tag names.
144
     *
145
     * @param  array $tags The array of tags to remove.
146
     * @return boolean Returns True on success or False on failure.
147
     */
148
    public function clean(array $tags)
149
    {
150
        $toRemove = array();
151
        foreach ($tags as $tag) {
152
            $keys = $this->loadTag($tag);
153
            if (null === $keys) {
154
                return false;
155
            }
156
            $toRemove = array_merge($toRemove, $keys);
157
        }
158
        $toRemove = array_unique($toRemove);
159
160
        foreach ($toRemove as $key) {
161
            $this->delete($this->removePrefixKey($key));
162
        }
163
164
        return true;
165
    }
166
167
    /**
168
     * Flush all the cached entries.
169
     *
170
     * @param  boolean $all Wether to flush the whole database, or (preferably)
171
     *                      the entries prefixed with prefix_key and prefix_tag.
172
     * @return boolean Returns True on success or False on failure.
173
     */
174
    public function flush($all = false)
175
    {
176
        $files = scandir($this->getOption('directory'));
177
        foreach ($files as $file) {
178
            if ('.' === substr($file, 0, 1)) {
179
                continue;
180
            }
181
            $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . $file;
182
            $fullKey = base64_decode($file);
183
            $key = $this->removePrefixKey($fullKey);
184
185
            if (!$all && ($key !== $fullKey || '' === $this->options['prefix_key'])) {
186
                unlink($path);
187
            }
188
        }
189
    }
190
191
    /**
192
     * Returns the time-to-live (in seconds) for the given key.
193
     *
194
     * @param  string $key The name of the key.
195
     * @return int|false Returns the number of seconds left, 0 if valid
196
     *                       forever or False if the key is non-existant.
197
     */
198
    public function getTtl($key)
199
    {
200
        $key = $this->mapKey($key);
201
        $path = $this->getOption('directory') . DIRECTORY_SEPARATOR . base64_encode($key);
202
        if (!file_exists($path) || !is_file($path)) {
203
            return false;
204
        }
205
206
        $handle = fopen($path, 'r');
207
        fgets($handle);
208
        $expire = fgets($handle);
209
        fclose($handle);
210
211
        if (0 === (int) $expire) {
212
            return 0;
213
        }
214
215
        return $expire - time();
0 ignored issues
show
Bug Compatibility introduced by
The expression $expire - time(); of type integer|double adds the type double to the return on line 215 which is incompatible with the return type declared by the interface Apix\Cache\Adapter::getTtl of type integer|false.
Loading history...
216
    }
217
}