Passed
Push — develop ( d502bc...2115be )
by Andrew
04:39
created

ImgixImageTransform::getWebPUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 4
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\imagetransforms;
12
13
use nystudio107\imageoptimize\ImageOptimize;
14
15
use craft\elements\Asset;
16
use craft\helpers\ArrayHelper;
17
use craft\helpers\Assets as AssetsHelper;
18
use craft\helpers\UrlHelper;
19
use craft\models\AssetTransform;
20
21
use Imgix\UrlBuilder;
22
use Psr\Http\Message\ResponseInterface;
23
24
use Craft;
25
26
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
27
 * @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 indented incorrectly; expected 2 spaces but found 4
Loading history...
28
 * @package   ImageOptimize
0 ignored issues
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 3
Loading history...
29
 * @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 indented incorrectly; expected 3 spaces but found 5
Loading history...
30
 */
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...
31
class ImgixImageTransform extends ImageTransform
32
{
33
    // Constants
34
    // =========================================================================
35
36
    const TRANSFORM_ATTRIBUTES_MAP = [
37
        'width'   => 'w',
38
        'height'  => 'h',
39
        'quality' => 'q',
40
        'format'  => 'fm',
41
    ];
42
43
    const IMGIX_PURGE_ENDPOINT = 'https://api.imgix.com/v2/image/purger';
44
45
    // Static Methods
46
    // =========================================================================
47
48
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
49
     * @inheritdoc
50
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
51
    public static function displayName(): string
52
    {
53
        return Craft::t('image-optimize', 'Imgix');
54
    }
55
56
    // Public Properties
57
    // =========================================================================
58
59
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
60
     * @var string
61
     */
62
    public $domain;
63
64
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
65
     * @var string
66
     */
67
    public $apiKey;
68
69
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
70
     * @var string
71
     */
72
    public $securityToken;
73
74
    // Public Methods
75
    // =========================================================================
76
77
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
78
     * @param Asset               $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
79
     * @param AssetTransform|null $transform
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
80
     * @param array               $params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
81
     *
82
     * @return string|null
83
     * @throws \yii\base\Exception
84
     * @throws \yii\base\InvalidConfigException
85
     */
86
    public function getTransformUrl(Asset $asset, $transform, array $params = [])
87
    {
88
        $url = null;
89
        $settings = ImageOptimize::$plugin->getSettings();
90
91
        $domain = $params['domain'] ?? 'demos.imgix.net';
92
        $builder = new UrlBuilder($domain);
93
        if ($asset && $builder) {
0 ignored issues
show
introduced by
$builder is of type Imgix\UrlBuilder, thus it always evaluated to true.
Loading history...
94
            $builder->setUseHttps(true);
95
            if ($transform) {
96
                // Map the transform properties
97
                foreach (self::TRANSFORM_ATTRIBUTES_MAP as $key => $value) {
98
                    if (!empty($transform[$key])) {
99
                        $params[$value] = $transform[$key];
100
                    }
101
                }
102
                // Remove any 'AUTO' settings
103
                ArrayHelper::removeValue($params, 'AUTO');
104
                // Handle the Imgix auto setting for compression/format
105
                $autoParams = [];
106
                if (empty($params['q'])) {
107
                    $autoParams[] = 'compress';
108
                }
109
                if (empty($params['fm'])) {
110
                    $autoParams[] = 'format';
111
                }
112
                if (!empty($autoParams)) {
113
                    $params['auto'] = implode(',', $autoParams);
114
                }
115
                // Handle interlaced images
116
                if (property_exists($transform, 'interlace')) {
117
                    if (($transform->interlace != 'none')
118
                        && (!empty($params['fm']))
119
                        && ($params['fm'] == 'jpg')
120
                    ) {
121
                        $params['fm'] = 'pjpg';
122
                    }
123
                }
124
                if ($settings->autoSharpenScaledImages) {
125
                    // See if the image has been scaled >= 50%
126
                    $widthScale = $asset->getWidth() / ($transform->width ?? $asset->getWidth());
127
                    $heightScale = $asset->getHeight() / ($transform->height ?? $asset->getHeight());
128
                    if (($widthScale >= 2.0) || ($heightScale >= 2.0)) {
129
                        $params['usm'] = 50.0;
130
                    }
131
                }
132
                // Handle the mode
133
                switch ($transform->mode) {
134
                    case 'fit':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
135
                        $params['fit'] = 'clamp';
136
                        break;
137
138
                    case 'stretch':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
139
                        $params['fit'] = 'scale';
140
                        break;
141
142
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
143
                        // Set a sane default
144
                        if (empty($transform->position)) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
145
                            $transform->position = 'center-center';
146
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
147
                        // Fit mode
148
                        $params['fit'] = 'crop';
149
                        $cropParams = [];
150
                        // Handle the focal point
151
                        $focalPoint = $asset->getFocalPoint();
152
                        if (!empty($focalPoint)) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
153
                            $params['fp-x'] = $focalPoint['x'];
154
                            $params['fp-y'] = $focalPoint['y'];
155
                            $cropParams[] = 'focalpoint';
156
                        } elseif (preg_match('/(top|center|bottom)-(left|center|right)/', $transform->position)) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
157
                            // Imgix defaults to 'center' if no param is present
158
                            $filteredCropParams = explode('-', $transform->position);
159
                            $filteredCropParams = array_diff($filteredCropParams, ['center']);
160
                            $cropParams[] = $filteredCropParams;
161
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
162
                        // Imgix
163
                        if (!empty($cropParams) && $transform->position !== 'center-center') {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
164
                            $params['crop'] = implode(',', $cropParams);
165
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
166
                        break;
167
                }
168
            } else {
169
                // No transform was passed in; so just auto all the things
170
                $params['auto'] = 'format,compress';
171
            }
172
            // Remove the api-key param
173
            unset($params['api-key']);
174
            // Apply the Security Token, if set
175
            if (!empty($settings->imgixSecurityToken)) {
0 ignored issues
show
Bug Best Practice introduced by
The property imgixSecurityToken does not exist on nystudio107\imageoptimize\models\Settings. Since you implemented __get, consider adding a @property annotation.
Loading history...
176
                $builder->setSignKey($settings->imgixSecurityToken);
177
            }
178
            // Finally, create the Imgix URL for this transformed image
179
            $assetUri = $this->getAssetUri($asset);
180
            $url = $builder->createURL($assetUri, $params);
181
            Craft::debug(
182
                'Imgix transform created for: '.$assetUri.' - Params: '.print_r($params, true).' - URL: '.$url,
183
                __METHOD__
184
            );
185
        }
186
187
        return $url;
188
    }
189
190
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
191
     * @param string              $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
192
     * @param Asset               $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
193
     * @param AssetTransform|null $transform
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
194
     * @param array               $params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
195
     *
196
     * @return string
197
     */
198
    public function getWebPUrl(string $url, Asset $asset, $transform, array $params = []): string
199
    {
200
        $url = preg_replace('/fm=[^&]*/', 'fm=webp', $url);
201
202
        return $url;
203
    }
204
205
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
206
     * @param Asset $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
207
     * @param array $params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
208
     *
209
     * @return null|string
210
     * @throws \yii\base\InvalidConfigException
211
     */
212
    public function getPurgeUrl(Asset $asset, array $params = [])
213
    {
214
        $url = null;
215
216
        $domain = isset($params['domain'])
217
            ? $params['domain']
218
            : 'demos.imgix.net';
219
        $builder = new UrlBuilder($domain);
220
        if ($asset && $builder) {
0 ignored issues
show
introduced by
$builder is of type Imgix\UrlBuilder, thus it always evaluated to true.
Loading history...
221
            $builder->setUseHttps(true);
222
            // Create the Imgix URL for purging this image
223
            $assetUri = $this->getAssetUri($asset);
224
            $url = $builder->createURL($assetUri, $params);
225
            // Strip the query string so we just pass in the raw URL
226
            $url = UrlHelper::stripQueryString($url);
227
        }
228
229
        return $url;
230
    }
231
232
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
233
     * @param string $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
234
     * @param array  $params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
235
     *
236
     * @return bool
237
     */
238
    public function purgeUrl(string $url, array $params = []): bool
239
    {
240
        $result = false;
241
        $apiKey = isset($params['api-key'])
242
            ? $params['api-key']
243
            : '';
244
        // create new guzzle client
245
        $guzzleClient = Craft::createGuzzleClient(['timeout' => 120, 'connect_timeout' => 120]);
246
        // Submit the sitemap index to each search engine
247
        try {
248
            /** @var ResponseInterface $response */
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...
249
            $response = $guzzleClient->post(self::IMGIX_PURGE_ENDPOINT, [
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...
250
                'auth'        => [
251
                    $apiKey,
252
                    '',
253
                ],
254
                'form_params' => [
255
                    'url' => $url,
256
                ],
257
            ]);
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...
258
            // See if it succeeded
259
            if (($response->getStatusCode() >= 200)
260
                && ($response->getStatusCode() < 400)
261
            ) {
262
                $result = true;
263
            }
264
            Craft::info(
265
                'URL purged: '.$url.' - Response code: '.$response->getStatusCode(),
266
                __METHOD__
267
            );
268
        } catch (\Exception $e) {
269
            Craft::error(
270
                'Error purging URL: '.$url.' - '.$e->getMessage(),
271
                __METHOD__
272
            );
273
        }
274
275
        return $result;
276
    }
277
278
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
279
     * @return array
280
     */
281
    public function getTransformParams(): array
282
    {
283
        $settings = ImageOptimize::$plugin->getSettings();
284
        $params = [
285
            'domain'  => $settings->imgixDomain,
0 ignored issues
show
Bug Best Practice introduced by
The property imgixDomain does not exist on nystudio107\imageoptimize\models\Settings. Since you implemented __get, consider adding a @property annotation.
Loading history...
286
            'api-key' => $settings->imgixApiKey,
0 ignored issues
show
Bug Best Practice introduced by
The property imgixApiKey does not exist on nystudio107\imageoptimize\models\Settings. Since you implemented __get, consider adding a @property annotation.
Loading history...
287
        ];
288
289
        return $params;
290
    }
291
292
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
293
     * @param Asset $asset
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
294
     *
295
     * @return mixed
296
     * @throws \yii\base\InvalidConfigException
297
     */
298
    public function getAssetUri(Asset $asset)
299
    {
300
        $volume = $asset->getVolume();
301
302
        // If this is a local volume, it implies your are using a "Web Folder"
303
        // source in Imgix. We can then also infer that:
304
        // - This volume has URLs
305
        // - The "Base URL" in Imgix is set to your domain root, per the ImageOptimize docs.
306
        //
307
        // Therefore, we need to parse the path from the full URL, so that it
308
        // includes the path of the volume.
309
        if ($volume instanceof \craft\volumes\Local) {
310
            $assetUrl = AssetsHelper::generateUrl($volume, $asset);
311
            $assetUri = parse_url($assetUrl, PHP_URL_PATH);
312
313
            return $assetUri;
314
        }
315
316
        return parent::getAssetUri($asset);
317
    }
318
319
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
320
     * @inheritdoc
321
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
322
    public function rules()
323
    {
324
        $rules = parent::rules();
325
        $rules = array_merge($rules, [
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...
326
            [['domain', 'apiKey', 'securityToken'], 'default', 'value' => ''],
327
            [['domain', 'apiKey', 'securityToken'], 'string'],
328
        ]);
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...
329
330
        return $rules;
331
    }
332
}
333