• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Cecilapp / Cecil / 5046041481

pending completion
5046041481

Pull #1697

github

GitHub
Merge 2cd309b47 into a16355c73
Pull Request #1697: perf: native_function_invocation

322 of 322 new or added lines in 62 files covered. (100.0%)

2784 of 4121 relevant lines covered (67.56%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.33
/src/Assets/Cache.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <arnaud@ligny.fr>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13

14
namespace Cecil\Assets;
15

16
use Cecil\Builder;
17
use Cecil\Collection\Page\Page;
18
use Cecil\Config;
19
use Cecil\Exception\RuntimeException;
20
use Cecil\Util;
21
use Psr\SimpleCache\CacheInterface;
22

23
class Cache implements CacheInterface
24
{
25
    /** @var Builder */
26
    protected $builder;
27

28
    /** @var Config */
29
    protected $config;
30

31
    /** @var string */
32
    protected $pool;
33

34
    /** @var string */
35
    protected $cacheDir;
36

37
    public function __construct(Builder $builder, string $pool = '')
38
    {
39
        $this->builder = $builder;
1✔
40
        $this->config = $builder->getConfig();
1✔
41
        $this->pool = $pool;
1✔
42
        $this->cacheDir = Util::joinFile($this->config->getCachePath(), $pool);
1✔
43
    }
44

45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function get($key, $default = null)
49
    {
50
        try {
51
            $key = $this->prepareKey($key);
1✔
52
            if (false === $content = Util\File::fileGetContents($this->getFilePathname($key))) {
1✔
53
                return $default;
×
54
            }
55
            $data = unserialize($content);
1✔
56
        } catch (\Exception $e) {
×
57
            $this->builder->getLogger()->error($e->getMessage());
×
58

59
            return $default;
×
60
        }
61

62
        return $data['value'];
1✔
63
    }
64

65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function set($key, $value, $ttl = null)
69
    {
70
        try {
71
            $key = $this->prepareKey($key);
1✔
72
            $data = serialize([
1✔
73
                'value'      => $value,
1✔
74
                'expiration' => time() + $ttl,
1✔
75
            ]);
1✔
76
            $this->prune($key);
1✔
77
            Util\File::getFS()->dumpFile($this->getFilePathname($key), $data);
1✔
78
        } catch (\Exception $e) {
×
79
            $this->builder->getLogger()->error($e->getMessage());
×
80

81
            return false;
×
82
        }
83

84
        return true;
1✔
85
    }
86

87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function delete($key)
91
    {
92
        try {
93
            $key = $this->prepareKey($key);
×
94
            Util\File::getFS()->remove($this->getFilePathname($key));
×
95
            $this->prune($key);
×
96
        } catch (\Exception $e) {
×
97
            $this->builder->getLogger()->error($e->getMessage());
×
98

99
            return false;
×
100
        }
101

102
        return true;
×
103
    }
104

105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function clear()
109
    {
110
        try {
111
            Util\File::getFS()->remove($this->cacheDir);
×
112
        } catch (\Exception $e) {
×
113
            $this->builder->getLogger()->error($e->getMessage());
×
114

115
            return false;
×
116
        }
117

118
        return true;
×
119
    }
120

121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function getMultiple($keys, $default = null)
125
    {
126
        throw new \Exception(sprintf('%s::%s not yet implemented.', __CLASS__, __FUNCTION__));
×
127
    }
128

129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function setMultiple($values, $ttl = null)
133
    {
134
        throw new \Exception(sprintf('%s::%s not yet implemented.', __CLASS__, __FUNCTION__));
×
135
    }
136

137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function deleteMultiple($keys)
141
    {
142
        throw new \Exception(sprintf('%s::%s not yet implemented.', __CLASS__, __FUNCTION__));
×
143
    }
144

145
    /**
146
     * {@inheritdoc}
147
     */
148
    public function has($key)
149
    {
150
        $key = $this->prepareKey($key);
1✔
151
        if (!Util\File::getFS()->exists($this->getFilePathname($key))) {
1✔
152
            return false;
1✔
153
        }
154

155
        return true;
1✔
156
    }
157

158
    /**
159
     * Creates key with the MD5 hash of a string.
160
     */
161
    public function createKeyFromString(string $value): string
162
    {
163
        return hash('md5', $value);
1✔
164
    }
165

166
    /**
167
     * Creates key from a file: "$relativePath__MD5".
168
     *
169
     * @throws RuntimeException
170
     */
171
    public function createKeyFromPath(string $path, string $relativePath): string
172
    {
173
        if (false === $content = Util\File::fileGetContents($path)) {
1✔
174
            throw new RuntimeException(sprintf('Can\'t create cache key for "%s"', $path));
×
175
        }
176

177
        return $this->prepareKey(sprintf('%s__%s', $relativePath, $this->createKeyFromString($content)));
1✔
178
    }
179

180
    /**
181
     * Creates key from an Asset source: "$filename_$ext_$tag__VERSION__MD5".
182
     */
183
    public function createKeyFromAsset(Asset $asset, array $tags = null): string
184
    {
185
        $tags = implode('_', $tags ?? []);
1✔
186

187
        return $this->prepareKey(sprintf(
1✔
188
            '%s%s%s__%s__%s',
1✔
189
            $asset['filename'],
1✔
190
            "_{$asset['ext']}",
1✔
191
            $tags ? "_$tags" : '',
1✔
192
            $this->builder->getVersion(),
1✔
193
            $this->createKeyFromString($asset['content_source'] ?? '')
1✔
194
        ));
1✔
195
    }
196

197
    /**
198
     * Clear cache by pattern.
199
     */
200
    public function clearByPattern(string $pattern): int
201
    {
202
        try {
203
            $fileCount = 0;
×
204
            $iterator = new \RecursiveIteratorIterator(
×
205
                new \RecursiveDirectoryIterator($this->cacheDir),
×
206
                \RecursiveIteratorIterator::SELF_FIRST
×
207
            );
×
208
            foreach ($iterator as $file) {
×
209
                if ($file->isFile()) {
×
210
                    if (preg_match('/' . $pattern . '/i', $file->getPathname())) {
×
211
                        Util\File::getFS()->remove($file->getPathname());
×
212
                        $fileCount++;
×
213
                        $this->builder->getLogger()->debug(sprintf('Cache file "%s" removed', $file->getPathname()));
×
214
                    }
215
                }
216
            }
217
        } catch (\Exception $e) {
×
218
            $this->builder->getLogger()->error($e->getMessage());
×
219

220
            return 0;
×
221
        }
222

223
        return $fileCount;
×
224
    }
225

226
    /**
227
     * Returns cache file pathname from key.
228
     */
229
    private function getFilePathname(string $key): string
230
    {
231
        return Util::joinFile($this->cacheDir, sprintf('%s.ser', $key));
1✔
232
    }
233

234
    /**
235
     * Removes previous cache files.
236
     */
237
    private function prune(string $key): bool
238
    {
239
        try {
240
            $keyAsArray = explode('__', $this->prepareKey($key), -1);
1✔
241
            if (!empty($keyAsArray)) {
1✔
242
                $pattern = Util::joinFile($this->cacheDir, $keyAsArray[0]) . '*';
1✔
243
                foreach (glob($pattern) as $filename) {
1✔
244
                    Util\File::getFS()->remove($filename);
1✔
245
                }
246
            }
247
        } catch (\Exception $e) {
×
248
            $this->builder->getLogger()->error($e->getMessage());
×
249

250
            return false;
×
251
        }
252

253
        return true;
1✔
254
    }
255

256
    /**
257
     * $key must be a valid string.
258
     */
259
    private function prepareKey(string $key): string
260
    {
261
        $key = str_replace(['https://', 'http://'], '', $key);
1✔
262
        $key = Page::slugify($key);
1✔
263
        $key = trim($key, '/');
1✔
264
        $key = str_replace(['\\', '/'], ['-', '-'], $key);
1✔
265
        $key = substr($key, 0, 200); // Maximum filename length in NTFS?
1✔
266

267
        return $key;
1✔
268
    }
269
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc