Passed
Push — v1 ( bd6b35...d8ddb5 )
by Andrew
12:25 queued 04:35
created

Manifest::combinePaths()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 24
rs 9.6666
cc 3
nc 1
nop 1
1
<?php
2
/**
3
 * Twigpack plugin for Craft CMS 3.x
4
 *
5
 * Twigpack is the conduit between Twig and webpack, with manifest.json &
6
 * webpack-dev-server HMR support
7
 *
8
 * @link      https://nystudio107.com/
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
9
 * @copyright Copyright (c) 2018 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
10
 */
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...
11
12
namespace nystudio107\imageoptimize\helpers;
13
14
use Craft;
15
use craft\helpers\Json as JsonHelper;
16
use craft\helpers\UrlHelper;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, nystudio107\imageoptimize\helpers\UrlHelper. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
17
18
use yii\base\Exception;
19
use yii\caching\TagDependency;
20
use yii\web\NotFoundHttpException;
21
22
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
23
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
24
 * @package   Twigpack
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
25
 * @since     1.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...
26
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
27
class Manifest
28
{
29
    // Constants
30
    // =========================================================================
31
32
    const CACHE_KEY = 'twigpack-image-optimize';
33
    const CACHE_TAG = 'twigpack-image-optimize';
34
35
    const DEVMODE_CACHE_DURATION = 1;
36
37
    const SUPPRESS_ERRORS_FOR_MODULES = [
38
        'styles.js',
39
        'commons.js',
40
        'vendors.js',
41
        'vendors.css',
42
    ];
43
44
    // Protected Static Properties
45
    // =========================================================================
46
47
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
48
     * @var array
49
     */
50
    protected static $files;
51
52
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
53
     * @var bool
54
     */
55
    protected static $isHot = false;
56
57
    // Public Static Methods
58
    // =========================================================================
59
60
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
61
     * @param array  $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
62
     * @param string $moduleName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
63
     * @param bool   $async
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
64
     *
65
     * @return string
66
     * @throws NotFoundHttpException
67
     */
68
    public static function getCssModuleTags(array $config, string $moduleName, bool $async): string
69
    {
70
        $legacyModule = self::getModule($config, $moduleName, 'legacy', true);
71
        if ($legacyModule === null) {
72
            return '';
73
        }
74
        $lines = [];
75
        if ($async) {
76
            $lines[] = "<link rel=\"preload\" href=\"{$legacyModule}\" as=\"style\" onload=\"this.onload=null;this.rel='stylesheet'\" />";
77
            $lines[] = "<noscript><link rel=\"stylesheet\" href=\"{$legacyModule}\"></noscript>";
78
        } else {
79
            $lines[] = "<link rel=\"stylesheet\" href=\"{$legacyModule}\" />";
80
        }
81
82
        return implode("\r\n", $lines);
83
    }
84
85
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
86
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
87
     *
88
     * @return string
89
     */
90
    public static function getCssInlineTags(string $path): string
91
    {
92
        $result = self::getFile($path);
93
        if ($result) {
94
            $result = "<style>\r\n".$result."</style>\r\n";
95
            return $result;
96
        }
97
98
        return '';
99
    }
100
101
    /**
102
     * Returns the uglified loadCSS rel=preload Polyfill as per:
103
     * https://github.com/filamentgroup/loadCSS#how-to-use-loadcss-recommended-example
104
     *
105
     * @return string
106
     */
107
    public static function getCssRelPreloadPolyfill(): string
108
    {
109
        return <<<EOT
110
<script>
111
/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
112
!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){var e=t.media||"all";function a(){t.media=e}t.addEventListener?t.addEventListener("load",a):t.attachEvent&&t.attachEvent("onload",a),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(a,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this);
113
</script>
114
EOT;
115
    }
116
117
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
118
     * @param array  $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
119
     * @param string $moduleName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
120
     * @param bool   $async
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
121
     *
122
     * @return null|string
123
     * @throws NotFoundHttpException
124
     */
125
    public static function getJsModuleTags(array $config, string $moduleName, bool $async)
126
    {
127
        $legacyModule = self::getModule($config, $moduleName, 'legacy');
128
        if ($legacyModule === null) {
129
            return '';
130
        }
131
        if ($async) {
132
            $modernModule = self::getModule($config, $moduleName, 'modern');
133
            if ($modernModule === null) {
134
                return '';
135
            }
136
        }
137
        $lines = [];
138
        if ($async) {
139
            $lines[] = "<script type=\"module\" src=\"{$modernModule}\"></script>";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $modernModule does not seem to be defined for all execution paths leading up to this point.
Loading history...
140
            $lines[] = "<script nomodule src=\"{$legacyModule}\"></script>";
141
        } else {
142
            $lines[] = "<script src=\"{$legacyModule}\"></script>";
143
        }
144
145
        return implode("\r\n", $lines);
146
    }
147
148
    /**
149
     * Safari 10.1 supports modules, but does not support the `nomodule`
150
     * attribute - it will load <script nomodule> anyway. This snippet solve
151
     * this problem, but only for script tags that load external code, e.g.:
152
     * <script nomodule src="nomodule.js"></script>
153
     *
154
     * Again: this will **not* # prevent inline script, e.g.:
155
     * <script nomodule>alert('no modules');</script>.
156
     *
157
     * This workaround is possible because Safari supports the non-standard
158
     * 'beforeload' event. This allows us to trap the module and nomodule load.
159
     *
160
     * Note also that `nomodule` is supported in later versions of Safari -
161
     * it's just 10.1 that omits this attribute.
162
     *
163
     * c.f.: https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc
164
     *
165
     * @return string
166
     */
167
    public static function getSafariNomoduleFix(): string
168
    {
169
        return <<<EOT
170
<script>
171
!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()}}();
172
</script>
173
EOT;
174
    }
175
176
    /**
177
     * Return the URI to a module
178
     *
179
     * @param array  $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
180
     * @param string $moduleName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
181
     * @param string $type
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
182
     * @param bool   $soft
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
183
     *
184
     * @return null|string
185
     * @throws NotFoundHttpException
186
     */
187
    public static function getModule(array $config, string $moduleName, string $type = 'modern', bool $soft = false)
188
    {
189
        // Get the module entry
190
        $module = self::getModuleEntry($config, $moduleName, $type, $soft);
191
        if ($module !== null) {
192
            $prefix = self::$isHot
193
                ? $config['devServer']['publicPath']
194
                : $config['server']['publicPath'];
195
            // If the module isn't a full URL, prefix it
196
            if (!UrlHelper::isAbsoluteUrl($module)) {
197
                $module = self::combinePaths($prefix, $module);
198
            }
199
            // Resolve any aliases
200
            $alias = Craft::getAlias($module, false);
201
            if ($alias) {
202
                $module = $alias;
203
            }
204
            // Make sure it's a full URL
205
            if (!UrlHelper::isAbsoluteUrl($module) && !is_file($module)) {
0 ignored issues
show
Bug introduced by
It seems like $module can also be of type true; however, parameter $url of craft\helpers\UrlHelper::isAbsoluteUrl() 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

205
            if (!UrlHelper::isAbsoluteUrl(/** @scrutinizer ignore-type */ $module) && !is_file($module)) {
Loading history...
Bug introduced by
It seems like $module can also be of type true; however, parameter $filename of is_file() 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

205
            if (!UrlHelper::isAbsoluteUrl($module) && !is_file(/** @scrutinizer ignore-type */ $module)) {
Loading history...
206
                try {
207
                    $module = UrlHelper::siteUrl($module);
0 ignored issues
show
Bug introduced by
It seems like $module can also be of type true; however, parameter $path of craft\helpers\UrlHelper::siteUrl() 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

207
                    $module = UrlHelper::siteUrl(/** @scrutinizer ignore-type */ $module);
Loading history...
208
                } catch (Exception $e) {
209
                    Craft::error($e->getMessage(), __METHOD__);
210
                }
211
            }
212
        }
213
214
        return $module;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $module also could return the type true which is incompatible with the documented return type null|string.
Loading history...
215
    }
216
217
    /**
218
     * Return a module's raw entry from the manifest
219
     *
220
     * @param array  $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
221
     * @param string $moduleName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
222
     * @param string $type
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
223
     * @param bool   $soft
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
224
     *
225
     * @return null|string
226
     * @throws NotFoundHttpException
227
     */
228
    public static function getModuleEntry(array $config, string $moduleName, string $type = 'modern', bool $soft = false)
229
    {
230
        $module = null;
231
        // Get the manifest file
232
        $manifest = self::getManifestFile($config, $type);
233
        if ($manifest !== null) {
234
            // Make sure it exists in the manifest
235
            if (empty($manifest[$moduleName])) {
236
                // Don't report errors for any files in SUPPRESS_ERRORS_FOR_MODULES
237
                if (!in_array($moduleName, self::SUPPRESS_ERRORS_FOR_MODULES)) {
238
                    self::reportError(Craft::t(
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...
239
                        'image-optimize',
240
                        'Module does not exist in the manifest: {moduleName}',
241
                        ['moduleName' => $moduleName]
242
                    ), $soft);
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...
243
                }
244
245
                return null;
246
            }
247
            $module = $manifest[$moduleName];
248
        }
249
250
        return $module;
251
    }
252
253
    /**
254
     * Return a JSON-decoded manifest file
255
     *
256
     * @param array  $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
257
     * @param string $type
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
258
     *
259
     * @return null|array
260
     * @throws NotFoundHttpException
261
     */
262
    public static function getManifestFile(array $config, string $type = 'modern')
263
    {
264
        $manifest = null;
265
        // Determine whether we should use the devServer for HMR or not
266
        $devMode = Craft::$app->getConfig()->getGeneral()->devMode;
267
        self::$isHot = ($devMode && $config['useDevServer']);
268
        // Try to get the manifest
269
        while ($manifest === null) {
270
            $manifestPath = self::$isHot
271
                ? $config['devServer']['manifestPath']
272
                : $config['server']['manifestPath'];
273
            // Normalize the path
274
            $path = self::combinePaths($manifestPath, $config['manifest'][$type]);
275
            $manifest = self::getJsonFile($path);
276
            // If the manifest isn't found, and it was hot, fall back on non-hot
277
            if ($manifest === null) {
278
                // We couldn't find a manifest; throw an error
279
                self::reportError(Craft::t(
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...
280
                    'image-optimize',
281
                    'Manifest file not found at: {manifestPath}',
282
                    ['manifestPath' => $manifestPath]
283
                ), true);
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...
284
                if (self::$isHot) {
285
                    // Try again, but not with home module replacement
286
                    self::$isHot = false;
287
                } else {
288
                    // Give up and return null
289
                    return null;
290
                }
291
            }
292
        }
293
294
        return $manifest;
295
    }
296
297
    /**
298
     * Returns the contents of a file from a URI path
299
     *
300
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
301
     *
302
     * @return string
303
     */
304
    public static function getFile(string $path): string
305
    {
306
        return self::getFileFromUri($path, null) ?? '';
307
    }
308
309
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
310
     * @param array  $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
311
     * @param string $fileName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
312
     * @param string $type
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
313
     *
314
     * @return string
315
     */
316
    public static function getFileFromManifest(array $config, string $fileName, string $type = 'legacy'): string
317
    {
318
        try {
319
            $path = self::getModuleEntry($config, $fileName, $type, true);
320
        } catch (NotFoundHttpException $e) {
321
            Craft::error($e->getMessage(), __METHOD__);
322
        }
323
        if ($path !== null) {
324
            $path = self::combinePaths(
325
                $config['localFiles']['basePath'],
326
                $path
327
            );
328
329
            return self::getFileFromUri($path, null) ?? '';
330
        }
331
332
        return '';
333
    }
334
335
    /**
336
     * Return the contents of a JSON file from a URI path
337
     *
338
     * @param string $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
339
     *
340
     * @return null|array
341
     */
342
    protected static function getJsonFile(string $path)
343
    {
344
        return self::getFileFromUri($path, [self::class, 'jsonFileDecode']);
345
    }
346
347
    /**
348
     * Invalidate all of the manifest caches
349
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
350
    public static function invalidateCaches()
351
    {
352
        $cache = Craft::$app->getCache();
353
        TagDependency::invalidate($cache, self::CACHE_TAG);
354
        Craft::info('All manifest caches cleared', __METHOD__);
355
    }
356
357
    // Protected Static Methods
358
    // =========================================================================
359
360
    /**
361
     * Return the contents of a file from a URI path
362
     *
363
     * @param string        $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
364
     * @param callable|null $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
365
     *
366
     * @return null|mixed
367
     */
368
    protected static function getFileFromUri(string $path, callable $callback = null)
369
    {
370
        // Resolve any aliases
371
        $alias = Craft::getAlias($path, false);
372
        if ($alias) {
373
            $path = $alias;
374
        }
375
        // Make sure it's a full URL
376
        if (!UrlHelper::isAbsoluteUrl($path) && !is_file($path)) {
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type true; however, parameter $filename of is_file() 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

376
        if (!UrlHelper::isAbsoluteUrl($path) && !is_file(/** @scrutinizer ignore-type */ $path)) {
Loading history...
Bug introduced by
It seems like $path can also be of type true; however, parameter $url of craft\helpers\UrlHelper::isAbsoluteUrl() 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

376
        if (!UrlHelper::isAbsoluteUrl(/** @scrutinizer ignore-type */ $path) && !is_file($path)) {
Loading history...
377
            try {
378
                $path = UrlHelper::siteUrl($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type true; however, parameter $path of craft\helpers\UrlHelper::siteUrl() 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

378
                $path = UrlHelper::siteUrl(/** @scrutinizer ignore-type */ $path);
Loading history...
379
            } catch (Exception $e) {
380
                Craft::error($e->getMessage(), __METHOD__);
381
            }
382
        }
383
384
        return self::getFileContents($path, $callback);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type true; however, parameter $path of nystudio107\imageoptimiz...fest::getFileContents() 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

384
        return self::getFileContents(/** @scrutinizer ignore-type */ $path, $callback);
Loading history...
385
    }
386
387
    /**
388
     * Return the contents of a file from the passed in path
389
     *
390
     * @param string   $path
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
391
     * @param callable $callback
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
392
     *
393
     * @return null|mixed
394
     */
395
    protected static function getFileContents(string $path, callable $callback = null)
396
    {
397
        // Return the memoized manifest if it exists
398
        if (!empty(self::$files[$path])) {
399
            return self::$files[$path];
400
        }
401
        // Create the dependency tags
402
        $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...
403
            'tags' => [
404
                self::CACHE_TAG,
405
                self::CACHE_TAG.$path,
406
            ],
407
        ]);
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...
408
        // Set the cache duration based on devMode
409
        $cacheDuration = Craft::$app->getConfig()->getGeneral()->devMode
410
            ? self::DEVMODE_CACHE_DURATION
411
            : null;
412
        // Get the result from the cache, or parse the file
413
        $cache = Craft::$app->getCache();
414
        $file = $cache->getOrSet(
415
            self::CACHE_KEY.$path,
416
            function () use ($path, $callback) {
417
                $result = null;
418
                $contents = @file_get_contents($path);
419
                if ($contents) {
420
                    $result = $contents;
421
                    if ($callback) {
422
                        $result = $callback($result);
423
                    }
424
                }
425
426
                return $result;
427
            },
428
            $cacheDuration,
429
            $dependency
430
        );
431
        self::$files[$path] = $file;
432
433
        return $file;
434
    }
435
436
    /**
437
     * Combined the passed in paths, whether file system or URL
438
     *
439
     * @param string ...$paths
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
440
     *
441
     * @return string
442
     */
443
    protected static function combinePaths(string ...$paths): string
444
    {
445
        $last_key = \count($paths) - 1;
446
        array_walk($paths, function (&$val, $key) use ($last_key) {
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...
447
            switch ($key) {
448
                case 0:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
449
                    $val = rtrim($val, '/ ');
450
                    break;
451
                case $last_key:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
452
                    $val = ltrim($val, '/ ');
453
                    break;
454
                default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
455
                    $val = trim($val, '/ ');
456
                    break;
457
            }
458
        });
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...
459
460
        $first = array_shift($paths);
461
        $last = array_pop($paths);
462
        $paths = array_filter($paths);
463
        array_unshift($paths, $first);
464
        $paths[] = $last;
465
466
        return implode('/', $paths);
467
    }
468
469
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
470
     * @param string $error
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
471
     * @param bool   $soft
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
472
     *
473
     * @throws NotFoundHttpException
474
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
475
    protected static function reportError(string $error, $soft = false)
476
    {
477
        $devMode = Craft::$app->getConfig()->getGeneral()->devMode;
478
        if ($devMode && !$soft) {
479
            throw new NotFoundHttpException($error);
480
        }
481
        Craft::error($error, __METHOD__);
482
    }
483
484
    // Private Static Methods
485
    // =========================================================================
486
487
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
488
     * @param $string
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
489
     *
490
     * @return mixed
491
     */
492
    private static function jsonFileDecode($string)
0 ignored issues
show
Coding Style introduced by
Private method name "Manifest::jsonFileDecode" must be prefixed with an underscore
Loading history...
493
    {
494
        return JsonHelper::decodeIfJson($string);
495
    }
496
}
497