Completed
Push — master ( 670224...062404 )
by Arthur
26s queued 11s
created

Thumbnailer::thumbnailDownload()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 58
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 34
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 58
rs 8.7537

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace WebThumbnailer\Application;
6
7
use WebThumbnailer\Application\WebAccess\WebAccessFactory;
8
use WebThumbnailer\Exception\BadRulesException;
9
use WebThumbnailer\Exception\CacheException;
10
use WebThumbnailer\Exception\DownloadException;
11
use WebThumbnailer\Exception\ImageConvertException;
12
use WebThumbnailer\Exception\IOException;
13
use WebThumbnailer\Exception\MissingRequirementException;
14
use WebThumbnailer\Exception\NotAnImageException;
15
use WebThumbnailer\Exception\ThumbnailNotFoundException;
16
use WebThumbnailer\Finder\Finder;
17
use WebThumbnailer\Finder\FinderFactory;
18
use WebThumbnailer\Utils\ApplicationUtils;
19
use WebThumbnailer\Utils\ImageUtils;
20
use WebThumbnailer\Utils\SizeUtils;
21
use WebThumbnailer\WebThumbnailer;
22
23
/**
24
 * Main application class, it will:
25
 *   - retrieve the thumbnail URL using the approriate finder,
26
 *   - in download mode, download the thumb and resize it,
27
 *   - use the cache.
28
 */
29
class Thumbnailer
30
{
31
    /** @var string Array key for download type option. */
32
    protected const DL_OPTION = 'dl';
33
34
    /** @var string User given URL, from where to generate a thumbnail. */
35
    protected $url;
36
37
    /** @var Finder instance. */
38
    protected $finder;
39
40
    /** @var mixed[] Thumbnailer user options. */
41
    protected $options;
42
43
    /** @var mixed[] .$_SERVER */
44
    protected $server;
45
46
    /**
47
     * @param string       $url     User given URL, from where to generate a thumbnail.
48
     * @param mixed[]      $options Thumbnailer user options.
49
     * @param mixed[]|null $server  $_SERVER.
50
     *
51
     * @throws MissingRequirementException
52
     * @throws BadRulesException
53
     * @throws IOException
54
     */
55
    public function __construct(string $url, array $options, ?array $server)
56
    {
57
        ApplicationUtils::checkExtensionRequirements(['gd']);
58
        ApplicationUtils::checkPHPVersion('5.6', PHP_VERSION);
59
60
        $this->url = $url;
61
        $this->server = $server;
62
        $this->finder = FinderFactory::getFinder($url);
63
        $this->finder->setUserOptions($options);
64
        $this->setOptions($options);
65
    }
66
67
    /**
68
     * Set Thumbnailer options from user input.
69
     *
70
     * @param mixed[] $options User options array.
71
     *
72
     * @throws BadRulesException
73
     * @throws IOException
74
     */
75
    protected function setOptions(array $options): void
76
    {
77
        static::checkOptions($options);
78
79
        $this->options[static::DL_OPTION] = ConfigManager::get('settings.default.download_mode', 'DOWNLOAD');
80
81
        foreach ($options as $key => $value) {
82
            // Download option.
83
            if (
84
                $value === WebThumbnailer::DOWNLOAD
85
                || $value === WebThumbnailer::HOTLINK
86
                || $value === WebThumbnailer::HOTLINK_STRICT
87
            ) {
88
                $this->options[static::DL_OPTION] = $value;
89
                break;
90
            }
91
        }
92
93
        // DL size option
94
        if (
95
            isset($options[WebThumbnailer::DOWNLOAD_MAX_SIZE])
96
            && is_int($options[WebThumbnailer::DOWNLOAD_MAX_SIZE])
97
        ) {
98
            $this->options[WebThumbnailer::DOWNLOAD_MAX_SIZE] = $options[WebThumbnailer::DOWNLOAD_MAX_SIZE];
99
        } else {
100
            $maxdl = ConfigManager::get('settings.default.max_img_dl', 4194304);
101
            $this->options[WebThumbnailer::DOWNLOAD_MAX_SIZE] = $maxdl;
102
        }
103
104
        // DL timeout option
105
        if (
106
            isset($options[WebThumbnailer::DOWNLOAD_TIMEOUT])
107
            && is_int($options[WebThumbnailer::DOWNLOAD_TIMEOUT])
108
        ) {
109
            $this->options[WebThumbnailer::DOWNLOAD_TIMEOUT] = $options[WebThumbnailer::DOWNLOAD_TIMEOUT];
110
        } else {
111
            $timeout = ConfigManager::get('settings.default.timeout', 30);
112
            $this->options[WebThumbnailer::DOWNLOAD_TIMEOUT] = $timeout;
113
        }
114
115
        if (isset($options[WebThumbnailer::NOCACHE])) {
116
            $this->options[WebThumbnailer::NOCACHE] = $options[WebThumbnailer::NOCACHE];
117
        }
118
119
        if (isset($options[WebThumbnailer::CROP])) {
120
            $this->options[WebThumbnailer::CROP] = $options[WebThumbnailer::CROP];
121
        } else {
122
            $this->options[WebThumbnailer::CROP] = false;
123
        }
124
125
        if (isset($options[WebThumbnailer::DEBUG])) {
126
            $this->options[WebThumbnailer::DEBUG] = $options[WebThumbnailer::DEBUG];
127
        } else {
128
            $this->options[WebThumbnailer::DEBUG] = false;
129
        }
130
131
        // Image size
132
        $this->setSizeOptions($options);
133
    }
134
135
    /**
136
     * Make sure user options are coherent.
137
     *   - Only one thumb mode can be defined.
138
     *
139
     * @param mixed[] $options User options array.
140
     *
141
     * @return bool True if the check is successful.
142
     *
143
     * @throws BadRulesException Invalid options.
144
     */
145
    protected static function checkOptions(array $options): bool
146
    {
147
        $incompatibleFlagsList = [
148
            [WebThumbnailer::DOWNLOAD, WebThumbnailer::HOTLINK, WebThumbnailer::HOTLINK_STRICT],
149
        ];
150
151
        foreach ($incompatibleFlagsList as $incompatibleFlags) {
152
            if (count(array_intersect($incompatibleFlags, $options)) > 1) {
153
                $error = 'Only one of these flags can be set between: ';
154
                foreach ($incompatibleFlags as $flag) {
155
                    $error .= $flag . ' ';
156
                }
157
                throw new BadRulesException($error);
158
            }
159
        }
160
161
        return true;
162
    }
163
164
    /**
165
     * Set specific size option, allowing 'meta' size SMALL, MEDIUM, etc.
166
     *
167
     * @param mixed[] $options User options array.
168
     *
169
     * @throws BadRulesException
170
     * @throws IOException
171
     */
172
    protected function setSizeOptions(array $options): void
173
    {
174
        foreach ([WebThumbnailer::MAX_WIDTH, WebThumbnailer::MAX_HEIGHT] as $parameter) {
175
            $value = 0;
176
            if (!empty($options[$parameter])) {
177
                if (SizeUtils::isMetaSize((string) $options[$parameter])) {
178
                    $value = SizeUtils::getMetaSize((string) $options[$parameter]);
179
                } elseif (is_int($options[$parameter]) || ctype_digit($options[$parameter])) {
180
                    $value = $options[$parameter];
181
                }
182
            }
183
184
            $this->options[$parameter] = $value;
185
        }
186
187
        if ($this->options[WebThumbnailer::MAX_WIDTH] == 0 && $this->options[WebThumbnailer::MAX_HEIGHT] == 0) {
188
            $maxwidth = ConfigManager::get('settings.default.max_width', 160);
189
            $this->options[WebThumbnailer::MAX_WIDTH] = $maxwidth;
190
            $maxheight = ConfigManager::get('settings.default.max_height', 160);
191
            $this->options[WebThumbnailer::MAX_HEIGHT] = $maxheight;
192
        }
193
    }
194
195
    /**
196
     * Get the thumbnail according to download mode:
197
     *   - HOTLINK_STRICT: will only try to get hotlink thumb.
198
     *   - HOTLINK: will retrieve hotlink if available, or download otherwise.
199
     *   - DOWNLOAD: will download the thumb, resize it, and store it in cache.
200
     *
201
     * Default mode: DOWNLOAD.
202
     *
203
     * @return string|false The thumbnail URL (relative if downloaded), or false if no thumb found.
204
     *
205
     * @throws DownloadException
206
     * @throws ImageConvertException
207
     * @throws NotAnImageException
208
     * @throws ThumbnailNotFoundException
209
     * @throws IOException
210
     * @throws CacheException
211
     * @throws BadRulesException
212
     */
213
    public function getThumbnail()
214
    {
215
        $cache = CacheManager::getCacheFilePath(
216
            $this->url,
217
            $this->finder->getDomain(),
218
            CacheManager::TYPE_FINDER,
219
            $this->options[WebThumbnailer::MAX_WIDTH],
220
            $this->options[WebThumbnailer::MAX_HEIGHT]
221
        );
222
        // Loading Finder result from cache if enabled and valid to prevent useless requests.
223
        if (
224
            empty($this->options[WebThumbnailer::NOCACHE])
225
            && CacheManager::isCacheValid($cache, $this->finder->getDomain(), CacheManager::TYPE_FINDER)
226
        ) {
227
            $thumbUrl = file_get_contents($cache);
228
        } else {
229
            $thumbUrl = $this->finder->find();
230
            $thumbUrl = $thumbUrl !== false ? html_entity_decode($thumbUrl) : $thumbUrl;
231
            file_put_contents($cache, $thumbUrl);
232
        }
233
234
        if (empty($thumbUrl)) {
235
            $error = 'No thumbnail could be found using ' . $this->finder->getName() . ' finder: ' . $this->url;
236
            throw new ThumbnailNotFoundException($error);
237
        }
238
239
        // Only hotlink, find() is enough.
240
        if ($this->options[static::DL_OPTION] === WebThumbnailer::HOTLINK_STRICT) {
241
            return $this->thumbnailStrictHotlink($thumbUrl);
242
        }
243
        // Hotlink if available, download otherwise.
244
        if ($this->options[static::DL_OPTION] === WebThumbnailer::HOTLINK) {
245
            return $this->thumbnailHotlink($thumbUrl);
246
        } else { // Download
247
            return $this->thumbnailDownload($thumbUrl);
248
        }
249
    }
250
251
    /**
252
     * Get thumbnails in HOTLINK_STRICT mode.
253
     * Won't work for domains which doesn't allow hotlinking.
254
     *
255
     * @param string $thumbUrl Thumbnail URL, generated by the Finder.
256
     *
257
     * @return string|false The thumbnail URL, or false if hotlinking is disabled.
258
     *
259
     * @throws ThumbnailNotFoundException Hotlink is disabled for this domains.
260
     */
261
    protected function thumbnailStrictHotlink(string $thumbUrl)
262
    {
263
        if (!$this->finder->isHotlinkAllowed()) {
264
            throw new ThumbnailNotFoundException('Hotlink is not supported for this URL.');
265
        }
266
267
        return $thumbUrl;
268
    }
269
270
    /**
271
     * Get thumbnails in HOTLINK mode.
272
     *
273
     * @param string $thumbUrl Thumbnail URL, generated by the Finder.
274
     *
275
     * @return string|bool The thumbnail URL, or false if no thumb found.
276
     *
277
     * @throws DownloadException
278
     * @throws ImageConvertException
279
     * @throws NotAnImageException
280
     * @throws IOException
281
     * @throws CacheException
282
     * @throws BadRulesException
283
     */
284
    protected function thumbnailHotlink(string $thumbUrl)
285
    {
286
        if (!$this->finder->isHotlinkAllowed()) {
287
            return $this->thumbnailDownload($thumbUrl);
288
        }
289
290
        return $thumbUrl;
291
    }
292
293
    /**
294
     * Get thumbnails in HOTLINK mode.
295
     *
296
     * @param string $thumbUrl Thumbnail URL, generated by the Finder.
297
     *
298
     * @return string|false The thumbnail URL, or false if no thumb found.
299
     *
300
     * @throws DownloadException     Couldn't download the image
301
     * @throws ImageConvertException Thumbnail not generated
302
     * @throws NotAnImageException
303
     * @throws IOException
304
     * @throws CacheException
305
     * @throws BadRulesException
306
     */
307
    protected function thumbnailDownload(string $thumbUrl)
308
    {
309
        // Cache file path.
310
        $thumbPath = CacheManager::getCacheFilePath(
311
            $thumbUrl,
312
            $this->finder->getDomain(),
313
            CacheManager::TYPE_THUMB,
314
            $this->options[WebThumbnailer::MAX_WIDTH],
315
            $this->options[WebThumbnailer::MAX_HEIGHT],
316
            $this->options[WebThumbnailer::CROP]
317
        );
318
319
        // If the cache is valid, serve it.
320
        if (
321
            empty($this->options[WebThumbnailer::NOCACHE])
322
            && CacheManager::isCacheValid(
323
                $thumbPath,
324
                $this->finder->getDomain(),
325
                CacheManager::TYPE_THUMB
326
            )
327
        ) {
328
            return $thumbPath;
329
        }
330
331
        $webaccess = WebAccessFactory::getWebAccess($thumbUrl);
332
333
        // Download the thumb.
334
        list($headers, $data) = $webaccess->getContent(
335
            $thumbUrl,
336
            $this->options[WebThumbnailer::DOWNLOAD_TIMEOUT],
337
            $this->options[WebThumbnailer::DOWNLOAD_MAX_SIZE]
338
        );
339
340
        if (strpos($headers[0], '200') === false) {
341
            throw new DownloadException(
342
                'Unreachable thumbnail URL. HTTP ' . $headers[0] . '.' . PHP_EOL .
343
                ' - thumbnail URL: ' . $thumbUrl
344
            );
345
        }
346
347
        if (empty($data)) {
348
            throw new DownloadException('Couldn\'t download the thumbnail at ' . $thumbUrl);
349
        }
350
351
        // Resize and save it locally.
352
        ImageUtils::generateThumbnail(
353
            $data,
354
            $thumbPath,
355
            $this->options[WebThumbnailer::MAX_WIDTH],
356
            $this->options[WebThumbnailer::MAX_HEIGHT],
357
            $this->options[WebThumbnailer::CROP]
358
        );
359
360
        if (!is_file($thumbPath)) {
361
            throw new ImageConvertException('Thumbnail was not generated.');
362
        }
363
364
        return $thumbPath;
365
    }
366
}
367