AddonProvider   F
last analyzed

Complexity

Total Complexity 74

Size/Duplication

Total Lines 588
Duplicated Lines 12.59 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 74
loc 588
rs 2.3809
c 2
b 0
f 0
wmc 74
lcom 1
cbo 17

20 Methods

Rating   Name   Duplication   Size   Complexity  
C register() 0 42 8
A boot() 0 10 3
A registerProviders() 0 6 2
A registerCommands() 0 15 2
A bindClasses() 0 6 2
A bindSingletons() 0 6 2
B registerEvents() 10 19 5
C registerRoutes() 32 43 8
B registerFieldsRoutes() 32 62 7
A registerPlugins() 0 17 3
B registerSchedules() 0 22 5
A registerOverrides() 0 12 3
A registerMiddleware() 0 6 2
A registerRouteMiddleware() 0 6 2
A registerAdditionalRoutes() 0 18 4
A routesAreCached() 0 12 3
C registerApi() 0 51 8
A __construct() 0 17 1
A registerFactories() 0 12 2
A bindAliases() 0 6 2

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 AddonProvider 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 AddonProvider, and based on these observations, apply Extract Interface, too.

1
<?php namespace Anomaly\Streams\Platform\Addon;
2
3
use Anomaly\Streams\Platform\Addon\Extension\Extension;
4
use Anomaly\Streams\Platform\Addon\Module\Module;
5
use Anomaly\Streams\Platform\Http\Middleware\MiddlewareCollection;
6
use Anomaly\Streams\Platform\View\Event\RegisteringTwigPlugins;
7
use Anomaly\Streams\Platform\View\ViewMobileOverrides;
8
use Anomaly\Streams\Platform\View\ViewOverrides;
9
use Illuminate\Console\Events\ArtisanStarting;
10
use Illuminate\Console\Scheduling\Schedule;
11
use Illuminate\Contracts\Events\Dispatcher;
12
use Illuminate\Contracts\Foundation\Application;
13
use Illuminate\Database\Eloquent\Factory;
14
use Illuminate\Foundation\AliasLoader;
15
use Illuminate\Routing\Router;
16
17
/**
18
 * Class AddonProvider
19
 *
20
 * @link    http://pyrocms.com/
21
 * @author  PyroCMS, Inc. <[email protected]>
22
 * @author  Ryan Thompson <[email protected]>
23
 */
24
class AddonProvider
25
{
26
27
    /**
28
     * The cached services.
29
     *
30
     * @var array
31
     */
32
    protected $cached = [];
33
34
    /**
35
     * The registered providers.
36
     *
37
     * @var array
38
     */
39
    protected $providers = [];
40
41
    /**
42
     * The router instance.
43
     *
44
     * @var Router
45
     */
46
    protected $router;
47
48
    /**
49
     * The event dispatcher.
50
     *
51
     * @var Dispatcher
52
     */
53
    protected $events;
54
55
    /**
56
     * The factory manager.
57
     *
58
     * @var Factory
59
     */
60
    protected $factory;
61
62
    /**
63
     * The scheduler instance.
64
     *
65
     * @var Schedule
66
     */
67
    protected $schedule;
68
69
    /**
70
     * The application container.
71
     *
72
     * @var Application
73
     */
74
    protected $application;
75
76
    /**
77
     * The middleware collection.
78
     *
79
     * @var MiddlewareCollection
80
     */
81
    protected $middlewares;
82
83
    /**
84
     * The view overrides.
85
     *
86
     * @var ViewOverrides
87
     */
88
    protected $viewOverrides;
89
90
    /**
91
     * The mobile view overrides.
92
     *
93
     * @var ViewMobileOverrides
94
     */
95
    protected $viewMobileOverrides;
96
97
    /**
98
     * Create a new AddonProvider instance.
99
     *
100
     * @param Router               $router
101
     * @param Dispatcher           $events
102
     * @param Schedule             $schedule
103
     * @param Application          $application
104
     * @param ViewOverrides        $viewOverrides
105
     * @param MiddlewareCollection $middlewares
106
     * @param ViewMobileOverrides  $viewMobileOverrides
107
     */
108
    public function __construct(
109
        Router $router,
110
        Dispatcher $events,
111
        Schedule $schedule,
112
        Application $application,
113
        ViewOverrides $viewOverrides,
114
        MiddlewareCollection $middlewares,
115
        ViewMobileOverrides $viewMobileOverrides
116
    ) {
117
        $this->router              = $router;
118
        $this->events              = $events;
119
        $this->schedule            = $schedule;
120
        $this->application         = $application;
121
        $this->middlewares         = $middlewares;
122
        $this->viewOverrides       = $viewOverrides;
123
        $this->viewMobileOverrides = $viewMobileOverrides;
124
    }
125
126
    /**
127
     * Register the service provider for an addon.
128
     *
129
     * @param Addon $addon
130
     */
131
    public function register(Addon $addon)
132
    {
133
        if ($addon instanceof Module && !$addon->isEnabled() && $addon->getSlug() !== 'installer') {
134
            return;
135
        }
136
137
        if ($addon instanceof Extension && !$addon->isEnabled()) {
138
            return;
139
        }
140
141
        $provider = $addon->getServiceProvider();
142
143
        if (!class_exists($provider)) {
144
            return;
145
        }
146
147
        $this->providers[] = $provider = $addon->newServiceProvider();
148
149
        $this->bindAliases($provider);
150
        $this->bindClasses($provider);
151
        $this->bindSingletons($provider);
152
153
        $this->registerRoutes($provider, $addon);
154
        $this->registerOverrides($provider, $addon);
155
        $this->registerApi($provider, $addon);
156
157
        $this->registerEvents($provider);
158
        $this->registerPlugins($provider);
159
        $this->registerCommands($provider);
160
        $this->registerSchedules($provider);
161
        $this->registerMiddleware($provider);
162
        $this->registerRouteMiddleware($provider);
163
164
        $this->registerFactories($addon);
165
166
        if (method_exists($provider, 'register')) {
167
            $this->application->call([$provider, 'register'], ['provider' => $this]);
168
        }
169
170
        // Call other providers last.
171
        $this->registerProviders($provider);
172
    }
173
174
    /**
175
     * Boot the service providers.
176
     */
177
    public function boot()
178
    {
179
        foreach ($this->providers as $provider) {
180
            if (method_exists($provider, 'boot')) {
181
                $this->application->call([$provider, 'boot']);
182
            }
183
184
            $this->registerAdditionalRoutes($provider);
185
        }
186
    }
187
188
    /**
189
     * Register the addon providers.
190
     *
191
     * @param AddonServiceProvider $provider
192
     */
193
    protected function registerProviders(AddonServiceProvider $provider)
194
    {
195
        foreach ($provider->getProviders() as $provider) {
196
            $this->application->register($provider);
197
        }
198
    }
199
200
    /**
201
     * Register the addon commands.
202
     *
203
     * @param AddonServiceProvider $provider
204
     */
205
    protected function registerCommands(AddonServiceProvider $provider)
206
    {
207
        if ($commands = $provider->getCommands()) {
208
209
            // To register the commands with Artisan, we will grab each of the arguments
210
            // passed into the method and listen for Artisan "start" event which will
211
            // give us the Artisan console instance which we will give commands to.
212
            $this->events->listen(
213
                'Illuminate\Console\Events\ArtisanStarting',
214
                function (ArtisanStarting $event) use ($commands) {
215
                    $event->artisan->resolveCommands($commands);
216
                }
217
            );
218
        }
219
    }
220
221
    /**
222
     * Register the addon commands.
223
     *
224
     * @param Addon $addon
225
     */
226
    protected function registerFactories(Addon $addon)
227
    {
228
        /**
229
         * @todo Move this back into a
230
         *       dependency for 3.4
231
         *       causes issues with
232
         *       3.2 conversions.
233
         */
234
        if (is_dir($factories = $addon->getPath('factories'))) {
235
            app(Factory::class)->load($factories);
236
        }
237
    }
238
239
    /**
240
     * Bind class aliases.
241
     *
242
     * @param AddonServiceProvider $provider
243
     */
244
    protected function bindAliases(AddonServiceProvider $provider)
245
    {
246
        if ($aliases = $provider->getAliases()) {
247
            AliasLoader::getInstance($aliases)->register();
248
        }
249
    }
250
251
    /**
252
     * Bind addon classes.
253
     *
254
     * @param AddonServiceProvider $provider
255
     */
256
    protected function bindClasses(AddonServiceProvider $provider)
257
    {
258
        foreach ($provider->getBindings() as $abstract => $concrete) {
259
            $this->application->bind($abstract, $concrete);
260
        }
261
    }
262
263
    /**
264
     * Bind addon singletons.
265
     *
266
     * @param AddonServiceProvider $provider
267
     */
268
    protected function bindSingletons(AddonServiceProvider $provider)
269
    {
270
        foreach ($provider->getSingletons() as $abstract => $concrete) {
271
            $this->application->singleton($abstract, $concrete);
272
        }
273
    }
274
275
    /**
276
     * Register the addon events.
277
     *
278
     * @param AddonServiceProvider $provider
279
     */
280
    protected function registerEvents(AddonServiceProvider $provider)
281
    {
282
        if (!$listen = $provider->getListeners()) {
283
            return;
284
        }
285
286
        foreach ($listen as $event => $listeners) {
287 View Code Duplication
            foreach ($listeners as $key => $listener) {
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...
288
                if (is_integer($listener)) {
289
                    $priority = $listener;
290
                    $listener = $key;              
291
                } else {
292
                    $priority = 0;
293
                }
294
295
                $this->events->listen($event, $listener, $priority);
296
            }
297
        }
298
    }
299
300
    /**
301
     * Register the addon routes.
302
     *
303
     * @param AddonServiceProvider $provider
304
     * @param Addon                $addon
305
     */
306
    protected function registerRoutes(AddonServiceProvider $provider, Addon $addon)
307
    {
308
        if ($this->routesAreCached()) {
309
            return;
310
        }
311
312
        if (!$routes = $provider->getRoutes()) {
313
            return;
314
        }
315
316 View Code Duplication
        foreach ($routes as $uri => $route) {
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...
317
318
            /*
319
             * If the route definition is an
320
             * not an array then let's make it one.
321
             * Array type routes give us more control
322
             * and allow us to pass information in the
323
             * request's route action array.
324
             */
325
            if (!is_array($route)) {
326
                $route = [
327
                    'uses' => $route,
328
                ];
329
            }
330
331
            $verb        = array_pull($route, 'verb', 'any');
332
            $middleware  = array_pull($route, 'middleware', []);
333
            $constraints = array_pull($route, 'constraints', []);
334
335
            array_set($route, 'streams::addon', $addon->getNamespace());
336
337
            if (is_string($route['uses']) && !str_contains($route['uses'], '@')) {
338
                $this->router->resource($uri, $route['uses']);
339
            } else {
340
341
                $route = $this->router->{$verb}($uri, $route)->where($constraints);
342
343
                if ($middleware) {
344
                    call_user_func_array([$route, 'middleware'], (array)$middleware);
345
                }
346
            }
347
        }
348
    }
349
350
    /**
351
     * Register the addon routes.
352
     *
353
     * @param AddonServiceProvider $provider
354
     * @param Addon                $addon
355
     */
356
    protected function registerApi(AddonServiceProvider $provider, Addon $addon)
357
    {
358
        if ($this->routesAreCached()) {
359
            return;
360
        }
361
362
        if (!$routes = $provider->getApi()) {
363
            return;
364
        }
365
366
        $this->router->group(
367
            [
368
                'middleware' => 'auth:api',
369
            ],
370
            function (Router $router) use ($routes, $addon) {
371
372
                foreach ($routes as $uri => $route) {
373
374
                    /*
375
                     * If the route definition is an
376
                     * not an array then let's make it one.
377
                     * Array type routes give us more control
378
                     * and allow us to pass information in the
379
                     * request's route action array.
380
                     */
381
                    if (!is_array($route)) {
382
                        $route = [
383
                            'uses' => $route,
384
                        ];
385
                    }
386
387
                    $verb        = array_pull($route, 'verb', 'any');
388
                    $middleware  = array_pull($route, 'middleware', []);
389
                    $constraints = array_pull($route, 'constraints', []);
390
391
                    array_set($route, 'streams::addon', $addon->getNamespace());
392
393
                    if (is_string($route['uses']) && !str_contains($route['uses'], '@')) {
394
                        $router->resource($uri, $route['uses']);
395
                    } else {
396
397
                        $route = $router->{$verb}($uri, $route)->where($constraints);
398
399
                        if ($middleware) {
400
                            call_user_func_array([$route, 'middleware'], (array)$middleware);
401
                        }
402
                    }
403
                }
404
            }
405
        );
406
    }
407
408
    /**
409
     * Register field routes.
410
     *
411
     * @param Addon $addon
412
     * @param       $controller
413
     * @param null  $segment
414
     */
415
    public function registerFieldsRoutes(Addon $addon, $controller, $segment = null)
416
    {
417
        if ($segment) {
418
            $segment = $addon->getSlug();
419
        }
420
421
        $routes = [
422
            'admin/' . $segment . '/fields'             => [
423
                'as'   => $addon->getNamespace('fields.index'),
424
                'uses' => $controller . '@index',
425
            ],
426
            'admin/' . $segment . '/fields/choose'      => [
427
                'as'   => $addon->getNamespace('fields.choose'),
428
                'uses' => $controller . '@choose',
429
            ],
430
            'admin/' . $segment . '/fields/create'      => [
431
                'as'   => $addon->getNamespace('fields.create'),
432
                'uses' => $controller . '@create',
433
            ],
434
            'admin/' . $segment . '/fields/edit/{id}'   => [
435
                'as'   => $addon->getNamespace('fields.edit'),
436
                'uses' => $controller . '@edit',
437
            ],
438
            'admin/' . $segment . '/fields/change/{id}' => [
439
                'as'   => $addon->getNamespace('fields.change'),
440
                'uses' => $controller . '@change',
441
            ],
442
        ];
443
444 View Code Duplication
        foreach ($routes as $uri => $route) {
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...
445
446
            /*
447
             * If the route definition is an
448
             * not an array then let's make it one.
449
             * Array type routes give us more control
450
             * and allow us to pass information in the
451
             * request's route action array.
452
             */
453
            if (!is_array($route)) {
454
                $route = [
455
                    'uses' => $route,
456
                ];
457
            }
458
459
            $verb        = array_pull($route, 'verb', 'any');
460
            $middleware  = array_pull($route, 'middleware', []);
461
            $constraints = array_pull($route, 'constraints', []);
462
463
            array_set($route, 'streams::addon', $addon->getNamespace());
464
465
            if (is_string($route['uses']) && !str_contains($route['uses'], '@')) {
466
                $this->router->resource($uri, $route['uses']);
467
            } else {
468
469
                $route = $this->router->{$verb}($uri, $route)->where($constraints);
470
471
                if ($middleware) {
472
                    call_user_func_array([$route, 'middleware'], (array)$middleware);
473
                }
474
            }
475
        }
476
    }
477
478
    /**
479
     * Register the addon plugins.
480
     *
481
     * @param AddonServiceProvider $provider
482
     */
483
    protected function registerPlugins(AddonServiceProvider $provider)
484
    {
485
        if (!$plugins = $provider->getPlugins()) {
486
            return;
487
        }
488
489
        $this->events->listen(
490
            'Anomaly\Streams\Platform\View\Event\RegisteringTwigPlugins',
491
            function (RegisteringTwigPlugins $event) use ($plugins) {
492
                $twig = $event->getTwig();
493
494
                foreach ($plugins as $plugin) {
495
                    $twig->addExtension(app($plugin));
496
                }
497
            }
498
        );
499
    }
500
501
    /**
502
     * Register the addon schedules.
503
     *
504
     * @param AddonServiceProvider $provider
505
     */
506
    protected function registerSchedules(AddonServiceProvider $provider)
507
    {
508
        if (!$schedules = $provider->getSchedules()) {
509
            return;
510
        }
511
512
        foreach ($schedules as $frequency => $commands) {
513
            foreach (array_filter($commands) as $command) {
514
                if (str_is('* * * *', $frequency)) {
515
                    $this->schedule->command($command)->cron($frequency);
516
                } else {
517
518
                    $parts = explode('|', $frequency);
519
520
                    $method    = camel_case(array_shift($parts));
521
                    $arguments = explode(',', array_shift($parts));
522
523
                    call_user_func_array([$this->schedule->command($command), $method], $arguments);
524
                }
525
            }
526
        }
527
    }
528
529
    /**
530
     * Register view overrides.
531
     *
532
     * @param AddonServiceProvider $provider
533
     * @param Addon                $addon
534
     */
535
    protected function registerOverrides(AddonServiceProvider $provider, Addon $addon)
536
    {
537
        $overrides = $provider->getOverrides();
538
        $mobiles   = $provider->getMobile();
539
540
        if (!$overrides && !$mobiles) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $overrides of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $mobiles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
541
            return;
542
        }
543
544
        $this->viewOverrides->put($addon->getNamespace(), $overrides);
545
        $this->viewMobileOverrides->put($addon->getNamespace(), $mobiles);
546
    }
547
548
    /**
549
     * Register middleware.
550
     *
551
     * @param AddonServiceProvider $provider
552
     */
553
    protected function registerMiddleware(AddonServiceProvider $provider)
554
    {
555
        foreach ($provider->getMiddleware() as $middleware) {
556
            $this->middlewares->push($middleware);
557
        }
558
    }
559
560
    /**
561
     * Register route middleware.
562
     *
563
     * @param AddonServiceProvider $provider
564
     */
565
    protected function registerRouteMiddleware(AddonServiceProvider $provider)
566
    {
567
        foreach ($provider->getRouteMiddleware() as $name => $class) {
568
            $this->router->middleware($name, $class);
569
        }
570
    }
571
572
    /**
573
     * Register additional routes.
574
     *
575
     * @param AddonServiceProvider $provider
576
     */
577
    protected function registerAdditionalRoutes(AddonServiceProvider $provider)
578
    {
579
        if ($this->routesAreCached()) {
580
            return;
581
        }
582
583
        if (method_exists($provider, 'map')) {
584
            try {
585
                $this->application->call([$provider, 'map']);
586
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
587
                /*
588
                 * If, for whatever reason, this fails let
589
                 * it fail silently. Mapping additional routes
590
                 * could be volatile at certain application states.
591
                 */
592
            }
593
        }
594
    }
595
596
    /**
597
     * Check if routes are cached.
598
     */
599
    protected function routesAreCached()
600
    {
601
        if (in_array('routes', $this->cached)) {
602
            return true;
603
        }
604
605
        if (file_exists(base_path('bootstrap/cache/routes.php'))) {
606
            return $this->cached[] = 'routes';
607
        }
608
609
        return false;
610
    }
611
}
612