Passed
Pull Request — assets (#772)
by Arnaud
08:47 queued 05:50
created

Cache::prepareKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 8
rs 10
1
<?php
2
/**
3
 * This file is part of the Cecil/Cecil package.
4
 *
5
 * Copyright (c) Arnaud Ligny <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Cecil\Assets;
12
13
use Cecil\Builder;
14
use Cecil\Collection\Page\Page;
15
use Cecil\Config;
16
use Cecil\Util;
17
use Exception;
18
use Psr\SimpleCache\CacheInterface;
19
20
class Cache implements CacheInterface
21
{
22
    /** @var Builder */
23
    protected $builder;
24
    /** @var Config */
25
    protected $config;
26
    /** @var string */
27
    protected $pool;
28
    /** @var string */
29
    protected $cacheDir;
30
31
    /**
32
     * @param Builder $builder
33
     * @param string  $pool
34
     */
35
    public function __construct(Builder $builder, string $pool)
36
    {
37
        $this->builder = $builder;
38
        $this->config = $builder->getConfig();
39
        $this->pool = $pool;
40
        $this->cacheDir = Util::joinFile($this->config->getCachePath(), $pool);
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46
    public function get($key, $default = null)
47
    {
48
        $key = $this->prepareKey($key);
49
50
        try {
51
            $data = @unserialize(file_get_contents($this->getFilePathname($key)));
52
        } catch (Exception $e) {
53
            $this->builder->getLogger()->error($e->getMessage());
54
55
            return $default;
56
        }
57
58
        return $data['value'];
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function set($key, $value, $ttl = null)
65
    {
66
        try {
67
            $key = $this->prepareKey($key);
68
            $data = serialize([
69
                'value'      => $value,
70
                'expiration' => time() + $ttl,
71
            ]);
72
73
            $this->prune($key);
74
            Util::getFS()->dumpFile($this->getFilePathname($key), $data);
75
        } catch (Exception $e) {
76
            $this->builder->getLogger()->error($e->getMessage());
77
78
            return false;
79
        }
80
81
        return true;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function delete($key)
88
    {
89
        try {
90
            $key = $this->prepareKey($key);
91
            Util::getFS()->remove($this->getFilePathname($key));
92
            $this->prune($key);
93
        } catch (Exception $e) {
94
            $this->builder->getLogger()->error($e->getMessage());
95
96
            return false;
97
        }
98
99
        return true;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function clear()
106
    {
107
        try {
108
            Util::getFS()->remove($this->cacheDir);
109
        } catch (Exception $e) {
110
            $this->builder->getLogger()->error($e->getMessage());
111
112
            return false;
113
        }
114
115
        return true;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function getMultiple($keys, $default = null)
122
    {
123
        throw new Exception(sprintf('%s::%s not yet implemented.', __CLASS__, __FUNCTION__));
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function setMultiple($values, $ttl = null)
130
    {
131
        throw new Exception(sprintf('%s::%s not yet implemented.', __CLASS__, __FUNCTION__));
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function deleteMultiple($keys)
138
    {
139
        throw new Exception(sprintf('%s::%s not yet implemented.', __CLASS__, __FUNCTION__));
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function has($key)
146
    {
147
        $key = $this->prepareKey($key);
148
        if (!Util::getFS()->exists($this->getFilePathname($key))) {
149
            return false;
150
        }
151
152
        return true;
153
    }
154
155
    /**
156
     * Creates a hash (MD5) from a value.
157
     *
158
     * @param string $value
159
     *
160
     * @return string
161
     */
162
    public function createHash(string $value): string
163
    {
164
        return hash('md5', $value);
165
    }
166
167
    /**
168
     * Creates a cache key (with hash) from a file.
169
     *
170
     * @param string $file
171
     * @param string $path
172
     *
173
     * @return string
174
     */
175
    public function createKeyFromFile(string $file, string $path): string
176
    {
177
        if (false !== $content = file_get_contents($file)) {
178
            return \sprintf('%s__%s', $path, $this->createHash($content));
179
        }
180
181
        return '';
182
    }
183
184
    /**
185
     * Creates a cache key (with hash) from an Asset.
186
     *
187
     * @param Asset $asset
188
     *
189
     * @return string
190
     */
191
    public function createKeyFromAsset(Asset $asset): string
192
    {
193
        if (!empty($asset['content'])) {
194
            return \sprintf('%s__%s', $asset['path'], $this->createHash($asset['content']));
0 ignored issues
show
Bug introduced by
It seems like $asset['content'] can also be of type null; however, parameter $value of Cecil\Assets\Cache::createHash() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

194
            return \sprintf('%s__%s', $asset['path'], $this->createHash(/** @scrutinizer ignore-type */ $asset['content']));
Loading history...
195
        }
196
197
        return '';
198
    }
199
200
    /**
201
     * Returns cache file pathname.
202
     *
203
     * @param string $key
204
     *
205
     * @return string
206
     */
207
    private function getFilePathname(string $key): string
208
    {
209
        return Util::joinFile($this->cacheDir, $key);
210
    }
211
212
    /**
213
     * Removes previous cache files.
214
     *
215
     * @param string $key
216
     *
217
     * @return bool
218
     */
219
    private function prune(string $key): bool
220
    {
221
        try {
222
            $key = $this->prepareKey($key);
223
            $pattern = Util::joinFile($this->cacheDir, explode('__', $key)[0]).'*';
224
            foreach (glob($pattern) as $filename) {
225
                Util::getFS()->remove($filename);
226
            }
227
        } catch (Exception $e) {
228
            $this->builder->getLogger()->error($e->getMessage());
229
230
            return false;
231
        }
232
233
        return true;
234
    }
235
236
    /**
237
     * $key must be a string.
238
     *
239
     * @param string $key
240
     *
241
     * @return string
242
     */
243
    private function prepareKey(string $key): string
244
    {
245
        $key = str_replace(['https://', 'http://'], '', $key);
246
        $key = Page::slugify($key);
247
        $key = trim($key, '/');
248
        $key = str_replace(['\\', '/'], ['-', '-'], $key);
249
250
        return $key;
251
    }
252
}
253