Passed
Push — v4 ( 7fc762...711ea3 )
by Andrew
14:46 queued 07:41
created

OptimizedImage::pictureTag()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 1
c 3
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * Image Optimize plugin for Craft CMS
4
 *
5
 * Automatically optimize images after they've been transformed
6
 *
7
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
8
 * @copyright Copyright (c) 2017 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
9
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
10
11
namespace nystudio107\imageoptimize\models;
12
13
use Craft;
14
use craft\base\Model;
15
use craft\helpers\Template;
16
use craft\validators\ArrayValidator;
17
use nystudio107\imageoptimize\helpers\Color as ColorHelper;
18
use nystudio107\imageoptimize\helpers\UrlHelper;
19
use nystudio107\imageoptimize\ImageOptimize;
20
use function strlen;
21
22
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
23
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
24
 * @package   ImageOptimize
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
25
 * @since     1.2.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
26
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
27
class OptimizedImage extends Model
28
{
29
    // Public Properties
30
    // =========================================================================
31
32
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
33
     * @var ?array An array of optimized image variant URLs
34
     */
35
    public ?array $optimizedImageUrls = [];
36
37
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
38
     * @var ?array An array of optimized .webp image variant URLs
39
     */
40
    public ?array $optimizedWebPImageUrls = [];
41
42
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
43
     * @var ?array An array of the widths of the optimized image variants
44
     */
45
    public ?array $variantSourceWidths = [];
46
47
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
48
     * @var ?array An array of the heights of the optimized image variants
49
     */
50
    public ?array $variantHeights = [];
51
52
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
53
     * @var array An array of the x,y image focal point coords, ranging from 0.0 to 1.0
54
     */
55
    public ?array $focalPoint = [];
56
57
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
58
     * @var int The width of the original source image
59
     */
60
    public ?int $originalImageWidth = 0;
61
62
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
63
     * @var ?int The height of the original source image
64
     */
65
    public ?int $originalImageHeight = 0;
66
67
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
68
     * @var ?string The base64 encoded placeholder LQIP image
69
     */
70
    public ?string $placeholder = '';
71
72
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
73
     * @var ?string The base64 encoded placeholder LQIP SVG image
74
     */
75
    public ?string $placeholderSvg = '';
76
77
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
78
     * @var ?array An array the 5 most dominant colors in the image
79
     */
80
    public ?array $colorPalette = [];
81
82
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
83
     * @var ?int The overall lightness of the image, from 0..100
84
     */
85
    public ?int $lightness = 0;
86
87
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
88
     * @var ?int The width of the placeholder image
89
     */
90
    public ?int $placeholderWidth = 0;
91
92
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
93
     * @var ?int The height of the placeholder image
94
     */
95
    public ?int $placeholderHeight = 0;
96
97
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
98
     * @var ?array An array of errors logged when generating the image transforms
99
     */
100
    public ?array $stickyErrors = [];
101
102
    // Public Methods
103
    // =========================================================================
104
105
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
106
     * @inheritdoc
107
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
108
    public function rules(): array
109
    {
110
        return [
111
            ['optimizedImageUrls', ArrayValidator::class],
112
            ['optimizedWebPImageUrls', ArrayValidator::class],
113
            ['variantSourceWidths', ArrayValidator::class],
114
            ['variantHeights', ArrayValidator::class],
115
            ['focalPoint', 'safe'],
116
            ['originalImageWidth', 'integer'],
117
            ['originalImageHeight', 'integer'],
118
            ['placeholder', 'string'],
119
            ['placeholderSvg', 'string'],
120
            ['colorPalette', ArrayValidator::class],
121
            ['placeholderWidth', 'integer'],
122
            ['placeholderHeight', 'integer'],
123
            ['stickyErrors', ArrayValidator::class],
124
        ];
125
    }
126
127
    /**
128
     * Return the first image variant URL or the specific one passed in via
129
     * $width
130
     *
131
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
132
     *
133
     * @return string
134
     */
135
    public function src(int $width = 0): string
136
    {
137
        if (empty($width)) {
138
            return Template::raw(reset($this->optimizedImageUrls));
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedImageUrls can also be of type null; however, parameter $array of reset() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

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

138
            return Template::raw(reset(/** @scrutinizer ignore-type */ $this->optimizedImageUrls));
Loading history...
139
        }
140
141
        return Template::raw($this->optimizedImageUrls[$width] ?? '');
142
    }
143
144
    /**
145
     * Getter for CraftQL
146
     *
147
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
148
     *
149
     * @return string
150
     */
151
    public function getSrc(int $width = 0): string
152
    {
153
        return $this->src($width);
154
    }
155
156
    /**
157
     * Return a string of image URLs and their sizes
158
     *
159
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
160
     *                  srcsets
161
     *
162
     * @return string
163
     */
164
    public function srcset(bool $dpr = false): string
165
    {
166
        return Template::raw($this->getSrcsetFromArray($this->optimizedImageUrls, $dpr));
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedImageUrls can also be of type null; however, parameter $array of nystudio107\imageoptimiz...e::getSrcsetFromArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

166
        return Template::raw($this->getSrcsetFromArray(/** @scrutinizer ignore-type */ $this->optimizedImageUrls, $dpr));
Loading history...
167
    }
168
169
    /**
170
     * Getter for CraftQL
171
     *
172
     * @param bool $dpr
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
173
     *
174
     * @return string
175
     */
176
    public function getSrcset(bool $dpr = false): string
177
    {
178
        return $this->srcset($dpr);
179
    }
180
181
    /**
182
     * Return a string of image URLs and their sizes that match $width
183
     *
184
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
185
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
186
     *                  srcsets
187
     *
188
     * @return string
189
     */
190
    public function srcsetWidth(int $width, bool $dpr = false): string
191
    {
192
        $subset = $this->getSrcsetSubsetArray($this->optimizedImageUrls, $width, 'width');
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedImageUrls can also be of type null; however, parameter $set of nystudio107\imageoptimiz...:getSrcsetSubsetArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

192
        $subset = $this->getSrcsetSubsetArray(/** @scrutinizer ignore-type */ $this->optimizedImageUrls, $width, 'width');
Loading history...
193
194
        return Template::raw($this->getSrcsetFromArray($subset, $dpr));
195
    }
196
197
    /**
198
     * Return a string of image URLs and their sizes that are at least $width
199
     * or larger
200
     *
201
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
202
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
203
     *                  srcsets
204
     *
205
     * @return string
206
     */
207
    public function srcsetMinWidth(int $width, bool $dpr = false): string
208
    {
209
        $subset = $this->getSrcsetSubsetArray($this->optimizedImageUrls, $width, 'minwidth');
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedImageUrls can also be of type null; however, parameter $set of nystudio107\imageoptimiz...:getSrcsetSubsetArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

209
        $subset = $this->getSrcsetSubsetArray(/** @scrutinizer ignore-type */ $this->optimizedImageUrls, $width, 'minwidth');
Loading history...
210
211
        return Template::raw($this->getSrcsetFromArray($subset, $dpr));
212
    }
213
214
    /**
215
     * Return a string of image URLs and their sizes that are $width or smaller
216
     *
217
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
218
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
219
     *                  srcsets
220
     *
221
     * @return string
222
     */
223
    public function srcsetMaxWidth(int $width, bool $dpr = false): string
224
    {
225
        $subset = $this->getSrcsetSubsetArray($this->optimizedImageUrls, $width, 'maxwidth');
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedImageUrls can also be of type null; however, parameter $set of nystudio107\imageoptimiz...:getSrcsetSubsetArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

225
        $subset = $this->getSrcsetSubsetArray(/** @scrutinizer ignore-type */ $this->optimizedImageUrls, $width, 'maxwidth');
Loading history...
226
227
        return Template::raw($this->getSrcsetFromArray($subset, $dpr));
228
    }
229
230
    /**
231
     * Return the first webp image variant URL or the specific one passed in
232
     * via $width
233
     *
234
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
235
     *
236
     * @return string
237
     */
238
    public function srcWebp(int $width = 0): string
239
    {
240
        if (empty($width)) {
241
            return Template::raw(reset($this->optimizedWebPImageUrls));
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedWebPImageUrls can also be of type null; however, parameter $array of reset() does only seem to accept array|object, maybe add an additional type check? ( Ignorable by Annotation )

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

241
            return Template::raw(reset(/** @scrutinizer ignore-type */ $this->optimizedWebPImageUrls));
Loading history...
242
        }
243
244
        return Template::raw($this->optimizedWebPImageUrls[$width] ?? '');
245
    }
246
247
    /**
248
     * Getter for CraftQL
249
     *
250
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
251
     *
252
     * @return string
253
     */
254
    public function getSrcWebp(int $width = 0): string
255
    {
256
        return $this->srcWebp($width);
257
    }
258
259
    /**
260
     * Return a string of webp image URLs and their sizes
261
     *
262
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
263
     *                  srcsets
264
     *
265
     * @return string
266
     */
267
    public function srcsetWebp(bool $dpr = false): string
268
    {
269
        return Template::raw($this->getSrcsetFromArray($this->optimizedWebPImageUrls, $dpr));
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedWebPImageUrls can also be of type null; however, parameter $array of nystudio107\imageoptimiz...e::getSrcsetFromArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

269
        return Template::raw($this->getSrcsetFromArray(/** @scrutinizer ignore-type */ $this->optimizedWebPImageUrls, $dpr));
Loading history...
270
    }
271
272
    /**
273
     * Getter for CraftQL
274
     *
275
     * @param bool $dpr
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
276
     *
277
     * @return string
278
     */
279
    public function getSrcsetWebp(bool $dpr = false): string
280
    {
281
        return $this->srcsetWebp($dpr);
282
    }
283
284
    /**
285
     * Return a string of webp image URLs and their sizes that match $width
286
     *
287
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
288
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
289
     *                  srcsets
290
     *
291
     * @return string
292
     */
293
    public function srcsetWidthWebp(int $width, bool $dpr = false): string
294
    {
295
        $subset = $this->getSrcsetSubsetArray($this->optimizedWebPImageUrls, $width, 'width');
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedWebPImageUrls can also be of type null; however, parameter $set of nystudio107\imageoptimiz...:getSrcsetSubsetArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

295
        $subset = $this->getSrcsetSubsetArray(/** @scrutinizer ignore-type */ $this->optimizedWebPImageUrls, $width, 'width');
Loading history...
296
297
        return Template::raw($this->getSrcsetFromArray($subset, $dpr));
298
    }
299
300
    /**
301
     * Return a string of webp image URLs and their sizes that are at least
302
     * $width or larger
303
     *
304
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
305
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
306
     *                  srcsets
307
     *
308
     * @return string
309
     */
310
    public function srcsetMinWidthWebp(int $width, bool $dpr = false): string
311
    {
312
        $subset = $this->getSrcsetSubsetArray($this->optimizedWebPImageUrls, $width, 'minwidth');
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedWebPImageUrls can also be of type null; however, parameter $set of nystudio107\imageoptimiz...:getSrcsetSubsetArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

312
        $subset = $this->getSrcsetSubsetArray(/** @scrutinizer ignore-type */ $this->optimizedWebPImageUrls, $width, 'minwidth');
Loading history...
313
314
        return Template::raw($this->getSrcsetFromArray($subset, $dpr));
315
    }
316
317
    /**
318
     * Return a string of webp image URLs and their sizes that are $width or
319
     * smaller
320
     *
321
     * @param int $width
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
322
     * @param bool $dpr Whether to generate 1x, 2x srcsets vs the normal XXXw
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
323
     *                  srcsets
324
     *
325
     * @return string
326
     */
327
    public function srcsetMaxWidthWebp(int $width, bool $dpr = false): string
328
    {
329
        $subset = $this->getSrcsetSubsetArray($this->optimizedWebPImageUrls, $width, 'maxwidth');
0 ignored issues
show
Bug introduced by
It seems like $this->optimizedWebPImageUrls can also be of type null; however, parameter $set of nystudio107\imageoptimiz...:getSrcsetSubsetArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

329
        $subset = $this->getSrcsetSubsetArray(/** @scrutinizer ignore-type */ $this->optimizedWebPImageUrls, $width, 'maxwidth');
Loading history...
330
331
        return Template::raw($this->getSrcsetFromArray($subset, $dpr));
332
    }
333
334
    /**
335
     * Work around issues with `<img srcset>` returning sizes larger than are
336
     * available as per:
337
     * https://medium.com/@MRWwebDesign/responsive-images-the-sizes-attribute-and-unexpected-image-sizes-882a2eadb6db
338
     *
339
     * @return int
340
     */
341
    public function maxSrcsetWidth(): int
342
    {
343
        $result = 0;
344
        if (!empty($this->optimizedImageUrls)) {
345
            $tempArray = $this->optimizedImageUrls;
346
            ksort($tempArray, SORT_NUMERIC);
347
348
            $keys = array_keys($tempArray);
349
            $result = end($keys);
350
        }
351
352
        return $result;
353
    }
354
355
    /**
356
     * Getter for CraftQL
357
     *
358
     * @return int
359
     */
360
    public function getMaxSrcsetWidth(): int
361
    {
362
        return $this->maxSrcsetWidth();
363
    }
364
365
    /**
366
     * Return the colors as an array of RGB colors
367
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
368
    public function colorPaletteRgb(): array
369
    {
370
        $colors = [];
371
372
        foreach ($this->colorPalette as $color) {
373
            if (!empty($color)) {
374
                $colors[] = ColorHelper::HTMLToRGB($color);
375
            }
376
        }
377
378
        return $colors;
379
    }
380
381
    /**
382
     * Generate a complete <link rel="preload"> tag for this OptimizedImages model
383
     * ref: https://web.dev/preload-responsive-images/#imagesrcset-and-imagesizes
384
     *
385
     * @return LinkPreloadTag
386
     */
387
    public function linkPreloadTag(): LinkPreloadTag
388
    {
389
        return new LinkPreloadTag(['optimizedImage' => $this]);
390
    }
391
392
    /**
393
     * Generate a complete <img> tag for this OptimizedImage model
394
     *
395
     * @return ImgTag
396
     */
397
    public function imgTag(): ImgTag
398
    {
399
        return new ImgTag(['optimizedImage' => $this]);
400
    }
401
402
    /**
403
     * Generate a complete <picture> tag for this OptimizedImage model
404
     *
405
     * @return PictureTag
406
     */
407
    public function pictureTag(): PictureTag
408
    {
409
        return new PictureTag(['optimizedImage' => $this]);
410
    }
411
412
    /**
413
     * Return a base64-encoded placeholder image
414
     *
415
     * @return string
416
     */
417
    public function placeholderImage(): string
418
    {
419
        $header = 'data:image/jpeg;base64,';
420
        if (!empty($this->placeholder)) {
421
            $content = $this->placeholder;
422
        } else {
423
            // At least return something
424
            return $this->defaultPlaceholderImage();
425
        }
426
427
        return Template::raw($header . rawurlencode($content));
428
    }
429
430
    /**
431
     * Getter for CraftQL
432
     *
433
     * @return string
434
     */
435
    public function getPlaceholderImage(): string
436
    {
437
        return $this->placeholderImage();
438
    }
439
440
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
441
     * @return string
442
     */
443
    public function placeholderImageSize(): string
444
    {
445
        $placeholder = $this->placeholderImage();
446
        $contentLength = !empty(strlen($placeholder)) ? strlen($placeholder) : 0;
447
448
        return ImageOptimize::$plugin->optimize->humanFileSize($contentLength, 1);
449
    }
450
451
    /**
452
     * Return an SVG box as a placeholder image
453
     *
454
     * @param string|null $color
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
455
     *
456
     * @return string
457
     */
458
    public function placeholderBox(?string $color = null): string
459
    {
460
        $width = $this->placeholderWidth ?? 1;
461
        $height = $this->placeholderHeight ?? 1;
462
        $color = $color ?? $this->colorPalette[0] ?? '#CCC';
463
464
        return Template::raw(ImageOptimize::$plugin->placeholder->generatePlaceholderBox($width, $height, $color));
465
    }
466
467
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
468
     * @param string|null $color
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
469
     *
470
     * @return string
471
     */
472
    public function getPlaceholderBox(string $color = null): string
473
    {
474
        return $this->placeholderBox($color);
475
    }
476
477
    /**
478
     * Getter for CraftQL
479
     *
480
     * @return string
481
     */
482
    public function placeholderBoxSize(): string
483
    {
484
        $placeholder = $this->placeholderBox();
485
        $contentLength = !empty(strlen($placeholder)) ? strlen($placeholder) : 0;
486
487
        return ImageOptimize::$plugin->optimize->humanFileSize($contentLength, 1);
488
    }
489
490
    /**
491
     * Return a silhouette of the image as an SVG placeholder
492
     *
493
     * @return string
494
     */
495
    public function placeholderSilhouette(): string
496
    {
497
        $header = 'data:image/svg+xml,';
498
        if (!empty($this->placeholderSvg)) {
499
            $content = $this->placeholderSvg;
500
        } else {
501
            // At least return something
502
            return $this->defaultPlaceholderImage();
503
        }
504
505
        return Template::raw($header . $content);
506
    }
507
508
    /**
509
     * Getter for CraftQL
510
     *
511
     * @return string
512
     */
513
    public function getPlaceholderSilhouette(): string
514
    {
515
        return $this->placeholderSilhouette();
516
    }
517
518
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
519
     * @return string
520
     */
521
    public function placeholderSilhouetteSize(): string
522
    {
523
        $placeholder = $this->placeholderSilhouette();
524
        $contentLength = !empty(strlen($placeholder)) ? strlen($placeholder) : 0;
525
526
        return ImageOptimize::$plugin->optimize->humanFileSize($contentLength, 1);
527
    }
528
529
    /**
530
     *  Get the file size of any remote resource (using curl),
531
     *  either in bytes or - default - as human-readable formatted string.
532
     *
533
     * @param string $url Takes the remote object's URL.
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 1 found
Loading history...
534
     * @param bool $formatSize Whether to return size in bytes or
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
535
     *                              formatted.
0 ignored issues
show
Coding Style introduced by
Parameter comment not aligned correctly; expected 25 spaces but found 30
Loading history...
536
     * @param bool $useHead Whether to use HEAD requests. If false,
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
537
     *                              uses GET.
0 ignored issues
show
Coding Style introduced by
Parameter comment not aligned correctly; expected 22 spaces but found 30
Loading history...
538
     *
539
     * @return  mixed    Returns human-readable formatted size
0 ignored issues
show
Coding Style introduced by
Tag value for @return tag indented incorrectly; expected 7 spaces but found 2
Loading history...
540
     *                              or size in bytes (default: formatted).
541
     * @noinspection PhpComposerExtensionStubsInspection*@author  Stephan Schmitz <[email protected]>
542
     * @license MIT <http://eyecatchup.mit-license.org/>
0 ignored issues
show
Coding Style introduced by
Tag value for @license tag indented incorrectly; expected 6 spaces but found 1
Loading history...
543
     * @url     <https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d>
0 ignored issues
show
Coding Style introduced by
Tag value for @url tag indented incorrectly; expected 10 spaces but found 5
Loading history...
544
     *
545
     * @noinspection PhpComposerExtensionStubsInspection
0 ignored issues
show
Coding Style introduced by
Tags must be grouped together in a doc comment
Loading history...
546
     */
547
    public function getRemoteFileSize(string $url, bool $formatSize = true, bool $useHead = true): mixed
548
    {
549
        // Get an absolute URL with protocol that curl will be happy with
550
        $url = UrlHelper::absoluteUrlWithProtocol($url);
551
        $ch = curl_init($url);
552
        /** @noinspection CurlSslServerSpoofingInspection */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
553
        curl_setopt_array($ch, [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
554
            CURLOPT_RETURNTRANSFER => 1,
555
            CURLOPT_FOLLOWLOCATION => 1,
556
            CURLOPT_SSL_VERIFYPEER => 0,
557
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
558
        if ($useHead) {
559
            curl_setopt($ch, CURLOPT_NOBODY, 1);
560
        }
561
        curl_exec($ch);
562
        // content-length of download (in bytes), read from Content-Length: field
563
        $contentLength = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
564
        $error = curl_error($ch);
565
        if (!empty($error)) {
566
            Craft::error($error, __METHOD__);
567
        }
568
        curl_close($ch);
569
        // cannot retrieve file size, return "-1"
570
        if (!$contentLength) {
571
            return -1;
572
        }
573
        // return size in bytes
574
        if (!$formatSize) {
575
            return $contentLength;
576
        }
577
578
        return ImageOptimize::$plugin->optimize->humanFileSize($contentLength, 1);
579
    }
580
581
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
582
     * @param array $array
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
583
     * @param bool $dpr
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
584
     *
585
     * @return string
586
     */
587
    public function getSrcsetFromArray(array $array, bool $dpr = false): string
588
    {
589
        $srcset = '';
590
        foreach ($array as $key => $value) {
591
            if ($dpr) {
592
                $descriptor = '1x';
593
                if (!empty($array[(int)$key / 2])) {
594
                    $descriptor = '2x';
595
                }
596
                if (!empty($array[(int)$key / 3])) {
597
                    $descriptor = '3x';
598
                }
599
            } else {
600
                $descriptor = $key . 'w';
601
            }
602
            $srcset .= $value . ' ' . $descriptor . ', ';
603
        }
604
605
        return rtrim($srcset, ', ');
606
    }
607
608
    // Protected Methods
609
    // =========================================================================
610
611
    protected function getSrcsetSubsetArray(array $set, int $width, string $comparison): array
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
612
    {
613
        $subset = [];
614
        $index = 0;
615
        if (empty($this->variantSourceWidths)) {
616
            return $subset;
617
        }
618
        foreach ($this->variantSourceWidths as $variantSourceWidth) {
619
            $match = false;
620
            switch ($comparison) {
621
                case 'width':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
622
                    if ($variantSourceWidth == $width) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
623
                        $match = true;
624
                    }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
625
                    break;
626
627
                case 'minwidth':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
628
                    if ($variantSourceWidth >= $width) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
629
                        $match = true;
630
                    }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
631
                    break;
632
633
                case 'maxwidth':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
634
                    if ($variantSourceWidth <= $width) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
635
                        $match = true;
636
                    }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
637
                    break;
638
            }
639
            if ($match) {
640
                $subset += array_slice($set, $index, 1, true);
641
            }
642
            $index++;
643
        }
644
645
        return $subset;
646
    }
647
648
    /**
649
     * Return a default placeholder image
650
     *
651
     * @return string
652
     */
653
    protected function defaultPlaceholderImage(): string
654
    {
655
        $width = 1;
656
        $height = 1;
657
        $color = '#CCC';
658
659
        return Template::raw(ImageOptimize::$plugin->placeholder->generatePlaceholderBox($width, $height, $color));
660
    }
661
662
    /**
663
     * Swap the tag attributes to work with lazy loading
664
     * ref: https://web.dev/native-lazy-loading/#how-do-i-handle-browsers-that-don't-yet-support-native-lazy-loading
665
     *
666
     * @param string $loading 'eager', 'lazy', 'lazySizes', 'lazySizesFallback'
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
667
     * @param string $placeHolder 'box', 'color', 'image', 'silhouette'
668
     * @param array $attrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
669
     *
670
     * @return array
671
     */
672
    protected function swapLazyLoadAttrs(string $loading, string $placeHolder, array $attrs): array
673
    {
674
        // Set the class and loading attributes
675
        if (isset($attrs['class'])) {
676
            $attrs['class'] = trim($attrs['class'] . ' lazyload');
677
        }
678
        // Set the style on this element to be the placeholder image as the background-image
679
        if (isset($attrs['style']) && !empty($attrs['src'])) {
680
            $attrs['style'] = trim(
681
                $attrs['style'] .
682
                'background-image:url(' . $this->getLazyLoadSrc($placeHolder) . '); background-size: cover;'
683
            );
684
        }
685
        // Handle attributes that lazy  and lazySizesFallback have in common
686
        switch ($loading) {
687
            case 'lazy':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
688
            case 'lazySizesFallback':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
689
                if (isset($attrs['loading'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
690
                    $attrs['loading'] = 'lazy';
691
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
692
                break;
693
            default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
694
                break;
695
        }
696
        // Handle attributes that lazySizes and lazySizesFallback have in common
697
        switch ($loading) {
698
            case 'lazySizes':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
699
            case 'lazySizesFallback':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
700
                // Only swap to data- attributes if they want the LazySizes fallback
701
                if (!empty($attrs['sizes'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
702
                    $attrs['data-sizes'] = $attrs['sizes'];
703
                    $attrs['sizes'] = '';
704
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
705
                if (!empty($attrs['srcset'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
706
                    $attrs['data-srcset'] = $attrs['srcset'];
707
                    $attrs['srcset'] = '';
708
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
709
                if (!empty($attrs['src'])) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
710
                    $attrs['data-src'] = $attrs['src'];
711
                    $attrs['src'] = $this->getLazyLoadSrc($placeHolder);
712
                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
713
                break;
714
            default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
715
                break;
716
        }
717
718
        return $attrs;
719
    }
720
721
    /**
722
     * Return a lazy loading placeholder image based on the passed in $lazyload setting
723
     *
724
     * @param string $lazyLoad
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
725
     *
726
     * @return string
727
     */
728
    protected function getLazyLoadSrc(string $lazyLoad): string
729
    {
730
        $lazyLoad = strtolower($lazyLoad);
731
        return match ($lazyLoad) {
732
            'image' => $this->getPlaceholderImage(),
733
            'silhouette' => $this->getPlaceholderSilhouette(),
734
            'color' => $this->getPlaceholderBox($this->colorPalette[0] ?? null),
735
            default => $this->getPlaceholderBox('#CCC'),
736
        };
737
    }
738
}
739