Passed
Push — v1 ( f4defa...bf06b8 )
by Andrew
16:03 queued 10:49
created

Optimize::optimizeImage()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 22
rs 9.2222
cc 6
nc 4
nop 2
1
<?php
2
/**
3
 * ImageOptimize plugin for Craft CMS 3.x
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 nystudio107\imageoptimize\ImageOptimize;
14
use nystudio107\imageoptimize\helpers\PluginTemplate as PluginTemplateHelper;
15
use nystudio107\imageoptimize\imagetransforms\CraftImageTransform;
16
use nystudio107\imageoptimize\imagetransforms\ImageTransform;
17
use nystudio107\imageoptimize\imagetransforms\ImageTransformInterface;
18
use nystudio107\imageoptimizeimgix\imagetransforms\ImgixImageTransform;
19
use nystudio107\imageoptimizethumbor\imagetransforms\ThumborImageTransform;
20
use nystudio107\imageoptimizesharp\imagetransforms\SharpImageTransform;
21
22
use Craft;
23
use craft\base\Component;
24
use craft\base\Image;
25
use craft\console\Application as ConsoleApplication;
26
use craft\elements\Asset;
27
use craft\errors\ImageException;
28
use craft\errors\VolumeException;
29
use craft\events\AssetTransformImageEvent;
30
use craft\events\GetAssetThumbUrlEvent;
31
use craft\events\GetAssetUrlEvent;
32
use craft\events\GenerateTransformEvent;
33
use craft\events\RegisterComponentTypesEvent;
34
use craft\helpers\Assets as AssetsHelper;
35
use craft\helpers\Component as ComponentHelper;
36
use craft\helpers\FileHelper;
37
use craft\helpers\Html;
38
use craft\helpers\Image as ImageHelper;
39
use craft\image\Raster;
40
use craft\models\AssetTransform;
41
use craft\models\AssetTransformIndex;
42
43
use mikehaertl\shellcommand\Command as ShellCommand;
44
45
use yii\base\InvalidConfigException;
46
47
/** @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...
48
49
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
50
 * @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
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
51
 * @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...
52
 * @since     1.0.0
0 ignored issues
show
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
53
 */
0 ignored issues
show
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
Coding Style introduced by
Missing @category tag in class comment
Loading history...
54
class Optimize extends Component
55
{
56
    // Constants
57
    // =========================================================================
58
59
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
60
     * @event RegisterComponentTypesEvent The event that is triggered when registering
61
     *        Image Transform types
62
     *
63
     * Image Transform types must implement [[ImageTransformInterface]]. [[ImageTransform]]
64
     * provides a base implementation.
65
     *
66
     * ```php
67
     * use nystudio107\imageoptimize\services\Optimize;
68
     * use craft\events\RegisterComponentTypesEvent;
69
     * use yii\base\Event;
70
     *
71
     * Event::on(Optimize::class,
72
     *     Optimize::EVENT_REGISTER_IMAGE_TRANSFORM_TYPES,
73
     *     function(RegisterComponentTypesEvent $event) {
74
     *         $event->types[] = MyImageTransform::class;
75
     *     }
76
     * );
77
     * ```
78
     */
79
    const EVENT_REGISTER_IMAGE_TRANSFORM_TYPES = 'registerImageTransformTypes';
80
81
    const DEFAULT_IMAGE_TRANSFORM_TYPES = [
82
        CraftImageTransform::class,
83
        ImgixImageTransform::class,
84
        SharpImageTransform::class,
85
        ThumborImageTransform::class,
86
    ];
87
88
    // Public Methods
89
    // =========================================================================
90
91
    /**
92
     * Returns all available field type classes.
93
     *
94
     * @return string[] The available field type classes
95
     */
96
    public function getAllImageTransformTypes(): array
97
    {
98
        $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...
99
            ImageOptimize::$plugin->getSettings()->defaultImageTransformTypes ?? [],
100
            self::DEFAULT_IMAGE_TRANSFORM_TYPES
101
        ), 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...
102
103
        $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...
104
            'types' => $imageTransformTypes
105
        ]);
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...
106
        $this->trigger(self::EVENT_REGISTER_IMAGE_TRANSFORM_TYPES, $event);
107
108
        return $event->types;
109
    }
110
111
    /**
112
     * Creates an Image Transform with a given config.
113
     *
114
     * @param mixed $config The Image Transform’s class name, or its config,
115
     *                      with a `type` value and optionally a `settings` value
116
     *
117
     * @return null|ImageTransformInterface The Image Transform
118
     */
119
    public function createImageTransformType($config): ImageTransformInterface
120
    {
121
        if (is_string($config)) {
122
            $config = ['type' => $config];
123
        }
124
125
        try {
126
            /** @var ImageTransform $imageTransform */
0 ignored issues
show
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...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
127
            $imageTransform = ComponentHelper::createComponent($config, ImageTransformInterface::class);
128
        } catch (\Throwable $e) {
129
            $imageTransform = null;
130
            Craft::error($e->getMessage(), __METHOD__);
131
        }
132
133
        return $imageTransform;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $imageTransform could return the type null which is incompatible with the type-hinted return nystudio107\imageoptimiz...ImageTransformInterface. Consider adding an additional type-check to rule them out.
Loading history...
134
    }
135
136
    /**
137
     * Handle responding to EVENT_GET_ASSET_URL events
138
     *
139
     * @param GetAssetUrlEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
140
     *
141
     * @return null|string
142
     * @throws InvalidConfigException
143
     */
144
    public function handleGetAssetUrlEvent(GetAssetUrlEvent $event)
145
    {
146
        Craft::beginProfile('handleGetAssetUrlEvent', __METHOD__);
147
        $url = null;
148
        if (!ImageOptimize::$plugin->transformMethod instanceof CraftImageTransform) {
149
            $asset = $event->asset;
150
            $transform = $event->transform;
151
            // If the transform is empty in some regard, normalize it to null
152
            if (empty($transform)) {
153
                $transform = null;
154
            }
155
            // If there's no transform requested, return `null` so other plugins have a crack at it
156
            if ($transform === null) {
157
                return null;
158
            }
159
            // If we're passed in null, make a dummy AssetTransform model for Thumbor
160
            // For backwards compatibility
161
            if ($transform === null && ImageOptimize::$plugin->transformMethod instanceof ThumborImageTransform) {
162
                $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...
163
                    'width'     => $asset->width,
164
                    'interlace' => 'line',
165
                ]);
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...
166
            }
167
            // If we're passed an array, make an AssetTransform model out of it
168
            if (\is_array($transform)) {
169
                $transform = new AssetTransform($transform);
170
            }
171
            // If we're passing in a string, look up the asset transform in the db
172
            if (\is_string($transform)) {
173
                $assetTransforms = Craft::$app->getAssetTransforms();
174
                $transform = $assetTransforms->getTransformByHandle($transform);
175
            }
176
            // If the final format is an SVG, don't attempt to transform it
177
            $finalFormat = empty($transform['format']) ? $asset->getExtension() : $transform['format'];
178
            if ($finalFormat === 'svg') {
179
                return null;
180
            }
181
            // Normalize the extension to lowercase, for some transform methods that require this
182
            if (!empty($transform) && !empty($finalFormat)) {
183
                $format = $transform['format'] ?? null;
184
                $transform['format'] = $format === null ? null : strtolower($finalFormat);
185
            }
186
            // Generate an image transform url
187
            $url = ImageOptimize::$plugin->transformMethod->getTransformUrl(
188
                $asset,
189
                $transform
190
            );
191
        }
192
        Craft::endProfile('handleGetAssetUrlEvent', __METHOD__);
193
194
        return $url;
195
    }
196
197
    /**
198
     * Handle responding to EVENT_GET_ASSET_THUMB_URL events
199
     *
200
     * @param GetAssetThumbUrlEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
201
     *
202
     * @return null|string
203
     */
204
    public function handleGetAssetThumbUrlEvent(GetAssetThumbUrlEvent $event)
205
    {
206
        Craft::beginProfile('handleGetAssetThumbUrlEvent', __METHOD__);
207
        $url = $event->url;
208
        if (!ImageOptimize::$plugin->transformMethod instanceof CraftImageTransform) {
209
            $asset = $event->asset;
210
            if (ImageHelper::canManipulateAsImage($asset->getExtension())) {
211
                $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...
212
                    'width' => $event->width,
213
		    'height' => $event->height,
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 20 spaces, but found 6.
Loading history...
Coding Style introduced by
Line indented incorrectly; expected at least 16 spaces, found 6
Loading history...
214
                    'interlace' => 'line',
215
                ]);
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...
216
                /** @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...
217
                $transformMethod = ImageOptimize::$plugin->transformMethod;
218
                // If the final format is an SVG, don't attempt to transform it
219
                $finalFormat = empty($transform['format']) ? $asset->getExtension() : $transform['format'];
220
                if ($finalFormat === 'svg') {
221
                    return null;
222
                }
223
                // Normalize the extension to lowercase, for some transform methods that require this
224
                if ($transform !== null && !empty($finalFormat)) {
225
                    $transform['format'] = strtolower($finalFormat);
226
                }
227
                // Generate an image transform url
228
                if ($transformMethod->hasProperty('generateTransformsBeforePageLoad')) {
229
                    $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...
230
                }
231
                $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...
232
            }
233
        }
234
        Craft::endProfile('handleGetAssetThumbUrlEvent', __METHOD__);
235
236
        return $url;
237
    }
238
239
    /**
240
     * Returns whether `.webp` is a format supported by the server
241
     *
242
     * @return bool
243
     */
244
    public function serverSupportsWebP(): bool
245
    {
246
        $result = false;
247
        $variantCreators = ImageOptimize::$plugin->optimize->getActiveVariantCreators();
248
        foreach ($variantCreators as $variantCreator) {
249
            if ($variantCreator['creator'] === 'cwebp' && $variantCreator['installed']) {
250
                $result = true;
251
            }
252
        }
253
254
        return $result;
255
    }
256
257
    /**
258
     * Render the LazySizes fallback JS
259
     *
260
     * @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...
261
     * @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...
262
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
263
     */
264
    public function renderLazySizesFallbackJs($scriptAttrs = [], $variables = [])
265
    {
266
        $minifier = 'minify';
267
        if ($scriptAttrs === null) {
0 ignored issues
show
introduced by
The condition $scriptAttrs === null is always false.
Loading history...
268
            $minifier = 'jsMin';
269
        }
270
        $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...
271
            'scriptSrc' => 'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.0/lazysizes.min.js',
272
            ],
273
            $variables
274
        );
275
        $content = PluginTemplateHelper::renderPluginTemplate(
276
            'frontend/lazysizes-fallback-js',
277
            $vars,
278
            $minifier
279
        );
280
        $content = (string)$content;
281
        if ($scriptAttrs !== null) {
0 ignored issues
show
introduced by
The condition $scriptAttrs !== null is always true.
Loading history...
282
            $attrs = 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...
Unused Code introduced by
The assignment to $attrs is dead and can be removed.
Loading history...
283
                ],
284
                $scriptAttrs
285
            );
286
            $content = Html::tag('script', $content, $scriptAttrs);
287
        }
288
289
        return $content;
290
    }
291
292
    /**
293
     * Render the LazySizes fallback JS
294
     *
295
     * @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...
296
     * @param array $variables
0 ignored issues
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
297
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
298
     */
299
    public function renderLazySizesJs($scriptAttrs = [], $variables = [])
300
    {
301
        $minifier = 'minify';
302
        if ($scriptAttrs === null) {
0 ignored issues
show
introduced by
The condition $scriptAttrs === null is always false.
Loading history...
303
            $minifier = 'jsMin';
304
        }
305
        $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...
306
            'scriptSrc' => 'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.0/lazysizes.min.js',
307
        ],
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...
308
            $variables
309
        );
310
        $content = PluginTemplateHelper::renderPluginTemplate(
311
            'frontend/lazysizes-js',
312
            $vars,
313
            $minifier
314
        );
315
        $content = (string)$content;
316
        if ($scriptAttrs !== null) {
0 ignored issues
show
introduced by
The condition $scriptAttrs !== null is always true.
Loading history...
317
            $attrs = 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...
Unused Code introduced by
The assignment to $attrs is dead and can be removed.
Loading history...
318
            ],
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...
319
                $scriptAttrs
320
            );
321
            $content = Html::tag('script', $content, $scriptAttrs);
322
        }
323
324
        return $content;
325
    }
326
327
    /**
328
     * Handle responding to EVENT_GENERATE_TRANSFORM events
329
     *
330
     * @param GenerateTransformEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
331
     *
332
     * @return null|string
333
     */
334
    public function handleGenerateTransformEvent(GenerateTransformEvent $event)
335
    {
336
        Craft::beginProfile('handleGenerateTransformEvent', __METHOD__);
337
        $tempPath = null;
338
339
        // Only do this for local Craft transforms
340
        if (ImageOptimize::$plugin->transformMethod instanceof CraftImageTransform && $event->asset !== null) {
341
            // Apply any filters to the image
342
            if ($event->transformIndex->transform !== null) {
343
                $this->applyFiltersToImage($event->transformIndex->transform, $event->asset, $event->image);
344
            }
345
            // Save the transformed image to a temp file
346
            $tempPath = $this->saveTransformToTempFile(
347
                $event->transformIndex,
348
                $event->image
349
            );
350
            $originalFileSize = @filesize($tempPath);
351
            // Optimize the image
352
            $this->optimizeImage(
353
                $event->transformIndex,
354
                $tempPath
355
            );
356
            clearstatcache(true, $tempPath);
357
            // Log the results of the image optimization
358
            $optimizedFileSize = @filesize($tempPath);
359
            $index = $event->transformIndex;
360
            $message =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
361
                pathinfo($index->filename, PATHINFO_FILENAME)
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

361
                pathinfo(/** @scrutinizer ignore-type */ $index->filename, PATHINFO_FILENAME)
Loading history...
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

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

417
        $tempFilename = uniqid(/** @scrutinizer ignore-type */ pathinfo($index->filename, PATHINFO_FILENAME), true).'.'.$index->detectedFormat;
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

417
        $tempFilename = uniqid(pathinfo(/** @scrutinizer ignore-type */ $index->filename, PATHINFO_FILENAME), true).'.'.$index->detectedFormat;
Loading history...
418
        $tempPath = Craft::$app->getPath()->getTempPath().DIRECTORY_SEPARATOR.$tempFilename;
419
        try {
420
            $image->saveAs($tempPath);
421
        } catch (ImageException $e) {
422
            Craft::error('Transformed image save failed: '.$e->getMessage(), __METHOD__);
423
        }
424
        Craft::info('Transformed image saved to: '.$tempPath, __METHOD__);
425
426
        return $tempPath;
427
    }
428
429
    /**
430
     * Run any image post-processing/optimization on the image file
431
     *
432
     * @param AssetTransformIndex $index
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
433
     * @param string              $tempPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
434
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
435
    public function optimizeImage(AssetTransformIndex $index, string $tempPath)
436
    {
437
        Craft::beginProfile('optimizeImage', __METHOD__);
438
        $settings = ImageOptimize::$plugin->getSettings();
439
        // Get the active processors for the transform format
440
        $activeImageProcessors = $settings->activeImageProcessors;
441
        $fileFormat = $index->detectedFormat ?? $index->format;
442
        $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

442
        $fileFormat = strtolower(/** @scrutinizer ignore-type */ $fileFormat);
Loading history...
443
        // Special-case for 'jpeg'
444
        if ($fileFormat === 'jpeg') {
445
            $fileFormat = 'jpg';
446
        }
447
        if (!empty($activeImageProcessors[$fileFormat])) {
448
            // Iterate through all of the processors for this format
449
            $imageProcessors = $settings->imageProcessors;
450
            foreach ($activeImageProcessors[$fileFormat] as $processor) {
451
                if (!empty($processor) && !empty($imageProcessors[$processor])) {
452
                    $this->executeImageProcessor($imageProcessors[$processor], $tempPath);
453
                }
454
            }
455
        }
456
        Craft::endProfile('optimizeImage', __METHOD__);
457
    }
458
459
    /**
460
     * Translate bytes into something human-readable
461
     *
462
     * @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...
463
     * @param int $decimals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
464
     *
465
     * @return string
466
     */
467
    public function humanFileSize($bytes, $decimals = 1): string
468
    {
469
        $oldSize = Craft::$app->formatter->sizeFormatBase;
470
        Craft::$app->formatter->sizeFormatBase = 1000;
471
        $result = Craft::$app->formatter->asShortSize($bytes, $decimals);
472
        Craft::$app->formatter->sizeFormatBase = $oldSize;
473
474
        return $result;
475
    }
476
477
    /**
478
     * Create any image variants for the image file
479
     *
480
     * @param AssetTransformIndex $index
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
481
     * @param Asset               $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
482
     * @param string              $tempPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
483
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
484
    public function createImageVariants(AssetTransformIndex $index, Asset $asset, string $tempPath)
485
    {
486
        Craft::beginProfile('createImageVariants', __METHOD__);
487
        $settings = ImageOptimize::$plugin->getSettings();
488
        // Get the active image variant creators
489
        $activeImageVariantCreators = $settings->activeImageVariantCreators;
490
        $fileFormat = $index->detectedFormat ?? $index->format;
491
        $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

491
        $fileFormat = strtolower(/** @scrutinizer ignore-type */ $fileFormat);
Loading history...
492
        // Special-case for 'jpeg'
493
        if ($fileFormat === 'jpeg') {
494
            $fileFormat = 'jpg';
495
        }
496
        if (!empty($activeImageVariantCreators[$fileFormat])) {
497
            // Iterate through all of the image variant creators for this format
498
            $imageVariantCreators = $settings->imageVariantCreators;
499
            foreach ($activeImageVariantCreators[$fileFormat] as $variantCreator) {
500
                if (!empty($variantCreator) && !empty($imageVariantCreators[$variantCreator])) {
501
                    // Create the image variant in a temporary folder
502
                    $generalConfig = Craft::$app->getConfig()->getGeneral();
503
                    $quality = $index->transform->quality ?: $generalConfig->defaultImageQuality;
504
                    $outputPath = $this->executeVariantCreator(
505
                        $imageVariantCreators[$variantCreator],
506
                        $tempPath,
507
                        $quality
508
                    );
509
                    if ($outputPath !== null) {
510
                        // Get info on the original and the created variant
511
                        $originalFileSize = @filesize($tempPath);
512
                        $variantFileSize = @filesize($outputPath);
513
                        $message =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
514
                            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

514
                            /** @scrutinizer ignore-type */ pathinfo($tempPath, PATHINFO_FILENAME)
Loading history...
515
                            .'.'
516
                            .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

516
                            ./** @scrutinizer ignore-type */ pathinfo($tempPath, PATHINFO_EXTENSION)
Loading history...
517
                            .' -> '
518
                            .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

518
                            ./** @scrutinizer ignore-type */ pathinfo($outputPath, PATHINFO_FILENAME)
Loading history...
519
                            .'.'
520
                            .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

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