Thumbnailer::setOptions()   C
last analyzed

Complexity

Conditions 13
Paths 192

Size

Total Lines 65
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 38
dl 0
loc 65
rs 5.85
c 0
b 0
f 0
cc 13
nc 192
nop 1

How to fix   Long Method    Complexity   

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('7.1', 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
        // Resize mode, defaults to resample
132
        if (isset($options[WebThumbnailer::RESIZE_MODE])) {
133
            $this->options[WebThumbnailer::RESIZE_MODE] = $options[WebThumbnailer::RESIZE_MODE];
134
        } else {
135
            $this->options[WebThumbnailer::RESIZE_MODE] = WebThumbnailer::RESAMPLE;
136
        }
137
138
        // Image size
139
        $this->setSizeOptions($options);
140
    }
141
142
    /**
143
     * Make sure user options are coherent.
144
     *   - Only one thumb mode can be defined.
145
     *
146
     * @param mixed[] $options User options array.
147
     *
148
     * @return bool True if the check is successful.
149
     *
150
     * @throws BadRulesException Invalid options.
151
     */
152
    protected static function checkOptions(array $options): bool
153
    {
154
        $incompatibleFlagsList = [
155
            [WebThumbnailer::DOWNLOAD, WebThumbnailer::HOTLINK, WebThumbnailer::HOTLINK_STRICT],
156
        ];
157
158
        foreach ($incompatibleFlagsList as $incompatibleFlags) {
159
            if (count(array_intersect($incompatibleFlags, $options)) > 1) {
160
                $error = 'Only one of these flags can be set between: ';
161
                foreach ($incompatibleFlags as $flag) {
162
                    $error .= $flag . ' ';
163
                }
164
                throw new BadRulesException($error);
165
            }
166
        }
167
168
        return true;
169
    }
170
171
    /**
172
     * Set specific size option, allowing 'meta' size SMALL, MEDIUM, etc.
173
     *
174
     * @param mixed[] $options User options array.
175
     *
176
     * @throws BadRulesException
177
     * @throws IOException
178
     */
179
    protected function setSizeOptions(array $options): void
180
    {
181
        foreach ([WebThumbnailer::MAX_WIDTH, WebThumbnailer::MAX_HEIGHT] as $parameter) {
182
            $value = 0;
183
            if (!empty($options[$parameter])) {
184
                if (SizeUtils::isMetaSize((string) $options[$parameter])) {
185
                    $value = SizeUtils::getMetaSize((string) $options[$parameter]);
186
                } elseif (is_int($options[$parameter]) || ctype_digit($options[$parameter])) {
187
                    $value = $options[$parameter];
188
                }
189
            }
190
191
            $this->options[$parameter] = $value;
192
        }
193
194
        if ($this->options[WebThumbnailer::MAX_WIDTH] == 0 && $this->options[WebThumbnailer::MAX_HEIGHT] == 0) {
195
            $maxwidth = ConfigManager::get('settings.default.max_width', 160);
196
            $this->options[WebThumbnailer::MAX_WIDTH] = $maxwidth;
197
            $maxheight = ConfigManager::get('settings.default.max_height', 160);
198
            $this->options[WebThumbnailer::MAX_HEIGHT] = $maxheight;
199
        }
200
    }
201
202
    /**
203
     * Get the thumbnail according to download mode:
204
     *   - HOTLINK_STRICT: will only try to get hotlink thumb.
205
     *   - HOTLINK: will retrieve hotlink if available, or download otherwise.
206
     *   - DOWNLOAD: will download the thumb, resize it, and store it in cache.
207
     *
208
     * Default mode: DOWNLOAD.
209
     *
210
     * @return string|false The thumbnail URL (relative if downloaded), or false if no thumb found.
211
     *
212
     * @throws DownloadException
213
     * @throws ImageConvertException
214
     * @throws NotAnImageException
215
     * @throws ThumbnailNotFoundException
216
     * @throws IOException
217
     * @throws CacheException
218
     * @throws BadRulesException
219
     */
220
    public function getThumbnail()
221
    {
222
        $cache = CacheManager::getCacheFilePath(
223
            $this->url,
224
            $this->finder->getDomain(),
225
            CacheManager::TYPE_FINDER,
226
            $this->options[WebThumbnailer::MAX_WIDTH],
227
            $this->options[WebThumbnailer::MAX_HEIGHT]
228
        );
229
        // Loading Finder result from cache if enabled and valid to prevent useless requests.
230
        if (
231
            empty($this->options[WebThumbnailer::NOCACHE])
232
            && CacheManager::isCacheValid($cache, $this->finder->getDomain(), CacheManager::TYPE_FINDER)
233
        ) {
234
            $thumbUrl = file_get_contents($cache);
235
        } else {
236
            $thumbUrl = $this->finder->find();
237
            $thumbUrl = $thumbUrl !== false ? html_entity_decode($thumbUrl) : $thumbUrl;
238
            file_put_contents($cache, $thumbUrl);
239
        }
240
241
        if (empty($thumbUrl)) {
242
            $error = 'No thumbnail could be found using ' . $this->finder->getName() . ' finder: ' . $this->url;
243
            throw new ThumbnailNotFoundException($error);
244
        }
245
246
        // Only hotlink, find() is enough.
247
        if ($this->options[static::DL_OPTION] === WebThumbnailer::HOTLINK_STRICT) {
248
            return $this->thumbnailStrictHotlink($thumbUrl);
249
        }
250
        // Hotlink if available, download otherwise.
251
        if ($this->options[static::DL_OPTION] === WebThumbnailer::HOTLINK) {
252
            return $this->thumbnailHotlink($thumbUrl);
253
        } else { // Download
254
            return $this->thumbnailDownload($thumbUrl);
255
        }
256
    }
257
258
    /**
259
     * Get thumbnails in HOTLINK_STRICT mode.
260
     * Won't work for domains which doesn't allow hotlinking.
261
     *
262
     * @param string $thumbUrl Thumbnail URL, generated by the Finder.
263
     *
264
     * @return string The thumbnail URL.
265
     *
266
     * @throws ThumbnailNotFoundException Hotlink is disabled for this domains.
267
     */
268
    protected function thumbnailStrictHotlink(string $thumbUrl): string
269
    {
270
        if (!$this->finder->isHotlinkAllowed()) {
271
            throw new ThumbnailNotFoundException('Hotlink is not supported for this URL.');
272
        }
273
274
        return $thumbUrl;
275
    }
276
277
    /**
278
     * Get thumbnails in HOTLINK mode.
279
     *
280
     * @param string $thumbUrl Thumbnail URL, generated by the Finder.
281
     *
282
     * @return string|false The thumbnail URL, or false if no thumb found.
283
     *
284
     * @throws DownloadException
285
     * @throws ImageConvertException
286
     * @throws NotAnImageException
287
     * @throws IOException
288
     * @throws CacheException
289
     * @throws BadRulesException
290
     */
291
    protected function thumbnailHotlink(string $thumbUrl)
292
    {
293
        if (!$this->finder->isHotlinkAllowed()) {
294
            return $this->thumbnailDownload($thumbUrl);
295
        }
296
297
        return $thumbUrl;
298
    }
299
300
    /**
301
     * Get thumbnails in HOTLINK mode.
302
     *
303
     * @param string $thumbUrl Thumbnail URL, generated by the Finder.
304
     *
305
     * @return string|false The thumbnail URL, or false if no thumb found.
306
     *
307
     * @throws DownloadException     Couldn't download the image
308
     * @throws ImageConvertException Thumbnail not generated
309
     * @throws NotAnImageException
310
     * @throws IOException
311
     * @throws CacheException
312
     * @throws BadRulesException
313
     */
314
    protected function thumbnailDownload(string $thumbUrl)
315
    {
316
        // Cache file path.
317
        $thumbPath = CacheManager::getCacheFilePath(
318
            $thumbUrl,
319
            $this->finder->getDomain(),
320
            CacheManager::TYPE_THUMB,
321
            $this->options[WebThumbnailer::MAX_WIDTH],
322
            $this->options[WebThumbnailer::MAX_HEIGHT],
323
            $this->options[WebThumbnailer::CROP]
324
        );
325
326
        // If the cache is valid, serve it.
327
        if (
328
            empty($this->options[WebThumbnailer::NOCACHE])
329
            && CacheManager::isCacheValid(
330
                $thumbPath,
331
                $this->finder->getDomain(),
332
                CacheManager::TYPE_THUMB
333
            )
334
        ) {
335
            return $thumbPath;
336
        }
337
338
        $webaccess = WebAccessFactory::getWebAccess($thumbUrl);
339
340
        // Download the thumb.
341
        list($headers, $data) = $webaccess->getContent(
342
            $thumbUrl,
343
            $this->options[WebThumbnailer::DOWNLOAD_TIMEOUT],
344
            $this->options[WebThumbnailer::DOWNLOAD_MAX_SIZE]
345
        );
346
347
        if (strpos($headers[0], '200') === false) {
348
            throw new DownloadException(
349
                'Unreachable thumbnail URL. HTTP ' . $headers[0] . '.' . PHP_EOL .
350
                ' - thumbnail URL: ' . $thumbUrl
351
            );
352
        }
353
354
        if (empty($data)) {
355
            throw new DownloadException('Couldn\'t download the thumbnail at ' . $thumbUrl);
356
        }
357
358
        // Resize and save it locally.
359
        ImageUtils::generateThumbnail(
360
            $data,
361
            $thumbPath,
362
            $this->options[WebThumbnailer::MAX_WIDTH],
363
            $this->options[WebThumbnailer::MAX_HEIGHT],
364
            $this->options[WebThumbnailer::CROP],
365
            $this->options[WebThumbnailer::RESIZE_MODE]
366
        );
367
368
        if (!is_file($thumbPath)) {
369
            throw new ImageConvertException('Thumbnail was not generated.');
370
        }
371
372
        return $thumbPath;
373
    }
374
}
375