Passed
Push — develop ( f60d42...460389 )
by Andrew
05:03
created

Optimize::handleGetAssetUrlEvent()   C

Complexity

Conditions 13
Paths 243

Size

Total Lines 57
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 2 Features 0
Metric Value
eloc 32
c 9
b 2
f 0
dl 0
loc 57
rs 5.2458
cc 13
nc 243
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
 * ImageOptimize 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\services;
12
13
use Craft;
14
use craft\base\Component;
15
use craft\base\Image;
16
use craft\console\Application as ConsoleApplication;
17
use craft\elements\Asset;
18
use craft\errors\ImageException;
19
use craft\errors\VolumeException;
20
use craft\events\AssetTransformImageEvent;
21
use craft\events\GenerateTransformEvent;
22
use craft\events\GetAssetThumbUrlEvent;
23
use craft\events\GetAssetUrlEvent;
24
use craft\events\RegisterComponentTypesEvent;
25
use craft\helpers\Component as ComponentHelper;
26
use craft\helpers\FileHelper;
27
use craft\helpers\Html;
28
use craft\helpers\Image as ImageHelper;
29
use craft\image\Raster;
30
use craft\models\AssetTransform;
31
use craft\models\AssetTransformIndex;
32
use mikehaertl\shellcommand\Command as ShellCommand;
33
use nystudio107\imageoptimize\helpers\PluginTemplate as PluginTemplateHelper;
34
use nystudio107\imageoptimize\ImageOptimize;
35
use nystudio107\imageoptimize\imagetransforms\CraftImageTransform;
36
use nystudio107\imageoptimize\imagetransforms\ImageTransform;
37
use nystudio107\imageoptimize\imagetransforms\ImageTransformInterface;
38
use nystudio107\imageoptimize\models\Settings;
39
use nystudio107\imageoptimizeimgix\imagetransforms\ImgixImageTransform;
40
use nystudio107\imageoptimizesharp\imagetransforms\SharpImageTransform;
41
use nystudio107\imageoptimizethumbor\imagetransforms\ThumborImageTransform;
42
use Throwable;
43
use yii\base\InvalidConfigException;
44
use function function_exists;
45
use function is_array;
46
use function is_string;
47
48
/** @noinspection MissingPropertyAnnotationsInspection */
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...
49
50
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
51
 * @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...
52
 * @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...
53
 * @since     1.0.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...
54
 */
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...
55
class Optimize extends Component
56
{
57
    // Constants
58
    // =========================================================================
59
60
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
61
     * @event RegisterComponentTypesEvent The event that is triggered when registering
62
     *        Image Transform types
63
     *
64
     * Image Transform types must implement [[ImageTransformInterface]]. [[ImageTransform]]
65
     * provides a base implementation.
66
     *
67
     * ```php
68
     * use nystudio107\imageoptimize\services\Optimize;
69
     * use craft\events\RegisterComponentTypesEvent;
70
     * use yii\base\Event;
71
     *
72
     * Event::on(Optimize::class,
73
     *     Optimize::EVENT_REGISTER_IMAGE_TRANSFORM_TYPES,
74
     *     function(RegisterComponentTypesEvent $event) {
75
     *         $event->types[] = MyImageTransform::class;
76
     *     }
77
     * );
78
     * ```
79
     */
80
    const EVENT_REGISTER_IMAGE_TRANSFORM_TYPES = 'registerImageTransformTypes';
81
82
    const DEFAULT_IMAGE_TRANSFORM_TYPES = [
83
        CraftImageTransform::class,
84
        ImgixImageTransform::class,
85
        SharpImageTransform::class,
86
        ThumborImageTransform::class,
87
    ];
88
89
    // Public Methods
90
    // =========================================================================
91
92
    /**
93
     * Returns all available field type classes.
94
     *
95
     * @return string[] The available field type classes
96
     */
97
    public function getAllImageTransformTypes(): array
98
    {
99
        $imageTransformTypes = array_unique(array_merge(
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...
100
            ImageOptimize::$plugin->getSettings()->defaultImageTransformTypes ?? [],
101
            self::DEFAULT_IMAGE_TRANSFORM_TYPES
102
        ), SORT_REGULAR);
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...
103
104
        $event = new RegisterComponentTypesEvent([
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...
105
            'types' => $imageTransformTypes,
106
        ]);
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...
107
        $this->trigger(self::EVENT_REGISTER_IMAGE_TRANSFORM_TYPES, $event);
108
109
        return $event->types;
110
    }
111
112
    /**
113
     * Creates an Image Transform with a given config.
114
     *
115
     * @param mixed $config The Image Transform’s class name, or its config,
116
     *                      with a `type` value and optionally a `settings` value
117
     *
118
     * @return ?ImageTransformInterface The Image Transform
119
     */
120
    public function createImageTransformType($config): ?ImageTransformInterface
121
    {
122
        if (is_string($config)) {
123
            $config = ['type' => $config];
124
        }
125
126
        try {
127
            /** @var ImageTransform $imageTransform */
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...
128
            $imageTransform = ComponentHelper::createComponent($config, ImageTransformInterface::class);
129
        } catch (Throwable $e) {
130
            $imageTransform = null;
131
            Craft::error($e->getMessage(), __METHOD__);
132
        }
133
134
        return $imageTransform;
135
    }
136
137
    /**
138
     * Handle responding to EVENT_GET_ASSET_URL events
139
     *
140
     * @param GetAssetUrlEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
141
     *
142
     * @return null|string
143
     * @throws InvalidConfigException
144
     */
145
    public function handleGetAssetUrlEvent(GetAssetUrlEvent $event)
146
    {
147
        Craft::beginProfile('handleGetAssetUrlEvent', __METHOD__);
148
        $url = null;
149
        if (!ImageOptimize::$plugin->transformMethod instanceof CraftImageTransform) {
150
            $asset = $event->asset;
151
            $transform = $event->transform;
152
            // If the transform is empty in some regard, normalize it to null
153
            if (empty($transform)) {
154
                $transform = null;
155
            }
156
            // If there's no transform requested, return `null` so other plugins have a crack at it
157
            if ($transform === null) {
158
                return null;
159
            }
160
            // If we're passed in null, make a dummy AssetTransform model for Thumbor
161
            // For backwards compatibility
162
            if (ImageOptimize::$plugin->transformMethod instanceof ThumborImageTransform) {
163
                $transform = new AssetTransform([
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...
164
                    'width' => $asset->width,
165
                    'interlace' => 'line',
166
                ]);
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...
167
            }
168
            // If we're passed an array, make an AssetTransform model out of it
169
            if (is_array($transform)) {
170
                $transform = new AssetTransform($transform);
171
            }
172
            // If we're passing in a string, look up the asset transform in the db
173
            if (is_string($transform)) {
174
                $assetTransforms = Craft::$app->getAssetTransforms();
175
                $transform = $assetTransforms->getTransformByHandle($transform);
176
            }
177
            $finalFormat = empty($transform['format']) ? $asset->getExtension() : $transform['format'];
178
            // Normalize the extension to lowercase, for some transform methods that require this
179
            $finalFormat = strtolower($finalFormat);
180
            // Special-case for 'jpeg'
181
            if ($finalFormat === 'jpeg') {
182
                $finalFormat = 'jpg';
183
            }
184
            // If the final format is an SVG, don't attempt to transform it
185
            if ($finalFormat === 'svg') {
186
                return null;
187
            }
188
            // Normalize the extension to lowercase, for some transform methods that require this
189
            if (!empty($transform) && !empty($finalFormat)) {
190
                $format = $transform['format'] ?? null;
191
                $transform['format'] = $format === null ? null : strtolower($finalFormat);
192
            }
193
            // Generate an image transform url
194
            $url = ImageOptimize::$plugin->transformMethod->getTransformUrl(
195
                $asset,
196
                $transform
197
            );
198
        }
199
        Craft::endProfile('handleGetAssetUrlEvent', __METHOD__);
200
201
        return $url;
202
    }
203
204
    /**
205
     * Handle responding to EVENT_GET_ASSET_THUMB_URL events
206
     *
207
     * @param GetAssetThumbUrlEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
208
     *
209
     * @return null|string
210
     */
211
    public function handleGetAssetThumbUrlEvent(GetAssetThumbUrlEvent $event)
212
    {
213
        Craft::beginProfile('handleGetAssetThumbUrlEvent', __METHOD__);
214
        $url = $event->url;
215
        if (!ImageOptimize::$plugin->transformMethod instanceof CraftImageTransform) {
216
            $asset = $event->asset;
217
            if (ImageHelper::canManipulateAsImage($asset->getExtension())) {
218
                $transform = new AssetTransform([
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...
219
                    'width' => $event->width,
220
                    'height' => $event->height,
221
                    'interlace' => 'line',
222
                ]);
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...
223
                /** @var ImageTransform $transformMethod */
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...
224
                $transformMethod = ImageOptimize::$plugin->transformMethod;
225
                $finalFormat = empty($transform['format']) ? $asset->getExtension() : $transform['format'];
226
                // Normalize the extension to lowercase, for some transform methods that require this
227
                $finalFormat = strtolower($finalFormat);
228
                // Special-case for 'jpeg'
229
                if ($finalFormat === 'jpeg') {
230
                    $finalFormat = 'jpg';
231
                }
232
                // If the final format is an SVG, don't attempt to transform it
233
                if ($finalFormat === 'svg') {
234
                    return null;
235
                }
236
                // Normalize the extension to lowercase, for some transform methods that require this
237
                if ($transform !== null && !empty($finalFormat)) {
238
                    $transform['format'] = strtolower($finalFormat);
239
                }
240
                // Generate an image transform url
241
                if ($transformMethod->hasProperty('generateTransformsBeforePageLoad')) {
242
                    // This is a dynamic property that some image transforms have
243
                    /** @phpstan-ignore-next-line */
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...
244
                    $transformMethod->generateTransformsBeforePageLoad = $event->generate;
0 ignored issues
show
Bug Best Practice introduced by
The property generateTransformsBeforePageLoad does not exist on nystudio107\imageoptimiz...ansforms\ImageTransform. Since you implemented __set, consider adding a @property annotation.
Loading history...
245
                }
246
                $url = $transformMethod->getTransformUrl($asset, $transform);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $url is correct as $transformMethod->getTra...Url($asset, $transform) targeting nystudio107\imageoptimiz...form::getTransformUrl() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
247
            }
248
        }
249
        Craft::endProfile('handleGetAssetThumbUrlEvent', __METHOD__);
250
251
        return $url;
252
    }
253
254
    /**
255
     * Returns whether `.webp` is a format supported by the server
256
     *
257
     * @return bool
258
     */
259
    public function serverSupportsWebP(): bool
260
    {
261
        $result = false;
262
        $variantCreators = ImageOptimize::$plugin->optimize->getActiveVariantCreators();
263
        foreach ($variantCreators as $variantCreator) {
264
            if ($variantCreator['creator'] === 'cwebp' && $variantCreator['installed']) {
265
                $result = true;
266
            }
267
        }
268
269
        return $result;
270
    }
271
272
    /**
273
     * Render the LazySizes fallback JS
274
     *
275
     * @param array $scriptAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
276
     * @param array $variables
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
277
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
278
     */
279
    public function renderLazySizesFallbackJs($scriptAttrs = [], $variables = [])
280
    {
281
        $minifier = 'minify';
282
        $vars = array_merge([
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...
283
            'scriptSrc' => 'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.0/lazysizes.min.js',
284
        ],
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 12 spaces, but found 8.
Loading history...
285
            $variables
286
        );
287
        $content = PluginTemplateHelper::renderPluginTemplate(
288
            'frontend/lazysizes-fallback-js',
289
            $vars,
290
            $minifier
291
        );
292
        $content = (string)$content;
293
        if ($scriptAttrs !== null) {
0 ignored issues
show
introduced by
The condition $scriptAttrs !== null is always true.
Loading history...
294
            $attrs = array_merge([
0 ignored issues
show
Unused Code introduced by
The assignment to $attrs is dead and can be removed.
Loading history...
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
295
            ],
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 16 spaces, but found 12.
Loading history...
296
                $scriptAttrs
297
            );
298
            $content = Html::tag('script', $content, $scriptAttrs);
299
        }
300
301
        return $content;
302
    }
303
304
    /**
305
     * Render the LazySizes fallback JS
306
     *
307
     * @param array $scriptAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
308
     * @param array $variables
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
309
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
310
     */
311
    public function renderLazySizesJs($scriptAttrs = [], $variables = [])
312
    {
313
        $minifier = 'minify';
314
        $vars = array_merge([
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...
315
            'scriptSrc' => 'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.0/lazysizes.min.js',
316
        ],
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 12 spaces, but found 8.
Loading history...
317
            $variables
318
        );
319
        $content = PluginTemplateHelper::renderPluginTemplate(
320
            'frontend/lazysizes-js',
321
            $vars,
322
            $minifier
323
        );
324
        $content = (string)$content;
325
        if ($scriptAttrs !== null) {
0 ignored issues
show
introduced by
The condition $scriptAttrs !== null is always true.
Loading history...
326
            $attrs = array_merge([
0 ignored issues
show
Unused Code introduced by
The assignment to $attrs is dead and can be removed.
Loading history...
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
327
            ],
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 16 spaces, but found 12.
Loading history...
328
                $scriptAttrs
329
            );
330
            $content = Html::tag('script', $content, $scriptAttrs);
331
        }
332
333
        return $content;
334
    }
335
336
    /**
337
     * Handle responding to EVENT_GENERATE_TRANSFORM events
338
     *
339
     * @param GenerateTransformEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
340
     *
341
     * @return null|string
342
     */
343
    public function handleGenerateTransformEvent(GenerateTransformEvent $event)
344
    {
345
        Craft::beginProfile('handleGenerateTransformEvent', __METHOD__);
346
        $tempPath = null;
347
348
        // Only do this for local Craft transforms
349
        if (ImageOptimize::$plugin->transformMethod instanceof CraftImageTransform && $event->asset !== null) {
350
            // Apply any filters to the image
351
            if ($event->transformIndex->transform !== null) {
352
                $this->applyFiltersToImage($event->transformIndex->transform, $event->asset, $event->image);
353
            }
354
            // Save the transformed image to a temp file
355
            $tempPath = $this->saveTransformToTempFile(
356
                $event->transformIndex,
357
                $event->image
358
            );
359
            $originalFileSize = @filesize($tempPath);
360
            // Optimize the image
361
            $this->optimizeImage(
362
                $event->transformIndex,
363
                $tempPath
364
            );
365
            clearstatcache(true, $tempPath);
366
            // Log the results of the image optimization
367
            $optimizedFileSize = @filesize($tempPath);
368
            $index = $event->transformIndex;
369
            $message =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
370
                pathinfo($index->filename, PATHINFO_FILENAME)
0 ignored issues
show
Bug introduced by
Are you sure pathinfo($index->filenam...ices\PATHINFO_FILENAME) of type array|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

370
                /** @scrutinizer ignore-type */ pathinfo($index->filename, PATHINFO_FILENAME)
Loading history...
Bug introduced by
It seems like $index->filename can also be of type null; however, parameter $path of pathinfo() does only seem to accept string, 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

370
                pathinfo(/** @scrutinizer ignore-type */ $index->filename, PATHINFO_FILENAME)
Loading history...
371
                . '.'
372
                . $index->detectedFormat
373
                . ' -> '
374
                . Craft::t('image-optimize', 'Original')
375
                . ': '
376
                . $this->humanFileSize($originalFileSize, 1)
377
                . ', '
378
                . Craft::t('image-optimize', 'Optimized')
379
                . ': '
380
                . $this->humanFileSize($optimizedFileSize, 1)
381
                . ' -> '
382
                . Craft::t('image-optimize', 'Savings')
383
                . ': '
384
                . number_format(abs(100 - (($optimizedFileSize * 100) / $originalFileSize)), 1)
385
                . '%';
386
            Craft::info($message, __METHOD__);
387
            if (Craft::$app instanceof ConsoleApplication) {
388
                echo $message . PHP_EOL;
389
            }
390
            // Create any image variants
391
            $this->createImageVariants(
392
                $event->transformIndex,
393
                $event->asset,
394
                $tempPath
395
            );
396
        }
397
        Craft::endProfile('handleGenerateTransformEvent', __METHOD__);
398
399
        return $tempPath;
400
    }
401
402
    /**
403
     * Handle cleaning up any variant creator images
404
     *
405
     * @param AssetTransformImageEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
406
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
407
    public function handleAfterDeleteTransformsEvent(AssetTransformImageEvent $event)
408
    {
409
        $settings = ImageOptimize::$plugin->getSettings();
0 ignored issues
show
Unused Code introduced by
The assignment to $settings is dead and can be removed.
Loading history...
410
        // Only do this for local Craft transforms
411
        if (ImageOptimize::$plugin->transformMethod instanceof CraftImageTransform && $event->asset !== null) {
412
            $this->cleanupImageVariants($event->asset, $event->transformIndex);
413
        }
414
    }
415
416
    /**
417
     * Save out the image to a temp file
418
     *
419
     * @param AssetTransformIndex $index
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
420
     * @param Image $image
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 15 spaces after parameter type; 1 found
Loading history...
421
     *
422
     * @return string
423
     */
424
    public function saveTransformToTempFile(AssetTransformIndex $index, Image $image): string
425
    {
426
        $tempFilename = uniqid(pathinfo($index->filename, PATHINFO_FILENAME), true) . '.' . $index->detectedFormat;
0 ignored issues
show
Bug introduced by
It seems like $index->filename can also be of type null; however, parameter $path of pathinfo() does only seem to accept string, 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

426
        $tempFilename = uniqid(pathinfo(/** @scrutinizer ignore-type */ $index->filename, PATHINFO_FILENAME), true) . '.' . $index->detectedFormat;
Loading history...
Bug introduced by
It seems like pathinfo($index->filenam...ices\PATHINFO_FILENAME) can also be of type array; however, parameter $prefix of uniqid() does only seem to accept string, 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

426
        $tempFilename = uniqid(/** @scrutinizer ignore-type */ pathinfo($index->filename, PATHINFO_FILENAME), true) . '.' . $index->detectedFormat;
Loading history...
427
        $tempPath = Craft::$app->getPath()->getTempPath() . DIRECTORY_SEPARATOR . $tempFilename;
428
        try {
429
            $image->saveAs($tempPath);
430
        } catch (ImageException $e) {
431
            Craft::error('Transformed image save failed: ' . $e->getMessage(), __METHOD__);
432
        }
433
        Craft::info('Transformed image saved to: ' . $tempPath, __METHOD__);
434
435
        return $tempPath;
436
    }
437
438
    /**
439
     * Run any image post-processing/optimization on the image file
440
     *
441
     * @param AssetTransformIndex $index
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
442
     * @param string $tempPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 14 spaces after parameter type; 1 found
Loading history...
443
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
444
    public function optimizeImage(AssetTransformIndex $index, string $tempPath)
445
    {
446
        Craft::beginProfile('optimizeImage', __METHOD__);
447
        /** @var Settings $settings */
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
448
        $settings = ImageOptimize::$plugin->getSettings();
449
        // Get the active processors for the transform format
450
        $activeImageProcessors = $settings->activeImageProcessors;
451
        $fileFormat = $index->detectedFormat ?? $index->format;
452
        $fileFormat = strtolower($fileFormat);
0 ignored issues
show
Bug introduced by
It seems like $fileFormat can also be of type null; however, parameter $string of strtolower() does only seem to accept string, 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

452
        $fileFormat = strtolower(/** @scrutinizer ignore-type */ $fileFormat);
Loading history...
453
        // Special-case for 'jpeg'
454
        if ($fileFormat === 'jpeg') {
455
            $fileFormat = 'jpg';
456
        }
457
        if (!empty($activeImageProcessors[$fileFormat])) {
458
            // Iterate through all of the processors for this format
459
            $imageProcessors = $settings->imageProcessors;
460
            foreach ($activeImageProcessors[$fileFormat] as $processor) {
461
                if (!empty($processor) && !empty($imageProcessors[$processor])) {
462
                    $this->executeImageProcessor($imageProcessors[$processor], $tempPath);
463
                }
464
            }
465
        }
466
        Craft::endProfile('optimizeImage', __METHOD__);
467
    }
468
469
    /**
470
     * Translate bytes into something human-readable
471
     *
472
     * @param     $bytes
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 5
Loading history...
473
     * @param int $decimals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
474
     *
475
     * @return string
476
     */
477
    public function humanFileSize($bytes, $decimals = 1): string
478
    {
479
        $oldSize = Craft::$app->formatter->sizeFormatBase;
480
        Craft::$app->formatter->sizeFormatBase = 1000;
481
        $result = Craft::$app->formatter->asShortSize($bytes, $decimals);
482
        Craft::$app->formatter->sizeFormatBase = $oldSize;
483
484
        return $result;
485
    }
486
487
    /**
488
     * Create any image variants for the image file
489
     *
490
     * @param AssetTransformIndex $index
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
491
     * @param Asset $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 15 spaces after parameter type; 1 found
Loading history...
492
     * @param string $tempPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 14 spaces after parameter type; 1 found
Loading history...
493
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
494
    public function createImageVariants(AssetTransformIndex $index, Asset $asset, string $tempPath)
495
    {
496
        Craft::beginProfile('createImageVariants', __METHOD__);
497
        /** @var Settings $settings */
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
498
        $settings = ImageOptimize::$plugin->getSettings();
499
        // Get the active image variant creators
500
        $activeImageVariantCreators = $settings->activeImageVariantCreators;
501
        $fileFormat = $index->detectedFormat ?? $index->format;
502
        $fileFormat = strtolower($fileFormat);
0 ignored issues
show
Bug introduced by
It seems like $fileFormat can also be of type null; however, parameter $string of strtolower() does only seem to accept string, 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

502
        $fileFormat = strtolower(/** @scrutinizer ignore-type */ $fileFormat);
Loading history...
503
        // Special-case for 'jpeg'
504
        if ($fileFormat === 'jpeg') {
505
            $fileFormat = 'jpg';
506
        }
507
        if (!empty($activeImageVariantCreators[$fileFormat])) {
508
            // Iterate through all of the image variant creators for this format
509
            $imageVariantCreators = $settings->imageVariantCreators;
510
            foreach ($activeImageVariantCreators[$fileFormat] as $variantCreator) {
511
                if (!empty($variantCreator) && !empty($imageVariantCreators[$variantCreator])) {
512
                    // Create the image variant in a temporary folder
513
                    $generalConfig = Craft::$app->getConfig()->getGeneral();
514
                    $quality = $index->transform->quality ?: $generalConfig->defaultImageQuality;
515
                    $outputPath = $this->executeVariantCreator(
516
                        $imageVariantCreators[$variantCreator],
517
                        $tempPath,
518
                        $quality
519
                    );
520
                    if ($outputPath !== null) {
521
                        // Get info on the original and the created variant
522
                        $originalFileSize = @filesize($tempPath);
523
                        $variantFileSize = @filesize($outputPath);
524
                        $message =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
525
                            pathinfo($tempPath, PATHINFO_FILENAME)
0 ignored issues
show
Bug introduced by
Are you sure pathinfo($tempPath, nyst...ices\PATHINFO_FILENAME) of type array|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

525
                            /** @scrutinizer ignore-type */ pathinfo($tempPath, PATHINFO_FILENAME)
Loading history...
526
                            . '.'
527
                            . pathinfo($tempPath, PATHINFO_EXTENSION)
0 ignored issues
show
Bug introduced by
Are you sure pathinfo($tempPath, nyst...ces\PATHINFO_EXTENSION) of type array|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

527
                            . /** @scrutinizer ignore-type */ pathinfo($tempPath, PATHINFO_EXTENSION)
Loading history...
528
                            . ' -> '
529
                            . pathinfo($outputPath, PATHINFO_FILENAME)
0 ignored issues
show
Bug introduced by
Are you sure pathinfo($outputPath, ny...ices\PATHINFO_FILENAME) of type array|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

529
                            . /** @scrutinizer ignore-type */ pathinfo($outputPath, PATHINFO_FILENAME)
Loading history...
530
                            . '.'
531
                            . pathinfo($outputPath, PATHINFO_EXTENSION)
0 ignored issues
show
Bug introduced by
Are you sure pathinfo($outputPath, ny...ces\PATHINFO_EXTENSION) of type array|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

531
                            . /** @scrutinizer ignore-type */ pathinfo($outputPath, PATHINFO_EXTENSION)
Loading history...
532
                            . ' -> '
533
                            . Craft::t('image-optimize', 'Original')
534
                            . ': '
535
                            . $this->humanFileSize($originalFileSize, 1)
536
                            . ', '
537
                            . Craft::t('image-optimize', 'Variant')
538
                            . ': '
539
                            . $this->humanFileSize($variantFileSize, 1)
540
                            . ' -> '
541
                            . Craft::t('image-optimize', 'Savings')
542
                            . ': '
543
                            . number_format(abs(100 - (($variantFileSize * 100) / $originalFileSize)), 1)
544
                            . '%';
545
                        Craft::info($message, __METHOD__);
546
                        if (Craft::$app instanceof ConsoleApplication) {
547
                            echo $message . PHP_EOL;
548
                        }
549
                        // Copy the image variant into place
550
                        $this->copyImageVariantToVolume(
551
                            $imageVariantCreators[$variantCreator],
552
                            $asset,
553
                            $index,
554
                            $outputPath
555
                        );
556
                    }
557
                }
558
            }
559
        }
560
        Craft::endProfile('createImageVariants', __METHOD__);
561
    }
562
563
    /**
564
     * Return an array of active image processors
565
     *
566
     * @return array
567
     */
568
    public function getActiveImageProcessors(): array
569
    {
570
        $result = [];
571
        /** @var Settings $settings */
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
572
        $settings = ImageOptimize::$plugin->getSettings();
573
        // Get the active processors for the transform format
574
        $activeImageProcessors = $settings->activeImageProcessors;
575
        foreach ($activeImageProcessors as $imageFormat => $imageProcessor) {
576
            // Iterate through all of the processors for this format
577
            $imageProcessors = $settings->imageProcessors;
578
            foreach ($activeImageProcessors[$imageFormat] as $processor) {
579
                if (!empty($imageProcessors[$processor])) {
580
                    $thisImageProcessor = $imageProcessors[$processor];
581
                    $result[] = [
582
                        'format' => $imageFormat,
583
                        'creator' => $processor,
584
                        'command' => $thisImageProcessor['commandPath']
585
                            . ' '
586
                            . $thisImageProcessor['commandOptions'],
587
                        'installed' => is_file($thisImageProcessor['commandPath']),
588
                    ];
589
                }
590
            }
591
        }
592
593
        return $result;
594
    }
595
596
    /**
597
     * Return an array of active image variant creators
598
     *
599
     * @return array
600
     */
601
    public function getActiveVariantCreators(): array
602
    {
603
        $result = [];
604
        /** @var Settings $settings */
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
605
        $settings = ImageOptimize::$plugin->getSettings();
606
        // Get the active image variant creators
607
        $activeImageVariantCreators = $settings->activeImageVariantCreators;
608
        foreach ($activeImageVariantCreators as $imageFormat => $imageCreator) {
609
            // Iterate through all of the image variant creators for this format
610
            $imageVariantCreators = $settings->imageVariantCreators;
611
            foreach ($activeImageVariantCreators[$imageFormat] as $variantCreator) {
612
                if (!empty($imageVariantCreators[$variantCreator])) {
613
                    $thisVariantCreator = $imageVariantCreators[$variantCreator];
614
                    $result[] = [
615
                        'format' => $imageFormat,
616
                        'creator' => $variantCreator,
617
                        'command' => $thisVariantCreator['commandPath']
618
                            . ' '
619
                            . $thisVariantCreator['commandOptions'],
620
                        'installed' => is_file($thisVariantCreator['commandPath']),
621
                    ];
622
                }
623
            }
624
        }
625
626
        return $result;
627
    }
628
629
    // Protected Methods
630
    // =========================================================================
631
632
    /** @noinspection PhpUnusedParameterInspection
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Tag @noinspection cannot be grouped with parameter tags in a doc comment
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
633
     * @param AssetTransform $transform
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 8 spaces but found 1
Loading history...
634
     * @param Asset $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 10 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 8 spaces but found 1
Loading history...
635
     * @param Image $image
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 10 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 8 spaces but found 1
Loading history...
636
     */
0 ignored issues
show
Coding Style introduced by
There must be no blank lines after the function comment
Loading history...
Coding Style introduced by
Missing @return tag in function comment
Loading history...
637
638
    protected function applyFiltersToImage(AssetTransform $transform, Asset $asset, Image $image)
639
    {
640
        /** @var Settings $settings */
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...
641
        $settings = ImageOptimize::$plugin->getSettings();
642
        // Only try to apply filters to Raster images
643
        if ($image instanceof Raster) {
644
            $imagineImage = $image->getImagineImage();
645
            if ($imagineImage !== null) {
646
                // Handle auto-sharpening scaled down images
647
                if ($settings->autoSharpenScaledImages) {
648
                    // See if the image has been scaled >= 50%
649
                    $widthScale = (int)(($image->getWidth() / $asset->getWidth()) * 100);
650
                    $heightScale = (int)(($image->getHeight() / $asset->getHeight()) * 100);
651
                    if (($widthScale >= (int)$settings->sharpenScaledImagePercentage) || ($heightScale >= (int)$settings->sharpenScaledImagePercentage)) {
652
                        $imagineImage->effects()
653
                            ->sharpen();
654
                        Craft::debug(
655
                            Craft::t(
656
                                'image-optimize',
657
                                'Image transform >= 50%, sharpened the transformed image: {name}',
658
                                [
659
                                    'name' => $asset->title,
660
                                ]
661
                            ),
662
                            __METHOD__
663
                        );
664
                    }
665
                }
666
            }
667
        }
668
    }
669
670
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
671
     * @param string $tempPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $tempPath does not match actual variable name $thisProcessor
Loading history...
672
     * @param         $thisProcessor
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $thisProcessor does not match actual variable name $tempPath
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 9
Loading history...
673
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
674
    protected function executeImageProcessor($thisProcessor, string $tempPath)
675
    {
676
        // Make sure the command exists
677
        if (is_file($thisProcessor['commandPath'])) {
678
            // Set any options for the command
679
            $commandOptions = '';
680
            if (!empty($thisProcessor['commandOptions'])) {
681
                $commandOptions = ' '
682
                    . $thisProcessor['commandOptions']
683
                    . ' ';
684
            }
685
            // Redirect the command output if necessary for this processor
686
            $outputFileFlag = '';
687
            if (!empty($thisProcessor['commandOutputFileFlag'])) {
688
                $outputFileFlag = ' '
689
                    . $thisProcessor['commandOutputFileFlag']
690
                    . ' '
691
                    . escapeshellarg($tempPath)
692
                    . ' ';
693
            }
694
            // If both $commandOptions & $outputFileFlag are empty, pad it with a space
695
            if (empty($commandOptions) && empty($outputFileFlag)) {
696
                $commandOptions = ' ';
697
            }
698
            // Build the command to execute
699
            $cmd =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
700
                $thisProcessor['commandPath']
701
                . $commandOptions
702
                . $outputFileFlag
703
                . escapeshellarg($tempPath);
704
            // Execute the command
705
            $shellOutput = $this->executeShellCommand($cmd);
706
            Craft::info($cmd . "\n" . $shellOutput, __METHOD__);
707
        } else {
708
            Craft::error(
709
                $thisProcessor['commandPath']
710
                . ' '
711
                . Craft::t('image-optimize', 'does not exist'),
712
                __METHOD__
713
            );
714
        }
715
    }
716
717
    /**
718
     * Execute a shell command
719
     *
720
     * @param string $command
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
721
     *
722
     * @return string
723
     */
724
    protected function executeShellCommand(string $command): string
725
    {
726
        // Create the shell command
727
        $shellCommand = new ShellCommand();
728
        $shellCommand->setCommand($command);
729
730
        // If we don't have proc_open, maybe we've got exec
731
        if (!function_exists('proc_open') && function_exists('exec')) {
732
            $shellCommand->useExec = true;
733
        }
734
735
        // Return the result of the command's output or error
736
        if ($shellCommand->execute()) {
737
            $result = $shellCommand->getOutput();
738
        } else {
739
            $result = $shellCommand->getError();
740
        }
741
742
        return $result;
743
    }
744
745
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
746
     * @param         $variantCreatorCommand
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 9
Loading history...
747
     * @param string $tempPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
748
     * @param int $imageQuality
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 4 spaces after parameter type; 1 found
Loading history...
749
     *
750
     * @return string|null the path to the created variant
751
     */
752
    protected function executeVariantCreator($variantCreatorCommand, string $tempPath, int $imageQuality)
753
    {
754
        $outputPath = $tempPath;
755
        // Make sure the command exists
756
        if (is_file($variantCreatorCommand['commandPath'])) {
757
            // Get the output file for this image variant
758
            $outputPath .= '.' . $variantCreatorCommand['imageVariantExtension'];
759
            // Set any options for the command
760
            $commandOptions = '';
761
            if (!empty($variantCreatorCommand['commandOptions'])) {
762
                $commandOptions = ' '
763
                    . $variantCreatorCommand['commandOptions']
764
                    . ' ';
765
            }
766
            // Redirect the command output if necessary for this variantCreator
767
            $outputFileFlag = '';
768
            if (!empty($variantCreatorCommand['commandOutputFileFlag'])) {
769
                $outputFileFlag = ' '
770
                    . $variantCreatorCommand['commandOutputFileFlag']
771
                    . ' '
772
                    . escapeshellarg($outputPath)
773
                    . ' ';
774
            }
775
            // Get the quality setting of this transform
776
            $commandQualityFlag = '';
777
            if (!empty($variantCreatorCommand['commandQualityFlag'])) {
778
                $commandQualityFlag = ' '
779
                    . $variantCreatorCommand['commandQualityFlag']
780
                    . ' '
781
                    . $imageQuality
782
                    . ' ';
783
            }
784
            // Build the command to execute
785
            $cmd =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
786
                $variantCreatorCommand['commandPath']
787
                . $commandOptions
788
                . $commandQualityFlag
789
                . $outputFileFlag
790
                . escapeshellarg($tempPath);
791
            // Execute the command
792
            $shellOutput = $this->executeShellCommand($cmd);
793
            Craft::info($cmd . "\n" . $shellOutput, __METHOD__);
794
        } else {
795
            Craft::error(
796
                $variantCreatorCommand['commandPath']
797
                . ' '
798
                . Craft::t('image-optimize', 'does not exist'),
799
                __METHOD__
800
            );
801
            $outputPath = null;
802
        }
803
804
        return $outputPath;
805
    }
806
807
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
808
     * @param Asset $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 15 spaces after parameter type; 1 found
Loading history...
809
     * @param AssetTransformIndex $transformIndex
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
810
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
811
    protected function cleanupImageVariants(Asset $asset, AssetTransformIndex $transformIndex)
812
    {
813
        /** @var Settings $settings */
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...
814
        $settings = ImageOptimize::$plugin->getSettings();
815
        $assetTransforms = Craft::$app->getAssetTransforms();
816
        // Get the active image variant creators
817
        $activeImageVariantCreators = $settings->activeImageVariantCreators;
818
        $fileFormat = $transformIndex->detectedFormat ?? $transformIndex->format;
819
        $fileFormat = empty($fileFormat) ? $asset->getExtension() : $fileFormat;
820
        // Normalize the extension to lowercase, for some transform methods that require this
821
        $fileFormat = strtolower($fileFormat);
822
        // Special-case for 'jpeg'
823
        if ($fileFormat === 'jpeg') {
824
            $fileFormat = 'jpg';
825
        }
826
        if (!empty($activeImageVariantCreators[$fileFormat])) {
827
            // Iterate through all of the image variant creators for this format
828
            $imageVariantCreators = $settings->imageVariantCreators;
829
            if (!empty($activeImageVariantCreators[$fileFormat])) {
830
                foreach ($activeImageVariantCreators[$fileFormat] as $variantCreator) {
831
                    if (!empty($variantCreator) && !empty($imageVariantCreators[$variantCreator])) {
832
                        // Create the image variant in a temporary folder
833
                        $variantCreatorCommand = $imageVariantCreators[$variantCreator];
834
                        try {
835
                            $volume = $asset->getVolume();
836
                        } catch (InvalidConfigException $e) {
837
                            $volume = null;
838
                            Craft::error(
839
                                'Asset volume error: ' . $e->getMessage(),
840
                                __METHOD__
841
                            );
842
                        }
843
                        try {
844
                            $variantPath = $asset->getFolder()->path . $assetTransforms->getTransformSubpath(
845
                                    $asset,
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 32 spaces, but found 36.
Loading history...
846
                                    $transformIndex
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 32 spaces, but found 36.
Loading history...
847
                                );
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 28 spaces, but found 32.
Loading history...
848
                        } catch (InvalidConfigException $e) {
849
                            $variantPath = '';
850
                            Craft::error(
851
                                'Asset folder does not exist: ' . $e->getMessage(),
852
                                __METHOD__
853
                            );
854
                        }
855
                        $variantPath .= '.' . $variantCreatorCommand['imageVariantExtension'];
856
                        // Delete the variant file in case it is stale
857
                        try {
858
                            $volume->deleteFile($variantPath);
859
                        } catch (VolumeException $e) {
860
                            // We're fine with that.
861
                        }
862
                        Craft::info(
863
                            'Deleted variant: ' . $variantPath,
864
                            __METHOD__
865
                        );
866
                    }
867
                }
868
            }
869
        }
870
    }
871
872
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
873
     * @param                     $variantCreatorCommand
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 21
Loading history...
874
     * @param Asset $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 15 spaces after parameter type; 1 found
Loading history...
875
     * @param AssetTransformIndex $index
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
876
     * @param                     $outputPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 21
Loading history...
877
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
878
    protected function copyImageVariantToVolume(
879
        $variantCreatorCommand,
880
        Asset $asset,
881
        AssetTransformIndex $index,
882
        $outputPath
883
    )
884
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
885
        // If the image variant creation succeeded, copy it into place
886
        if (!empty($outputPath) && is_file($outputPath)) {
887
            // Figure out the resulting path for the image variant
888
            try {
889
                $volume = $asset->getVolume();
890
            } catch (InvalidConfigException $e) {
891
                $volume = null;
892
                Craft::error(
893
                    'Asset volume error: ' . $e->getMessage(),
894
                    __METHOD__
895
                );
896
            }
897
            $assetTransforms = Craft::$app->getAssetTransforms();
898
            try {
899
                $transformPath = $asset->getFolder()->path . $assetTransforms->getTransformSubpath($asset, $index);
900
            } catch (InvalidConfigException $e) {
901
                $transformPath = '';
902
                Craft::error(
903
                    'Error getting asset folder: ' . $e->getMessage(),
904
                    __METHOD__
905
                );
906
            }
907
            $variantPath = $transformPath . '.' . $variantCreatorCommand['imageVariantExtension'];
908
909
            // Delete the variant file in case it is stale
910
            try {
911
                $volume->deleteFile($variantPath);
912
            } catch (VolumeException $e) {
913
                // We're fine with that.
914
            }
915
916
            Craft::info(
917
                'Variant output path: ' . $outputPath . ' - Variant path: ' . $variantPath,
918
                __METHOD__
919
            );
920
921
            clearstatcache(true, $outputPath);
922
            $stream = @fopen($outputPath, 'rb');
923
            if ($stream !== false) {
924
                // Now create it
925
                try {
926
                    $volume->createFileByStream($variantPath, $stream, []);
927
                } catch (VolumeException $e) {
928
                    Craft::error(
929
                        Craft::t('image-optimize', 'Failed to create image variant at: ')
930
                        . $outputPath,
931
                        __METHOD__
932
                    );
933
                }
934
935
                FileHelper::unlink($outputPath);
936
            }
937
        } else {
938
            Craft::error(
939
                Craft::t('image-optimize', 'Failed to create image variant at: ')
940
                . $outputPath,
941
                __METHOD__
942
            );
943
        }
944
    }
945
946
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
947
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
948
     * @param string $extension
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
949
     *
950
     * @return string
951
     */
952
    protected function swapPathExtension(string $path, string $extension): string
953
    {
954
        $pathParts = pathinfo($path);
955
        $newPath = $pathParts['filename'] . '.' . $extension;
956
        if (!empty($pathParts['dirname']) && $pathParts['dirname'] !== '.') {
957
            $newPath = $pathParts['dirname'] . DIRECTORY_SEPARATOR . $newPath;
958
            $newPath = preg_replace('#/+#', '/', $newPath);
959
        }
960
961
        return $newPath;
962
    }
963
}
964