Completed
Push — master ( d49c5b...068681 )
by Ryan
05:52
created

AddonProvider::boot()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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