Completed
Push — master ( 091993...b15dfb )
by Sang
72:09 queued 61:35
created

Assets   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 430
Duplicated Lines 12.09 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 1
dl 52
loc 430
rs 4.5599
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A addScripts() 0 10 2
A addStyles() 0 10 2
A addStylesDirectly() 8 17 4
A addScriptsDirectly() 8 17 4
A removeStyles() 12 12 3
A removeScripts() 12 12 3
B getScripts() 0 24 6
B getStyles() 4 34 7
A scriptToHtml() 0 4 1
A styleToHtml() 0 4 1
A renderHeader() 0 8 1
A renderFooter() 0 6 1
B getScriptItem() 5 45 10
A getFallbackScript() 0 8 1
B itemToHtml() 3 32 8
A getBuildVersion() 0 4 2
A getHtmlBuilder() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Assets often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Assets, and based on these observations, apply Extract Interface, too.

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
        if (!is_array($assets)) {
82
            $assets = [$assets];
83
        }
84
85
        $this->scripts = array_merge($this->scripts, $assets);
86
87
        return $this;
88
    }
89
90
    /**
91
     * Add Css to current module.
92
     *
93
     * @param  array $assets
94
     * @return $this
95
     */
96
    public function addStyles($assets)
97
    {
98
        if (!is_array($assets)) {
99
            $assets = [$assets];
100
        }
101
102
        $this->styles = array_merge($this->styles, $assets);
103
104
        return $this;
105
    }
106
107
    /**
108
     * Add styles directly.
109
     *
110
     * @param  array|string $assets
111
     * @return $this
112
     */
113
    public function addStylesDirectly($assets)
114
    {
115
        if (!is_array($assets)) {
116
            $assets = [$assets];
117
        }
118
119 View Code Duplication
        foreach ($assets as &$item) {
0 ignored issues
show
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...
120
            if (!in_array($item, $this->appendedStyles)) {
121
                $this->appendedStyles[] = [
122
                    'src'        => $item,
123
                    'attributes' => [],
124
                ];
125
            }
126
        }
127
128
        return $this;
129
    }
130
131
    /**
132
     * Add scripts directly.
133
     *
134
     * @param  string|array $assets
135
     * @param  string $location
136
     * @return $this
137
     */
138
    public function addScriptsDirectly($assets, $location = self::ASSETS_SCRIPT_POSITION_FOOTER)
139
    {
140
        if (!is_array($assets)) {
141
            $assets = [$assets];
142
        }
143
144 View Code Duplication
        foreach ($assets as &$item) {
0 ignored issues
show
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...
145
            if (!in_array($item, $this->appendedScripts[$location])) {
146
                $this->appendedScripts[$location][] = [
147
                    'src'        => $item,
148
                    'attributes' => [],
149
                ];
150
            }
151
        }
152
153
        return $this;
154
    }
155
156
    /**
157
     * Remove Css to current module.
158
     *
159
     * @param  array $assets
160
     * @return $this
161
     */
162 View Code Duplication
    public function removeStyles($assets)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
163
    {
164
        if (!is_array($assets)) {
165
            $assets = [$assets];
166
        }
167
168
        foreach ($assets as $rem) {
169
            array_forget($this->styles, array_search($rem, $this->styles));
170
        }
171
172
        return $this;
173
    }
174
175
    /**
176
     * Add scripts.
177
     *
178
     * @param  array $assets
179
     * @return $this
180
     */
181 View Code Duplication
    public function removeScripts($assets)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
182
    {
183
        if (!is_array($assets)) {
184
            $assets = [$assets];
185
        }
186
187
        foreach ($assets as $rem) {
188
            array_forget($this->scripts, array_search($rem, $this->scripts));
189
        }
190
191
        return $this;
192
    }
193
194
    /**
195
     * Get All scripts in current module.
196
     *
197
     * @param  string $location `header` or `footer`
198
     * @return array
199
     */
200
    public function getScripts($location = null)
201
    {
202
        $scripts = [];
203
204
        $this->scripts = array_unique($this->scripts);
205
206
        foreach ($this->scripts as $script) {
207
            $configName = 'resources.scripts.' . $script;
208
209
            if (array_has($this->config, $configName)) {
210
                if (!empty($location) && $location !== array_get($this->config, $configName . '.location')) {
211
                    continue; // Skip assets that don't match this location
212
                }
213
214
                $scripts = array_merge($scripts, $this->getScriptItem($location, $configName, $script));
215
            }
216
        }
217
218
        if (isset($this->appendedScripts[$location])) {
219
            $scripts = array_merge($scripts, $this->appendedScripts[$location]);
220
        }
221
222
        return $scripts;
223
    }
224
225
    /**
226
     * Get All CSS in current module.
227
     *
228
     * @param  array $lastStyles Append last CSS to current module
229
     * @return array
230
     */
231
    public function getStyles($lastStyles = [])
232
    {
233
        $styles = [];
234
        if (!empty($lastStyles)) {
235
            $this->styles = array_merge($this->styles, $lastStyles);
236
        }
237
238
        $this->styles = array_unique($this->styles);
239
240
        foreach ($this->styles as $style) {
241
            $configName = 'resources.styles.' . $style;
242
243
            if (array_has($this->config, $configName)) {
244
                $src = array_get($this->config, $configName . '.src.local');
245
246
                $attributes = array_get($this->config, $configName . '.attributes', []);
247
248 View Code Duplication
                if (array_get($this->config, $configName . '.use_cdn') && !$this->config['offline']) {
0 ignored issues
show
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...
249
                    $src = array_get($this->config, $configName . '.src.cdn');
250
251
                    $attributes = [];
252
                }
253
254
                foreach ((array)$src as $s) {
255
                    $styles[] = [
256
                        'src'        => $s,
257
                        'attributes' => $attributes,
258
                    ];
259
                }
260
            }
261
        }
262
263
        return array_merge($styles, $this->appendedStyles);
264
    }
265
266
    /**
267
     * Convert script to html.
268
     *
269
     * @param  string $name
270
     * @return  string|null
271
     */
272
    public function scriptToHtml($name)
273
    {
274
        return $this->itemToHtml($name, 'script');
275
    }
276
277
    /**
278
     * Convert style to html.
279
     *
280
     * @param  string $name
281
     */
282
    public function styleToHtml($name)
283
    {
284
        return $this->itemToHtml($name, 'style');
285
    }
286
287
    /**
288
     * Render assets to header.
289
     *
290
     * @param array $lastStyles
291
     * @return string
292
     * @throws \Throwable
293
     */
294
    public function renderHeader($lastStyles = [])
295
    {
296
        $styles = $this->getStyles($lastStyles);
297
298
        $headScripts = $this->getScripts(self::ASSETS_SCRIPT_POSITION_HEADER);
299
300
        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...
301
    }
302
303
    /**
304
     * Render assets to footer.
305
     *
306
     * @return string
307
     * @throws \Throwable
308
     */
309
    public function renderFooter()
310
    {
311
        $bodyScripts = $this->getScripts(self::ASSETS_SCRIPT_POSITION_FOOTER);
312
313
        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...
314
    }
315
316
    /**
317
     * Get script item.
318
     *
319
     * @param string $location
320
     * @param string $configName
321
     * @param string $script
322
     * @return array
323
     */
324
    protected function getScriptItem($location, $configName, $script)
325
    {
326
        $scripts = [];
327
328
        $src = array_get($this->config, $configName . '.src.local');
329
330
        $cdn = false;
331
332
        $attributes = array_get($this->config, $configName . '.attributes', []);
333
334 View Code Duplication
        if (array_get($this->config, $configName . '.use_cdn') && !$this->config['offline']) {
0 ignored issues
show
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...
335
            $src = array_get($this->config, $configName . '.src.cdn');
336
337
            $cdn = true;
338
339
            $attributes = [];
340
        }
341
342
        if (!is_array($src)) {
343
            $scripts[] = [
344
                'src'        => $src,
345
                'attributes' => $attributes,
346
            ];
347
        } else {
348
            foreach ($src as $s) {
349
                $scripts[] = [
350
                    'src'        => $s,
351
                    'attributes' => $attributes,
352
                ];
353
            }
354
        }
355
356
        if (empty($src) &&
357
            $cdn &&
358
            $location === self::ASSETS_SCRIPT_POSITION_HEADER &&
359
            array_has($this->config, $configName . '.fallback')) {
360
            $scripts[] = $this->getFallbackScript($src, $configName);
361
        }
362
363
        if (array_get($this->config, $configName . '.include_style')) {
364
            $this->addStyles([$script]);
365
        }
366
367
        return $scripts;
368
    }
369
370
    /**
371
     * Fallback to local script if CDN fails.
372
     *
373
     * @param  string $src
374
     * @param  string $configName
375
     * @return array
376
     */
377
    protected function getFallbackScript($src, $configName)
378
    {
379
        return [
380
            'src'         => $src,
381
            'fallback'    => array_get($this->config, $configName . '.fallback'),
382
            'fallbackURL' => array_get($this->config, $configName . '.src.local'),
383
        ];
384
    }
385
386
    /**
387
     * Convert item to html.
388
     *
389
     * @param  string $name
390
     * @param  string $type
391
     * @return null|string
392
     */
393
    protected function itemToHtml($name, $type = 'style')
394
    {
395
        $html = '';
396
397
        if (!in_array($type, ['style', 'script'])) {
398
            return $html;
399
        }
400
401
        $config = 'resources.styles.' . $name;
402
403
        if ($type === 'script') {
404
            $config = 'resources.scripts.' . $name;
405
        }
406
407
        if (array_has($this->config, $config)) {
408
            $src = array_get($this->config, $config . '.src.local');
409
410 View Code Duplication
            if (array_get($this->config, $config . '.use_cdn') && !$this->config['offline']) {
0 ignored issues
show
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...
411
                $src = array_get($this->config, $config . '.src.cdn');
412
            }
413
414
            if (!is_array($src)) {
415
                $src = [$src];
416
            }
417
418
            foreach ($src as $item) {
419
                $html .= $this->{$type}($item, ['class' => 'hidden'])->toHtml();
420
            }
421
        }
422
423
        return $html;
424
    }
425
426
    /**
427
     * @return string
428
     */
429
    public function getBuildVersion()
430
    {
431
        return $this->build = $this->config['enable_version'] ? '?v=' . $this->config['version'] : '';
432
    }
433
434
    /**
435
     * @return HtmlBuilder
436
     */
437
    public function getHtmlBuilder()
438
    {
439
        return $this->htmlBuilder;
440
    }
441
}
442