Completed
Push — master ( 81c88a...07a9be )
by Arthur
04:09 queued 02:00
created

CacheManager::rebuildCacheFolders()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 32
nop 0
dl 0
loc 17
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace WebThumbnailer\Application;
4
5
use WebThumbnailer\Exception\CacheException;
6
use WebThumbnailer\Exception\IOException;
7
use WebThumbnailer\Utils\FileUtils;
8
9
/**
10
 * Class CacheManager
11
 *
12
 * Handles file caching using static methods.
13
 * There are 2 types of cache:
14
 *  - thumb: thumbnail images after being resized.
15
 *  - finder: url->thumbnail url resolution is also cached.
16
 * Cache files are organized by domains name, and have a unique name
17
 * based on their URL, max-width and max-height.
18
 *
19
 * Cache duration is defined in JSON settings.
20
 *
21
 * @package WebThumbnailer\Application
22
 */
23
class CacheManager
24
{
25
    /**
26
     * Thumbnails image cache.
27
     */
28
    const TYPE_THUMB  = 'thumb';
29
    /**
30
     * Finder cache.
31
     */
32
    const TYPE_FINDER = 'finder';
33
34
    /**
35
     * @var string Clean filename, used to clean directories periodically.
36
     */
37
    protected static $CLEAN_FILE = '.clean';
38
39
    /**
40
     * Returns the cache path according to the given type.
41
     *
42
     * @param string $type    Type of cache.
43
     * @param bool   $rebuilt Flag to tell if a rebuild tentative has been done.
44
     *
45
     * @return string Cache absolute path.
46
     *
47
     * @throws IOException Type not found.
48
     */
49
    public static function getCachePath($type, $rebuilt = false)
50
    {
51
        self::checkCacheType($type);
52
        $cache = ConfigManager::get('settings.path.cache', 'cache/');
53
        $path = FileUtils::getPath($cache, $type);
54
        if (!$path && !$rebuilt) {
55
            self::rebuildCacheFolders();
56
            return self::getCachePath($type, true);
57
        } elseif (!$path) {
58
            throw new IOException('Cache folders are not writable: '. $cache);
59
        }
60
        return $path;
61
    }
62
63
    /**
64
     * Get a thumb cache file absolute path.
65
     *
66
     * @param string     $url    URL of the thumbnail (unique file per URL).
67
     * @param string     $domain Domain concerned.
68
     * @param string     $type   Type of cache.
69
     * @param int|string $width  User setting for image width.
70
     * @param int|string $height User setting for image height.
71
     * @param bool       $crop   Crop enabled or not.
72
     *
73
     * @return string Absolute file path.
74
     */
75
    public static function getCacheFilePath($url, $domain, $type, $width = 0, $height = 0, $crop = false)
76
    {
77
        $domainHash = self::getDomainHash($domain);
78
        self::createDomainThumbCacheFolder($domainHash, $type);
79
        $domainFolder = FileUtils::getPath(self::getCachePath($type), $domainHash);
80
        if ($type === self::TYPE_THUMB) {
81
            $suffix = $width . $height . ($crop ? '1' : '0') .'.jpg';
82
        } else {
83
            $suffix = $width . $height;
84
        }
85
        return $domainFolder . self::getThumbFilename($url) . $suffix;
0 ignored issues
show
Bug introduced by
Are you sure $domainFolder of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

85
        return /** @scrutinizer ignore-type */ $domainFolder . self::getThumbFilename($url) . $suffix;
Loading history...
86
    }
87
88
    /**
89
     * Check whether a valid cache file exists or not.
90
     * Also check that that file is still valid.
91
     *
92
     * Support endless cache using a negative value.
93
     *
94
     * @param string $cacheFile Cache file path.
95
     * @param string $domain Domain concerned.
96
     * @param string $type   Type of cache.
97
     *
98
     * @return bool true if valid cache exists, false otherwise.
99
     */
100
    public static function isCacheValid($cacheFile, $domain, $type)
101
    {
102
        $out = false;
103
        $cacheDuration = ConfigManager::get('settings.cache_duration', 3600*24*31);
104
105
        if (is_readable($cacheFile)
106
            && ($cacheDuration < 0 || (time() - filemtime($cacheFile)) < $cacheDuration)
107
        ) {
108
            $out = true;
109
        } else {
110
            self::createDomainThumbCacheFolder($domain, $type);
111
        }
112
113
        return $out;
114
    }
115
116
    /**
117
     * Create the domains folder for thumb cache if it doesn't exists.
118
     *
119
     * @param string $domain Domain used.
120
     * @param string $type   Type of cache.
121
     */
122
    protected static function createDomainThumbCacheFolder($domain, $type)
123
    {
124
        $domainFolder = self::getCachePath($type) . $domain;
125
        if (!file_exists($domainFolder)) {
126
            mkdir($domainFolder, 0775, false);
127
            touch($domainFolder . '/' . self::$CLEAN_FILE);
128
        }
129
    }
130
131
    /**
132
     * Get the cache filename according to the given URL.
133
     * Using a sha1 hash to get unique valid filenames.
134
     *
135
     * @param string $url Thumbnail URL.
136
     *
137
     * @return string Thumb filename.
138
     */
139
    protected static function getThumbFilename($url)
140
    {
141
        return hash('sha1', $url);
142
    }
143
144
    /**
145
     * Make sure that the cache type exists.
146
     *
147
     * @param string $type Cache type.
148
     *
149
     * @throws CacheException Cache type doesn't exists.
150
     */
151
    protected static function checkCacheType($type)
152
    {
153
        if ($type != self::TYPE_THUMB && $type != self::TYPE_FINDER) {
154
            throw new CacheException('Unknown cache type '. $type);
155
        }
156
    }
157
158
    /**
159
     * Recreates cache folders just in case the user delete them.
160
     */
161
    protected static function rebuildCacheFolders()
162
    {
163
        $mainFolder = ConfigManager::get('settings.path.cache', 'cache/');
164
        if (! is_dir($mainFolder)) {
165
            mkdir($mainFolder, 0755);
166
        }
167
        if (! is_dir($mainFolder.self::TYPE_THUMB)) {
168
            mkdir($mainFolder.self::TYPE_THUMB, 0755);
169
        }
170
        if (! is_readable($mainFolder . self::TYPE_THUMB . DIRECTORY_SEPARATOR . '.gitkeep')) {
171
            touch($mainFolder . self::TYPE_THUMB . DIRECTORY_SEPARATOR . '.gitkeep');
172
        }
173
        if (! is_dir($mainFolder.self::TYPE_FINDER)) {
174
            mkdir($mainFolder . self::TYPE_FINDER, 0755);
175
        }
176
        if (! is_readable($mainFolder . self::TYPE_THUMB . DIRECTORY_SEPARATOR . '.gitkeep')) {
177
            touch($mainFolder.self::TYPE_FINDER.DIRECTORY_SEPARATOR.'.gitkeep');
178
        }
179
    }
180
181
    /**
182
     * Return the hashed folder name for a given domain.
183
     *
184
     * @param string $domain name
185
     *
186
     * @return string hash
187
     */
188
    protected static function getDomainHash($domain)
189
    {
190
        return md5($domain);
191
    }
192
}
193