Issues (36)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Directory.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 *
4
 * This file is part of the Apix Project.
5
 *
6
 * (c) Franck Cassedanne <franck at ouarz.net>
7
 *
8
 * @license     http://opensource.org/licenses/BSD-3-Clause  New BSD License
9
 *
10
 */
11
12
namespace Apix\Cache;
13
14
/**
15
 * Class Directory
16
 * Directory cache wrapper.
17
 * Expiration time and tags are stored separately from the cached data
18
 *
19
 * @package Apix\Cache
20
 * @author  MacFJA
21
 */
22
class Directory extends AbstractCache
23
{
24
    /**
25
     * Constructor.
26
     *
27
     * @param array  $options Array of options.
28
     */
29 323
    public function __construct(array $options = array())
30
    {
31
        $options += array(
32 323
            'directory' => sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'apix-cache',
33
            'locking' => true
34 323
        );
35 323
        parent::__construct(null, $options);
36 323
        $this->initDirectories();
37 323
    }
38
39
    /**
40
     * Initialize cache directories (create them)
41
     */
42 323
    protected function initDirectories()
43
    {
44 323
        $this->getBasePath('key');
45 323
        $this->getBasePath('tag');
46 323
        $this->getBasePath('ttl');
47 323
    }
48
49
    /**
50
     * Get the base path, and ensure they are created
51
     *
52
     * @param string $type The path type (key, ttl, tag)
53
     * @return string
54
     */
55 323
    protected function getBasePath($type)
56
    {
57 323
        $path = rtrim($this->getOption('directory'), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
58
59
        switch ($type) {
60 323
            case 'ttl':
61 323
                $path .= 'ttl' . DIRECTORY_SEPARATOR;
62 323
                break;
63 323
            case 'tags':
64 323
            case 'tag':
65 323
                $path .= 'tag' . DIRECTORY_SEPARATOR;
66 323
                break;
67
        }
68 323
        $this->buildPath($path);
69
70 323
        return $path;
71
    }
72
73
    /**
74
     * Get the file data.
75
     * If enable, lock file to preserve atomicity
76
     *
77
     * @param string $path The file path
78
     * @return string
79
     */
80 119
    protected function readFile($path)
81
    {
82 119
        $handle = fopen($path, 'rb');
83 119
        if ($this->getOption('locking')) {
84 119
            flock($handle, LOCK_SH);
85 119
        }
86 119
        $data = stream_get_contents($handle);
87 119
        if ($this->getOption('locking')) {
88 119
            flock($handle, LOCK_UN);
89 119
        }
90 119
        fclose($handle);
91
92 119
        return $data;
93
    }
94
95
    /**
96
     * Get the path of a cached data
97
     *
98
     * @param string $key The cache key
99
     * @return string
100
     */
101 272
    protected function getKeyPath($key)
102
    {
103 272
        $dir = $this->getBasePath('key');
104 272
        $baseKey = base64_encode($key);
105 272
        $sep = DIRECTORY_SEPARATOR;
106 272
        $path = $dir . preg_replace('/^(.)(.)(.).+$/', '$1' . $sep . '$2' . $sep . '$3' . $sep . '$0', $baseKey);
107
108 272
        return $path;
109
    }
110
111
    /**
112
     * Get the path of the expiration file for a key
113
     *
114
     * @param string $key The cache key
115
     * @return string
116
     */
117 238
    protected function getTtlPath($key)
118
    {
119 238
        $baseKey = base64_encode($key);
120 238
        $path = $this->getBasePath('ttl') . substr($baseKey, 0, 4);
121
122 238
        return $path;
123
    }
124
125
    /**
126
     * Get the expiration data of a key
127
     *
128
     * @param string $key The cache key
129
     * @return bool|int
130
     */
131 51
    protected function loadExpire($key)
132
    {
133 51
        $path = $this->getTtlPath($key);
134
135 51
        if (!is_file($path)) {
136 17
            return false;
137
        }
138
139 51
        $expires = json_decode($this->readFile($path), true);
140
141 51
        if (!array_key_exists(base64_encode($key), $expires)) {
142 17
            return false;
143
        }
144
145 34
        return $expires[base64_encode($key)];
146
    }
147
148
    /**
149
     * Get the path of a tag file
150
     *
151
     * @param string $tag The tag name
152
     * @return string
153
     */
154 136
    protected function getTagPath($tag)
155
    {
156 136
        $baseTag = base64_encode($tag);
157 136
        $path = $this->getBasePath('tag') . $baseTag;
158
159 136
        return $path;
160
    }
161
162
    /**
163
     * Build and return the path of a directory
164
     *
165
     * @param string $path The directory path to build
166
     * @return mixed
167
     */
168 323
    protected function buildPath($path)
169
    {
170 323
        if (!is_dir($path)) {
171 289
            mkdir($path, 0755, true);
172 289
        }
173 323
        return $path;
174
    }
175
176
    /**
177
     * Save a tag
178
     *
179
     * @param string $name The tag name
180
     * @param string[] $ids The list of cache keys associated to the tag
181
     */
182 119
    protected function saveTag($name, $ids)
183
    {
184 119
        $ids = array_unique($ids);
185 119
        array_walk($ids, function(&$item) { $item = base64_encode($item); });
186
187 119
        $path = $this->getTagPath($this->mapTag($name));
188 119
        $this->buildPath(dirname($path));
189 119
        file_put_contents($path, implode(PHP_EOL, $ids), $this->getOption('locking') ? LOCK_EX : null);
190 119
    }
191
192
    /**
193
     * Save the expiration time of a cache
194
     *
195
     * @param string $key the cache key
196
     * @param false|int $ttl The TTL of the cache
197
     */
198 238
    protected function saveExpire($key, $ttl)
199
    {
200 238
        $baseKey = base64_encode($key);
201
202 238
        $path = $this->getTtlPath($key);
203 238
        $this->buildPath(dirname($path));
204
205 238
        $expires = array();
206 238
        if (file_exists($path) && is_file($path)) {
207 17
            $expires = json_decode($this->readFile($path), true);
208 17
        }
209
210 238
        if ($ttl === false) {
211 221
            if (array_key_exists($baseKey, $expires)) {
212 17
                unset($expires[$baseKey]);
213 17
            } else {
214 204
                return;
215
            }
216 17
        } else {
217 51
            $expires[$baseKey] = time() + $ttl;
218
        }
219
220 51
        file_put_contents($path, json_encode($expires), $this->getOption('locking') ? LOCK_EX : null);
221 51
    }
222
223
    /**
224
     * Return the list of all existing tags
225
     *
226
     * @return string[]
227
     */
228 68
    protected function getAllTags()
229
    {
230 68
        $basePath = $this->getBasePath('tag');
231 68
        $baseTags = scandir($basePath);
232
233 68
        $tags = array();
234
235 68
        foreach ($baseTags as $baseTag) {
236 68
            if (substr($baseTag, 0, 1) === '.') {
237 68
                continue;
238
            }
239
240 51
            $tags[] = $this->removePrefixTag(base64_decode($baseTag));
241 68
        }
242
243 68
        return $tags;
244
    }
245
246
    /**
247
     * Retrieves the cache content for the given key.
248
     *
249
     * @param  string $key The cache key to retrieve.
250
     * @return mixed|null Returns the cached data or null.
251
     */
252 136
    public function loadKey($key)
253
    {
254 136
        $key = $this->mapKey($key);
255
256 136
        $path = $this->getKeyPath($key);
257 136
        if (!file_exists($path) && !is_file($path)) {
258 85
            return null;
259
        }
260
261 85
        return unserialize($this->readFile($path));
262
    }
263
264
    /**
265
     * Retrieves the cache keys for the given tag.
266
     *
267
     * @param  string $tag The cache tag to retrieve.
268
     * @return array|null Returns an array of cache keys or null.
269
     */
270 136
    public function loadTag($tag)
271
    {
272 136
        if (!$this->getOption('tag_enable')) {
273 34
            return null;
274
        }
275
276 102
        $tag = $this->mapTag($tag);
277
278 102
        $path = $this->getTagPath($tag);
279 102
        if (!is_file($path)) {
280 102
            return null;
281
        }
282
283 85
        $keys = file($path, FILE_IGNORE_NEW_LINES);
284
285 85
        if (0 === count($keys)) {
286 17
            return null;
287
        }
288
289 85
        array_walk($keys, function (&$item) { $item = base64_decode($item); });
290
291 85
        return $keys;
292
    }
293
294
    /**
295
     * Saves data to the cache.
296
     *
297
     * @param  mixed $data The data to cache.
298
     * @param  string $key The cache id to save.
299
     * @param  array $tags The cache tags for this cache entry.
300
     * @param  int $ttl The time-to-live in seconds, if set to null the
301
     *                       cache is valid forever.
302
     * @return boolean Returns True on success or False on failure.
303
     */
304 238
    public function save($data, $key, array $tags = null, $ttl = null)
305
    {
306 238
        $key = $this->mapKey($key);
307
308 238
        $path = $this->getKeyPath($key);
309 238
        $this->buildPath(dirname($path));
310 238
        file_put_contents($path, serialize($data), $this->getOption('locking') ? LOCK_EX : null);
311
312 238
        if (null !== $tags) {
313 119
            foreach ($tags as $tag) {
314 119
                $ids = $this->loadTag($tag);
315 119
                $ids[] = $key;
316 119
                $this->saveTag($tag, $ids);
317 119
            }
318 119
        }
319
320 238
        if (null !== $ttl) {
321 51
            $this->saveExpire($key, $ttl);
322 51
        } else {
323 221
            $this->saveExpire($key, false);
324
        }
325
326 238
        return true;
327
    }
328
329
    /**
330
     * Deletes the specified cache record.
331
     *
332
     * @param  string $key The cache id to remove.
333
     * @return boolean Returns True on success or False on failure.
334
     */
335 85
    public function delete($key)
336
    {
337 85
        $key = $this->mapKey($key);
338
339 85
        $path = $this->getKeyPath($key);
340 85
        if (!is_file($path)) {
341 34
            return false;
342
        }
343
344 68
        unlink($path);
345
346 68
        foreach ($this->getAllTags() as $tag) {
347 51
            $ids = $this->loadTag($tag);
348 51
            if (null === $ids) {
349 17
                continue;
350
            }
351 34
            if (in_array($key, $ids, true) !== false) {
352 34
                unset($ids[array_search($key, $ids, true)]);
353 34
                $this->saveTag($tag, $ids);
354 34
            }
355 68
        }
356
357 68
        $this->saveExpire($key, false);
358
359 68
        return true;
360
    }
361
362
    /**
363
     * Removes all the cached entries associated with the given tag names.
364
     *
365
     * @param  array $tags The array of tags to remove.
366
     * @return boolean Returns True on success or False on failure.
367
     */
368 17
    public function clean(array $tags)
369
    {
370 17
        $cleaned = false;
371 17
        foreach ($tags as $tag) {
372
            $ids = $this->loadTag($tag);
373 17
374 17
            if (null === $ids) {
375
                continue;
376 17
            }
377 17
            $cleaned = true;
378 17
379 17
            foreach ($ids as $key) {
380 17
                $this->delete($this->removePrefixKey($key));
381
            }
382 17
            unlink($this->getTagPath($this->mapTag($tag)));
383
        }
384
385
        return $cleaned;
386
    }
387
388
    /**
389
     * Flush all the cached entries.
390
     *
391
     * @param  boolean $all Wether to flush the whole database, or (preferably)
392 289
     *                      the entries prefixed with prefix_key and prefix_tag.
393
     * @return boolean Returns True on success or False on failure.
394 289
     */
395 289
    public function flush($all = false)
396 289
    {
397
        $this->delTree($this->getOption('directory'));
398
        $this->initDirectories();
399
    }
400
401
    /**
402
     * Remove a directory
403
     *
404 289
     * @param string $dir The path of the directory to remove
405 289
     * @return bool
406 289
     */
407 289
    public function delTree($dir) {
408 289
        $files = array_diff(scandir($dir), array('.','..'));
409 289
        foreach ($files as $file) {
410 289
            $newPath = $dir . DIRECTORY_SEPARATOR . $file;
411
            (is_dir($newPath)) ? $this->delTree($newPath) : unlink($newPath);
412
        }
413
        return rmdir($dir);
414
    }
415
416
    /**
417
     * Returns the time-to-live (in seconds) for the given key.
418
     *
419
     * @param  string $key The name of the key.
420 51
     * @return int|false Returns the number of seconds left, 0 if valid
421
     *                       forever or False if the key is non-existant.
422 51
     */
423
    public function getTtl($key)
424 51
    {
425
        $key = $this->mapKey($key);
426 51
427 34
        $path = $this->getKeyPath($key);
428
429
        if (!file_exists($path) && !is_file($path)) {
430 51
            return false;
431
        }
432 51
433 34
        $expire = $this->loadExpire($key);
434
435 34
        if (false === $expire) {
436
            return 0;
437
        }
438
        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 438 which is incompatible with the return type declared by the interface Apix\Cache\Adapter::getTtl of type integer|false.
Loading history...
439
    }
440
}
441