Passed
Push — v1 ( 740a10...974e3b )
by Andrew
04:45 queued 11s
created

ViteService::script()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 2
eloc 3
c 2
b 0
f 1
nc 2
nop 4
dl 0
loc 7
rs 10
1
<?php
2
/**
3
 * Vite plugin for Craft CMS 3.x
4
 *
5
 * Allows the use of the Vite.js next generation frontend tooling with Craft CMS
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) 2021 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\pluginvite\services;
12
13
use Craft;
14
use craft\base\Component;
15
use craft\helpers\Html as HtmlHelper;
16
use craft\helpers\Json as JsonHelper;
17
use craft\helpers\UrlHelper;
18
19
use yii\base\InvalidConfigException;
20
use yii\caching\ChainedDependency;
21
use yii\caching\FileDependency;
22
use yii\caching\TagDependency;
23
24
use GuzzleHttp\Client;
25
use GuzzleHttp\RequestOptions;
26
27
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
28
 * @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...
29
 * @package   Vite
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
30
 * @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...
31
 */
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...
32
class ViteService extends Component
33
{
34
    // Constants
35
    // =========================================================================
36
37
    const VITE_CLIENT = '@vite/client.js';
38
    const LEGACY_EXTENSION = '-legacy.';
39
    const LEGACY_POLYFILLS = 'vite/legacy-polyfills';
40
41
    const CACHE_KEY = 'vite';
42
    const CACHE_TAG = 'vite';
43
44
    const DEVMODE_CACHE_DURATION = 1;
45
46
    const USER_AGENT_STRING = 'User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13';
47
48
    const SAFARI_NOMODULE_FIX = '!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();';
49
50
    // Public Properties
51
    // =========================================================================
52
53
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
54
     * @var bool Should the dev server be used for?
55
     */
56
    public $useDevServer;
57
58
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
59
     * @var string File system path (or URL) to the Vite-built manifest.json
60
     */
61
    public $manifestPath;
62
63
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
64
     * @var string The public URL to the dev server (what appears in `<script src="">` tags
65
     */
66
    public $devServerPublic;
67
68
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
69
     * @var string The public URL to use when not using the dev server
70
     */
71
    public $serverPublic;
72
73
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
74
     * @var string String to be appended to the cache key
75
     */
76
    public $cacheKeySuffix = '';
77
78
    // Protected Properties
79
    // =========================================================================
80
81
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
82
     * @var bool Whether any legacy tags were found in this request
83
     */
84
    protected $hasLegacyTags = false;
85
86
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
87
     * @var bool Whether the legacy polyfill has been included yet or not
88
     */
89
    protected $legacyPolyfillIncluded = false;
90
91
    // Public Methods
92
    // =========================================================================
93
94
    /**
95
     * Return the appropriate tags to load the Vite script, either via the dev server or
96
     * extracting it from the manifest.json file
97
     *
98
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
99
     * @param bool $asyncCss
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
100
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
101
     * @param array $cssTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
102
     *
103
     * @return string
104
     */
105
    public function script(string $path, bool $asyncCss = true, array $scriptTagAttrs = [], array $cssTagAttrs = []): string
106
    {
107
        if ($this->useDevServer) {
108
            return $this->devServerScript($path, $scriptTagAttrs);
109
        }
110
111
        return $this->manifestScript($path, $asyncCss, $scriptTagAttrs, $cssTagAttrs);
112
    }
113
114
    /**
115
     * Return the script tag to load the script from the Vite dev server
116
     *
117
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
118
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
119
     *
120
     * @return string
121
     */
122
    public function devServerScript(string $path, array $scriptTagAttrs = []): string
123
    {
124
        $lines = [];
125
        // Include the entry script
126
        $url = $this->createUrl($this->devServerPublic, $path);
127
        $lines[] = HtmlHelper::jsFile($url, 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...
128
            'type' => 'module',
129
        ], $scriptTagAttrs));
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...
130
131
        return implode("\r\n", $lines);
132
    }
133
134
    /**
135
     * Return the script, module link, and CSS link tags for the script from the manifest.json file
136
     *
137
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
138
     * @param bool $asyncCss
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
139
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
140
     * @param array $cssTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
141
     *
142
     * @return string
143
     */
144
    public function manifestScript(string $path, bool $asyncCss = true, array $scriptTagAttrs = [], array $cssTagAttrs = []): string
145
    {
146
        $lines = [];
147
        $tags = $this->manifestTags($path, $asyncCss, $scriptTagAttrs, $cssTagAttrs);
148
        // Handle any legacy polyfills
149
        if ($this->hasLegacyTags && !$this->legacyPolyfillIncluded) {
150
            $lines[] = HtmlHelper::script(self::SAFARI_NOMODULE_FIX, []);
151
            $legacyPolyfillTags = $this->extractManifestTags(self::LEGACY_POLYFILLS, $asyncCss, $scriptTagAttrs, $cssTagAttrs, true);
152
            $tags = array_merge($legacyPolyfillTags, $tags);
153
            $this->legacyPolyfillIncluded = true;
154
        }
155
        foreach($tags as $tag) {
0 ignored issues
show
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
156
            if (!empty($tag)) {
157
                switch ($tag['type']) {
158
                    case 'file':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
159
                        $lines[] = HtmlHelper::jsFile($tag['url'], $tag['options']);
160
                        break;
161
                    case 'css':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
162
                        $lines[] = HtmlHelper::cssFile($tag['url'], $tag['options']);
163
                        break;
164
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
165
                        break;
166
                }
167
            }
168
        }
169
170
        return implode("\r\n", $lines);
171
    }
172
173
    /**
174
     * Register the appropriate tags to the Craft View to load the Vite script, either via the dev server or
175
     * extracting it from the manifest.json file
176
     *
177
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
178
     * @param bool $asyncCss
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
179
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
180
     * @param array $cssTagAttrs
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
181
     *
182
     * @return void
183
     * @throws InvalidConfigException
184
     */
185
    public function register(string $path, bool $asyncCss = true, array $scriptTagAttrs = [], array $cssTagAttrs = [])
186
    {
187
        if ($this->useDevServer) {
188
            $this->devServerRegister($path, $scriptTagAttrs);
189
190
            return;
191
        }
192
193
        $this->manifestRegister($path, $asyncCss, $scriptTagAttrs, $cssTagAttrs);
194
    }
195
196
    /**
197
     * Register the script tag to the Craft View to load the script from the Vite dev server
198
     *
199
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
200
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
201
     *
202
     * @return void
203
     */
204
    public function devServerRegister(string $path, array $scriptTagAttrs = [])
205
    {
206
        $view = Craft::$app->getView();
207
        // Include the entry script
208
        $url = $this->createUrl($this->devServerPublic, $path);
209
        $view->registerScript('', $view::POS_HEAD, 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...
210
            'type' => 'module',
211
            'src' => $url,
212
            ], $scriptTagAttrs));
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
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...
213
    }
214
215
    /**
216
     * Register the script, module link, and CSS link tags to the Craft View for the script from the manifest.json file
217
     *
218
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
219
     * @param bool $asyncCss
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
220
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
221
     * @param array $cssTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
222
     *
223
     * @return void
224
     * @throws InvalidConfigException
225
     */
226
    public function manifestRegister(string $path, bool $asyncCss = true, array $scriptTagAttrs = [], array $cssTagAttrs = [])
227
    {
228
        $view = Craft::$app->getView();
229
        $tags = $this->manifestTags($path, $asyncCss, $scriptTagAttrs, $cssTagAttrs);
230
        // Handle any legacy polyfills
231
        if ($this->hasLegacyTags && !$this->legacyPolyfillIncluded) {
232
            $view->registerScript(self::SAFARI_NOMODULE_FIX, $view::POS_HEAD, [], 'SAFARI_NOMODULE_FIX');
233
            $legacyPolyfillTags = $this->extractManifestTags(self::LEGACY_POLYFILLS, $asyncCss, $scriptTagAttrs, $cssTagAttrs, true);
234
            $tags = array_merge($legacyPolyfillTags, $tags);
235
            $this->legacyPolyfillIncluded = true;
236
        }
237
        foreach($tags as $tag) {
0 ignored issues
show
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
238
            if (!empty($tag)) {
239
                switch ($tag['type']) {
240
                    case 'file':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
241
                        $view->registerScript(
242
                            '',
243
                            $view::POS_HEAD,
244
                            array_merge(['src' => $tag['url']], $tag['options']),
245
                            md5($tag['url'] . json_encode($tag['options']))
246
                        );
247
                        break;
248
                    case 'css':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
249
                        $view->registerCssFile($tag['url'], $tag['options']);
250
                        break;
251
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
252
                        break;
253
                }
254
            }
255
        }
256
    }
257
258
    /**
259
     * Invalidate all of the Vite caches
260
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
261
    public function invalidateCaches()
262
    {
263
        $cache = Craft::$app->getCache();
264
        TagDependency::invalidate($cache, self::CACHE_TAG . $this->cacheKeySuffix);
265
        Craft::info('All Vite caches cleared', __METHOD__);
266
    }
267
268
    // Protected Methods
269
    // =========================================================================
270
271
    /**
272
     * Return an array of tags from the manifest, for both modern and legacy builds
273
     *
274
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
275
     * @param bool $asyncCss
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
276
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
277
     * @param array $cssTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
278
     *
279
     * @return array
280
     */
281
    protected function manifestTags(string $path, bool $asyncCss = true, array $scriptTagAttrs = [], array $cssTagAttrs = []): array
282
    {
283
        // Get the modern tags for this $path
284
        $tags = $this->extractManifestTags($path, $asyncCss, $scriptTagAttrs, $cssTagAttrs, false);
285
        // Look for a legacy version of this $path too
286
        $parts = pathinfo($path);
287
        $legacyPath = $parts['dirname']
288
            . '/'
289
            . $parts['filename']
290
            . self::LEGACY_EXTENSION
291
            . $parts['extension'];
292
        $legacyTags = $this->extractManifestTags($legacyPath, $asyncCss, $scriptTagAttrs, $cssTagAttrs, true);
293
        // Set a flag to indicate the some legacy gets were found
294
        $legacyPolyfillTags = [];
295
        if (!empty($legacyTags)) {
296
            $this->hasLegacyTags = true;
297
        }
298
        return array_merge(
299
            $legacyPolyfillTags,
300
            $tags,
301
            $legacyTags
302
        );
303
    }
304
305
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $legacy should have a doc-comment as per coding-style.
Loading history...
306
     * Return an array of data describing the  script, module link, and CSS link tags for the
307
     * script from the manifest.json file
308
     *
309
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
310
     * @param bool $asyncCss
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
311
     * @param array $scriptTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
312
     * @param array $cssTagAttrs
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
313
     *
314
     * @return array
315
     */
316
    protected function extractManifestTags(string $path, bool $asyncCss = true, array $scriptTagAttrs = [], array $cssTagAttrs = [], $legacy = false): array
317
    {
318
        $tags = [];
319
        // Grab the manifest
320
        $pathOrUrl = (string)Craft::parseEnv($this->manifestPath);
321
        $manifest = $this->fetchFile($pathOrUrl, [JsonHelper::class, 'decodeIfJson']);
322
        // If no manifest file is found, bail
323
        if ($manifest === null) {
324
            Craft::error('Manifest not found at ' . $this->manifestPath, __METHOD__);
325
326
            return [];
327
        }
328
        // Set the async CSS args
329
        $asyncCssOptions = [];
330
        if ($asyncCss) {
331
            $asyncCssOptions = [
332
                'media' => 'print',
333
                'onload' => "this.media='all'",
334
            ];
335
        }
336
        // Set the script args
337
        $scriptOptions = [
338
            'type' => 'module',
339
            'crossorigin' => true,
340
        ];
341
        if ($legacy) {
342
            $scriptOptions = [
343
                'type' => 'nomodule',
344
            ];
345
        }
346
        // Iterate through the manifest
347
        foreach ($manifest as $manifestKey => $entry) {
348
            if (isset($entry['isEntry']) && $entry['isEntry']) {
349
                // Include the entry script
350
                if (isset($entry['file']) && strpos($path, $manifestKey) !== false) {
351
                    $tags[] = [
352
                        'type' => 'file',
353
                        'url' => $this->createUrl($this->serverPublic, $entry['file']),
354
                        'options' => array_merge($scriptOptions, $scriptTagAttrs)
355
                    ];
356
                    // Include any CSS tags
357
                    $cssFiles = [];
358
                    $this->extractCssFiles($manifest, $manifestKey, $cssFiles);
359
                    foreach ($cssFiles as $cssFile) {
360
                        $tags[] = [
361
                            'type' => 'css',
362
                            'url' => $this->createUrl($this->serverPublic, $cssFile),
363
                            'options' => 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...
364
                                'rel' => 'stylesheet',
365
                            ], $asyncCssOptions, $cssTagAttrs)
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...
366
                        ];
367
                    }
368
                }
369
            }
370
        }
371
372
        return $tags;
373
    }
374
375
    /**
376
     * Extract any CSS files from entries recursively
377
     *
378
     * @param array $manifest
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
379
     * @param string $manifestKey
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
380
     * @param array $cssFiles
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
381
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
382
    protected function extractCssFiles(array $manifest, string $manifestKey, array &$cssFiles)
383
    {
384
        if (isset($manifest[$manifestKey])) {
385
            $entry = $manifest[$manifestKey];
386
            // Handle any CSS files
387
            if (isset($entry['css'])) {
388
                foreach ($entry['css'] as $css) {
389
                    $cssFiles[] = $css;
390
                }
391
            }
392
            //  Handle imports recursively
393
            if (isset($entry['imports'])) {
394
                foreach ($entry['imports'] as $import) {
395
                    $this->extractCssFiles($manifest, $import, $cssFiles);
396
                }
397
            }
398
            //  Handle dynamic imports recursively
399
            if (isset($entry['dynamicImports'])) {
400
                foreach ($entry['dynamicImports'] as $dynamicImport) {
401
                    $this->extractCssFiles($manifest, $dynamicImport, $cssFiles);
402
                }
403
            }
404
        }
405
    }
406
407
    /**
408
     * Combine a path with a URL to create a URL
409
     *
410
     * @param string $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
411
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
412
     *
413
     * @return string
414
     */
415
    protected function createUrl(string $url, string $path): string
416
    {
417
        $url = (string)Craft::parseEnv($url);
418
        return rtrim($url, '/') . '/' . trim($path, '/');
419
    }
420
421
    /**
422
     * Return the contents of a local or remote file, or null
423
     *
424
     * @param string $pathOrUrl
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...
Coding Style introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
425
     * @param callable|null $callback
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...
426
     * @return mixed
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
427
     */
428
    protected function fetchFile(string $pathOrUrl, callable $callback = null)
429
    {
430
        // Create the dependency tags
431
        $dependency = new TagDependency([
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...
432
            'tags' => [
433
                self::CACHE_TAG . $this->cacheKeySuffix,
434
                self::CACHE_TAG . $this->cacheKeySuffix . $pathOrUrl,
435
            ],
436
        ]);
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...
437
        // If this is a file path such as for the `manifest.json`, add a FileDependency so it's cache bust if the file changes
438
        if (!UrlHelper::isAbsoluteUrl($pathOrUrl)) {
439
            $dependency = new ChainedDependency([
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...
440
                'dependencies' => [
441
                    new FileDependency([
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...
442
                        'fileName' => $pathOrUrl
443
                    ]),
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...
444
                    $dependency
445
                ]
446
            ]);
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...
447
        }
448
        // Set the cache duration based on devMode
449
        $cacheDuration = Craft::$app->getConfig()->getGeneral()->devMode
450
            ? self::DEVMODE_CACHE_DURATION
451
            : null;
452
        // Get the result from the cache, or parse the file
453
        $cache = Craft::$app->getCache();
454
        $file = $cache->getOrSet(
455
            self::CACHE_KEY . $this->cacheKeySuffix . $pathOrUrl,
456
            function () use ($pathOrUrl, $callback) {
457
                $contents = null;
458
                $result = null;
459
                if (UrlHelper::isAbsoluteUrl($pathOrUrl)) {
460
                    // See if we can connect to the server
461
                    $clientOptions = [
462
                        RequestOptions::HTTP_ERRORS => false,
463
                        RequestOptions::CONNECT_TIMEOUT => 3,
464
                        RequestOptions::VERIFY => false,
465
                        RequestOptions::TIMEOUT => 5,
466
                    ];
467
                    $client = new Client($clientOptions);
468
                    try {
469
                        $response = $client->request('GET', $pathOrUrl, [
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...
470
                            RequestOptions::HEADERS => [
471
                                'User-Agent' => self::USER_AGENT_STRING,
472
                                'Accept' => '*/*',
473
                            ],
474
                        ]);
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...
475
                        if ($response->getStatusCode() === 200) {
476
                            $contents = $response->getBody()->getContents();
477
                        }
478
                    } catch (\Throwable $e) {
479
                        Craft::error($e, __METHOD__);
480
                    }
481
                } else {
482
                    $contents = @file_get_contents($pathOrUrl);
483
                }
484
                if ($contents) {
485
                    $result = $contents;
486
                    if ($callback) {
487
                        $result = $callback($result);
488
                    }
489
                }
490
491
                return $result;
492
            },
493
            $cacheDuration,
494
            $dependency
495
        );
496
497
        return $file;
498
    }
499
}
500