Completed
Push — master ( 23d101...355dc3 )
by Sang
01:16
created

Assets::getSourceUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
namespace Botble\Assets;
4
5
use Illuminate\Config\Repository;
6
7
/**
8
 * Class Assets.
9
 *
10
 * @since 22/07/2015 11:23 PM
11
 */
12
class Assets
13
{
14
    /**
15
     * @var Repository
16
     */
17
    protected $config;
18
19
    /**
20
     * @var HtmlBuilder
21
     */
22
    protected $htmlBuilder;
23
24
    /**
25
     * @var array
26
     */
27
    protected $scripts = [];
28
29
    /**
30
     * @var array
31
     */
32
    protected $styles = [];
33
34
    /**
35
     * @var array
36
     */
37
    protected $appendedScripts = [
38
        'header' => [],
39
        'footer' => [],
40
    ];
41
42
    /**
43
     * @var array
44
     */
45
    protected $appendedStyles = [];
46
47
    /**
48
     * @var string
49
     */
50
    protected $build = '';
51
52
    const ASSETS_SCRIPT_POSITION_HEADER = 'header';
53
54
    const ASSETS_SCRIPT_POSITION_FOOTER = 'footer';
55
56
    /**
57
     * Assets constructor.
58
     *
59
     * @param Repository $config
60
     * @param HtmlBuilder $htmlBuilder
61
     */
62
    public function __construct(Repository $config, HtmlBuilder $htmlBuilder)
63
    {
64
        $this->config = $config->get('assets');
65
66
        $this->scripts = $this->config['scripts'];
67
68
        $this->styles = $this->config['styles'];
69
70
        $this->htmlBuilder = $htmlBuilder;
71
    }
72
73
    /**
74
     * Add scripts to current module.
75
     *
76
     * @param array $assets
77
     * @return $this
78
     */
79
    public function addScripts($assets)
80
    {
81
        $this->scripts = array_merge($this->scripts, (array)$assets);
82
83
        return $this;
84
    }
85
86
    /**
87
     * Add Css to current module.
88
     *
89
     * @param array $assets
90
     * @return $this
91
     */
92
    public function addStyles($assets)
93
    {
94
        $this->styles = array_merge($this->styles, (array)$assets);
95
96
        return $this;
97
    }
98
99
    /**
100
     * Add styles directly.
101
     *
102
     * @param array|string $assets
103
     * @return $this
104
     */
105
    public function addStylesDirectly($assets)
106
    {
107 View Code Duplication
        foreach ((array)$assets as &$item) {
0 ignored issues
show
Bug introduced by
The expression (array) $assets cannot be used as a reference.

Let?s assume that you have the following foreach statement:

foreach ($array as &$itemValue) { }

$itemValue is assigned by reference. This is possible because the expression (in the example $array) can be used as a reference target.

However, if we were to replace $array with something different like the result of a function call as in

foreach (getArray() as &$itemValue) { }

then assigning by reference is not possible anymore as there is no target that could be modified.

Available Fixes

1. Do not assign by reference
foreach (getArray() as $itemValue) { }
2. Assign to a local variable first
$array = getArray();
foreach ($array as &$itemValue) {}
3. Return a reference
function &getArray() { $array = array(); return $array; }

foreach (getArray() as &$itemValue) { }
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
108
            if (!in_array($item, $this->appendedStyles)) {
109
                $this->appendedStyles[] = [
110
                    'src'        => $item,
111
                    'attributes' => [],
112
                ];
113
            }
114
        }
115
116
        return $this;
117
    }
118
119
    /**
120
     * Add scripts directly.
121
     *
122
     * @param string|array $assets
123
     * @param string $location
124
     * @return $this
125
     */
126
    public function addScriptsDirectly($assets, $location = self::ASSETS_SCRIPT_POSITION_FOOTER)
127
    {
128 View Code Duplication
        foreach ((array)$assets as &$item) {
0 ignored issues
show
Bug introduced by
The expression (array) $assets cannot be used as a reference.

Let?s assume that you have the following foreach statement:

foreach ($array as &$itemValue) { }

$itemValue is assigned by reference. This is possible because the expression (in the example $array) can be used as a reference target.

However, if we were to replace $array with something different like the result of a function call as in

foreach (getArray() as &$itemValue) { }

then assigning by reference is not possible anymore as there is no target that could be modified.

Available Fixes

1. Do not assign by reference
foreach (getArray() as $itemValue) { }
2. Assign to a local variable first
$array = getArray();
foreach ($array as &$itemValue) {}
3. Return a reference
function &getArray() { $array = array(); return $array; }

foreach (getArray() as &$itemValue) { }
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
            if (!in_array($item, $this->appendedScripts[$location])) {
130
                $this->appendedScripts[$location][] = [
131
                    'src'        => $item,
132
                    'attributes' => [],
133
                ];
134
            }
135
        }
136
137
        return $this;
138
    }
139
140
    /**
141
     * Remove Css to current module.
142
     *
143
     * @param array $assets
144
     * @return $this
145
     */
146
    public function removeStyles($assets)
147
    {
148
        foreach ((array)$assets as $rem) {
149
            array_forget($this->styles, array_search($rem, $this->styles));
150
        }
151
152
        return $this;
153
    }
154
155
    /**
156
     * Add scripts.
157
     *
158
     * @param array $assets
159
     * @return $this
160
     */
161
    public function removeScripts($assets)
162
    {
163
        foreach ((array)$assets as $rem) {
164
            array_forget($this->scripts, array_search($rem, $this->scripts));
165
        }
166
167
        return $this;
168
    }
169
170
    /**
171
     * Get All scripts in current module.
172
     *
173
     * @param string $location `header` or `footer`
174
     * @return array
175
     */
176
    public function getScripts($location = null)
177
    {
178
        $scripts = [];
179
180
        $this->scripts = array_unique($this->scripts);
181
182
        foreach ($this->scripts as $script) {
183
            $configName = 'resources.scripts.' . $script;
184
185
            if (!array_has($this->config, $configName)) {
186
                continue;
187
            }
188
189
            if (!empty($location) && $location !== array_get($this->config, $configName . '.location')) {
190
                continue; // Skip assets that don't match this location
191
            }
192
193
            $scripts = array_merge($scripts, $this->getScriptItem($location, $configName, $script));
194
        }
195
196
        if (isset($this->appendedScripts[$location])) {
197
            $scripts = array_merge($scripts, $this->appendedScripts[$location]);
198
        }
199
200
        return $scripts;
201
    }
202
203
    /**
204
     * Get All CSS in current module.
205
     *
206
     * @param array $lastStyles Append last CSS to current module
207
     * @return array
208
     */
209
    public function getStyles($lastStyles = [])
210
    {
211
        $styles = [];
212
        if (!empty($lastStyles)) {
213
            $this->styles = array_merge($this->styles, $lastStyles);
214
        }
215
216
        $this->styles = array_unique($this->styles);
217
218
        foreach ($this->styles as $style) {
219
            $configName = 'resources.styles.' . $style;
220
221
            if (!array_has($this->config, $configName)) {
222
                continue;
223
            }
224
225
            $src = $this->getSourceUrl($configName);
226
227
            $attributes = $this->isUsingCdn($configName) ? [] : array_get($this->config, $configName . '.attributes', []);
228
229
            foreach ((array)$src as $s) {
230
                $styles[] = [
231
                    'src'        => $s,
232
                    'attributes' => $attributes,
233
                ];
234
            }
235
        }
236
237
        return array_merge($styles, $this->appendedStyles);
238
    }
239
240
    /**
241
     * Convert script to html.
242
     *
243
     * @param string $name
244
     * @return  string|null
245
     */
246
    public function scriptToHtml($name)
247
    {
248
        return $this->itemToHtml($name, 'script');
249
    }
250
251
    /**
252
     * Convert style to html.
253
     *
254
     * @param string $name
255
     */
256
    public function styleToHtml($name)
257
    {
258
        return $this->itemToHtml($name, 'style');
259
    }
260
261
    /**
262
     * Render assets to header.
263
     *
264
     * @param array $lastStyles
265
     * @return string
266
     * @throws \Throwable
267
     */
268
    public function renderHeader($lastStyles = [])
269
    {
270
        $styles = $this->getStyles($lastStyles);
271
272
        $headScripts = $this->getScripts(self::ASSETS_SCRIPT_POSITION_HEADER);
273
274
        return view('assets::header', compact('styles', 'headScripts'))->render();
0 ignored issues
show
Bug introduced by
The method render does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
275
    }
276
277
    /**
278
     * Render assets to footer.
279
     *
280
     * @return string
281
     * @throws \Throwable
282
     */
283
    public function renderFooter()
284
    {
285
        $bodyScripts = $this->getScripts(self::ASSETS_SCRIPT_POSITION_FOOTER);
286
287
        return view('assets::footer', compact('bodyScripts'))->render();
0 ignored issues
show
Bug introduced by
The method render does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
288
    }
289
290
    /**
291
     * Get script item.
292
     *
293
     * @param string $location
294
     * @param string $configName
295
     * @param string $script
296
     * @return array
297
     */
298
    protected function getScriptItem($location, $configName, $script)
299
    {
300
        $scripts = [];
301
302
        $isUsingCdn = $this->isUsingCdn($configName);
303
304
        $attributes = $isUsingCdn ? [] : array_get($this->config, $configName . '.attributes', []);
305
306
        $src = $this->getSourceUrl($configName);
307
        
308
        foreach ((array)$src as $s) {
309
            $scripts[] = [
310
                'src'        => $s,
311
                'attributes' => $attributes,
312
            ];
313
        }
314
315
        if (empty($src) &&
316
            $isUsingCdn &&
317
            $location === self::ASSETS_SCRIPT_POSITION_HEADER &&
318
            array_has($this->config, $configName . '.fallback')) {
319
            $scripts[] = $this->getFallbackScript($src, $configName);
320
        }
321
322
        if (array_get($this->config, $configName . '.include_style')) {
323
            $this->addStyles([$script]);
324
        }
325
326
        return $scripts;
327
    }
328
329
    /**
330
     * Fallback to local script if CDN fails.
331
     *
332
     * @param string $src
333
     * @param string $configName
334
     * @return array
335
     */
336
    protected function getFallbackScript($src, $configName)
337
    {
338
        return [
339
            'src'         => $src,
340
            'fallback'    => array_get($this->config, $configName . '.fallback'),
341
            'fallbackURL' => array_get($this->config, $configName . '.src.local'),
342
        ];
343
    }
344
345
    /**
346
     * Convert item to html.
347
     *
348
     * @param string $name
349
     * @param string $type
350
     * @return null|string
351
     */
352
    protected function itemToHtml($name, $type = 'style')
353
    {
354
        $html = '';
355
356
        if (!in_array($type, ['style', 'script'])) {
357
            return $html;
358
        }
359
360
        $configName = 'resources.' . $type . 's.' . $name;
361
362
        if (!array_has($this->config, $configName)) {
363
            return $html;
364
        }
365
        
366
        $src = $this->getSourceUrl($configName);
367
368
        foreach ((array)$src as $item) {
369
            $html .= $this->{$type}($item, ['class' => 'hidden'])->toHtml();
370
        }
371
372
        return $html;
373
    }
374
375
    /**
376
     * @param $configName
377
     * @return mixed
378
     */
379
    protected function getSourceUrl($configName)
380
    {
381
        $src = array_get($this->config, $configName . '.src.local');
382
383
        if ($this->isUsingCdn($configName)) {
384
            $src = array_get($this->config, $configName . '.src.cdn');
385
        }
386
        
387
        return $src;
388
    }
389
390
    /**
391
     * @param $configName
392
     * @return bool
393
     */
394
    protected function isUsingCdn($configName)
395
    {
396
        return array_get($this->config, $configName . '.use_cdn', false) && !$this->config['offline'];
397
    }
398
399
    /**
400
     * @return string
401
     */
402
    public function getBuildVersion()
403
    {
404
        return $this->build = $this->config['enable_version'] ? '?v=' . $this->config['version'] : '';
405
    }
406
407
    /**
408
     * @return HtmlBuilder
409
     */
410
    public function getHtmlBuilder()
411
    {
412
        return $this->htmlBuilder;
413
    }
414
}
415