Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
97.06% covered (success)
97.06%
33 / 34
CRAP
99.47% covered (success)
99.47%
186 / 187
Application
0.00% covered (danger)
0.00%
0 / 1
97.06% covered (success)
97.06%
33 / 34
108
99.47% covered (success)
99.47%
186 / 187
 __construct
100.00% covered (success)
100.00%
1 / 1
13
100.00% covered (success)
100.00%
21 / 21
 bootstrap
100.00% covered (success)
100.00%
1 / 1
23
100.00% covered (success)
100.00%
25 / 25
 init
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 autoloader
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 router
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 services
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 events
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 modules
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 registerRouter
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 registerServices
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 registerEvents
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 registerModules
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 registerAutoloader
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
5 / 5
 module
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 register
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
7 / 7
 unregister
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 isRegistered
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 addRoute
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 addRoutes
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 setService
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 getService
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 removeService
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 on
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 off
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 trigger
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
6 / 6
 run
0.00% covered (danger)
0.00%
0 / 1
7.01
94.44% covered (success)
94.44%
17 / 18
 __set
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
20 / 20
 __get
100.00% covered (success)
100.00%
1 / 1
8
100.00% covered (success)
100.00%
14 / 14
 __isset
100.00% covered (success)
100.00%
1 / 1
8
100.00% covered (success)
100.00%
14 / 14
 __unset
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
20 / 20
 offsetSet
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 offsetGet
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 offsetExists
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 offsetUnset
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2021 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop;
15
16/**
17 * Application class
18 *
19 * @category   Pop
20 * @package    Pop
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2021 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    3.7.0
25 * @property   $config mixed
26 */
27class Application extends AbstractApplication implements \ArrayAccess
28{
29
30    /**
31     * Application router
32     * @var Router\Router
33     */
34    protected $router = null;
35
36    /**
37     * Service locator
38     * @var Service\Locator
39     */
40    protected $services = null;
41
42    /**
43     * Event manager
44     * @var Event\Manager
45     */
46    protected $events = null;
47
48    /**
49     * Module manager
50     * @var Module\Manager
51     */
52    protected $modules = null;
53
54    /**
55     * Autoloader
56     * @var mixed
57     */
58    protected $autoloader = null;
59
60    /**
61     * Constructor
62     *
63     * Instantiate an application object
64     *
65     * Optional parameters are a service locator instance, a router instance,
66     * an event manager instance or a configuration object or array
67     */
68    public function __construct()
69    {
70        $args       = func_get_args();
71        $autoloader = null;
72        $config     = null;
73
74        foreach ($args as $arg) {
75            $class = (is_object($arg)) ? get_class($arg) : '';
76            if ((stripos($class, 'classload') !== false) || (stripos($class, 'autoload') !== false)) {
77                $autoloader = $arg;
78            } else if ($arg instanceof Router\Router) {
79                $this->registerRouter($arg);
80            } else if ($arg instanceof Service\Locator) {
81                $this->registerServices($arg);
82            } else if ($arg instanceof Event\Manager) {
83                $this->registerEvents($arg);
84            } else if ($arg instanceof Module\Manager) {
85                $this->registerModules($arg);
86            } else if (is_array($arg) || ($arg instanceof \ArrayAccess) || ($arg instanceof \ArrayObject)) {
87                $config = $arg;
88            }
89        }
90
91        if (null !== $config) {
92            $this->registerConfig($config);
93        }
94
95        $this->bootstrap($autoloader);
96    }
97
98    /**
99     * Bootstrap the application, creating the required objects if they haven't been created yet
100     * and registering with the autoloader, adding routes, services and events
101     *
102     * @param  mixed $autoloader
103     * @throws Exception
104     * @throws Module\Exception
105     * @throws Service\Exception
106     * @return Application
107     */
108    public function bootstrap($autoloader = null)
109    {
110        if (null !== $autoloader) {
111            $this->registerAutoloader($autoloader);
112        }
113        if (null === $this->router) {
114            $this->registerRouter(new Router\Router());
115        }
116        if (null === $this->services) {
117            $this->registerServices(new Service\Locator());
118        }
119        if (null === $this->events) {
120            $this->registerEvents(new Event\Manager());
121        }
122        if (null === $this->modules) {
123            $this->registerModules(new Module\Manager());
124        }
125
126        // If the autoloader is set and the the application config has a
127        // defined prefix and src, register with the autoloader
128        if ((null !== $this->autoloader) && isset($this->config['prefix']) &&
129            isset($this->config['src']) && file_exists($this->config['src'])) {
130            // Register as PSR-0
131            if (isset($this->config['psr-0']) && ($this->config['psr-0'])) {
132                $this->autoloader->add($this->config['prefix'], $this->config['src']);
133            // Else, default to PSR-4
134            } else {
135                $this->autoloader->addPsr4($this->config['prefix'], $this->config['src']);
136            }
137        }
138
139        // If routes are set in the app config, register them with the application
140        if (isset($this->config['routes']) && (null !== $this->router)) {
141            $this->router->addRoutes($this->config['routes']);
142        }
143
144        // If services are set in the app config, register them with the application
145        if (isset($this->config['services']) && (null !== $this->services)) {
146            foreach ($this->config['services'] as $name => $service) {
147                $this->setService($name, $service);
148            }
149        }
150
151        // If events are set in the app config, register them with the application
152        if (isset($this->config['events']) && (null !== $this->events)) {
153            foreach ($this->config['events'] as $event) {
154                if (isset($event['name']) && isset($event['action'])) {
155                    $this->on($event['name'], $event['action'], ((isset($event['priority'])) ? $event['priority'] : 0));
156                }
157            }
158        }
159
160        return $this;
161    }
162
163    /**
164     * Initialize the application
165     *
166     * @throws Event\Exception
167     * @throws \ReflectionException
168     * @return Application
169     */
170    public function init()
171    {
172        $this->trigger('app.init');
173        return $this;
174    }
175
176    /**
177     * Get the autoloader object
178     *
179     * @return mixed
180     */
181    public function autoloader()
182    {
183        return $this->autoloader;
184    }
185
186    /**
187     * Access the application router
188     *
189     * @return Router\Router
190     */
191    public function router()
192    {
193        return $this->router;
194    }
195
196    /**
197     * Get the service locator
198     *
199     * @return Service\Locator
200     */
201    public function services()
202    {
203        return $this->services;
204    }
205
206    /**
207     * Get the event manager
208     *
209     * @return Event\Manager
210     */
211    public function events()
212    {
213        return $this->events;
214    }
215
216    /**
217     * Access all application module configs
218     *
219     * @return Module\Manager
220     */
221    public function modules()
222    {
223        return $this->modules;
224    }
225
226    /**
227     * Register a new router object with the application
228     *
229     * @param  Router\Router $router
230     * @return Application
231     */
232    public function registerRouter(Router\Router $router)
233    {
234        $this->router = $router;
235        Router\Route::setRouter($router);
236        return $this;
237    }
238
239    /**
240     * Register a new service locator object with the application
241     *
242     * @param  Service\Locator $services
243     * @return Application
244     */
245    public function registerServices(Service\Locator $services)
246    {
247        $this->services = $services;
248        return $this;
249    }
250
251    /**
252     * Register a new event manager object with the application
253     *
254     * @param  Event\Manager $events
255     * @return Application
256     */
257    public function registerEvents(Event\Manager $events)
258    {
259        $this->events = $events;
260        return $this;
261    }
262
263    /**
264     * Register a new module manager object with the application
265     *
266     * @param  Module\Manager $modules
267     * @return Application
268     */
269    public function registerModules(Module\Manager $modules)
270    {
271        $this->modules = $modules;
272        return $this;
273    }
274
275    /**
276     * Register the autoloader object with the application
277     *
278     * @param  mixed $autoloader
279     * @throws Exception
280     * @return Application
281     */
282    public function registerAutoloader($autoloader)
283    {
284        if (!method_exists($autoloader, 'add') || !method_exists($autoloader, 'addPsr4')) {
285            throw new Exception(
286                'Error: The autoloader instance must contain the methods \'add\' and \'addPsr4\', ' .
287                'as with Composer\Autoload\ClassLoader or Pop\Loader\ClassLoader.'
288            );
289        }
290        $this->autoloader = $autoloader;
291        return $this;
292    }
293
294    /**
295     * Access a module object
296     *
297     * @param  string $name
298     * @return Module\ModuleInterface
299     */
300    public function module($name)
301    {
302        return (isset($this->modules[$name])) ? $this->modules[$name] : null;
303    }
304
305    /**
306     * Register a module with the module manager object
307     *
308     * @param  mixed $module
309     * @param  string $name
310     * @throws Module\Exception
311     * @throws Service\Exception
312     * @return Application
313     */
314    public function register($module, $name = null)
315    {
316        if (!($module instanceof Module\ModuleInterface)) {
317            $module = new Module\Module($module, $this);
318        }
319
320        if (null !== $name) {
321            $module->setName($name);
322        }
323
324        if (!$module->isRegistered()) {
325            $module->register($this);
326        }
327
328        return $this;
329    }
330
331    /**
332     * Unregister a module with the module manager object
333     *
334     * @param  string $name
335     * @return Application
336     */
337    public function unregister($name)
338    {
339        unset($this->modules[$name]);
340        return $this;
341    }
342
343    /**
344     * Determine whether a module is registered with the application object
345     *
346     * @param  string $name
347     * @return boolean
348     */
349    public function isRegistered($name)
350    {
351        return $this->modules->isRegistered($name);
352    }
353
354    /**
355     * Add a route
356     *
357     * @param  string $route
358     * @param  mixed  $controller
359     * @return Application
360     */
361    public function addRoute($route, $controller)
362    {
363        $this->router->addRoute($route, $controller);
364        return $this;
365    }
366
367    /**
368     * Add routes
369     *
370     * @param  array $routes
371     * @return Application
372     */
373    public function addRoutes(array $routes)
374    {
375        $this->router->addRoutes($routes);
376        return $this;
377    }
378
379    /**
380     * Set a service
381     *
382     * @param  string $name
383     * @param  mixed $service
384     * @throws Service\Exception
385     * @return Application
386     */
387    public function setService($name, $service)
388    {
389        $this->services->set($name, $service);
390        return $this;
391    }
392
393    /**
394     * Get a service
395     *
396     * @param  string $name
397     * @throws Service\Exception
398     * @return mixed
399     */
400    public function getService($name)
401    {
402        return $this->services->get($name);
403    }
404
405    /**
406     * Remove a service
407     *
408     * @param  string $name
409     * @return Application
410     */
411    public function removeService($name)
412    {
413        $this->services->remove($name);
414        return $this;
415    }
416
417    /**
418     * Attach an event. Default hook-points are:
419     *
420     *   app.init
421     *   app.route.pre
422     *   app.dispatch.pre
423     *   app.dispatch.post
424     *   app.error
425     *
426     * @param  string $name
427     * @param  mixed  $action
428     * @param  int    $priority
429     * @return Application
430     */
431    public function on($name, $action, $priority = 0)
432    {
433        $this->events->on($name, $action, $priority);
434        return $this;
435    }
436
437    /**
438     * Detach an event. Default hook-points are:
439     *
440     *   app.init
441     *   app.route.pre
442     *   app.dispatch.pre
443     *   app.dispatch.post
444     *   app.error
445     *
446     * @param  string $name
447     * @param  mixed  $action
448     * @return Application
449     */
450    public function off($name, $action)
451    {
452        $this->events->off($name, $action);
453        return $this;
454    }
455
456    /**
457     * Trigger an event
458     *
459     * @param  string $name
460     * @param  array $args
461     * @return Application
462     */
463    public function trigger($name, array $args = [])
464    {
465        if (count($args) == 0) {
466            $args = ['application' => $this];
467        } else if (!in_array($this, $args, true)) {
468            $args['application'] = $this;
469        }
470        $this->events->trigger($name, $args);
471        return $this;
472    }
473
474    /**
475     * Run the application.
476     *
477     * @param  boolean $exit
478     * @param  string  $forceRoute
479     * @throws Event\Exception
480     * @throws Router\Exception
481     * @throws \ReflectionException
482     * @return void
483     */
484    public function run($exit = true, $forceRoute = null)
485    {
486        try {
487            $this->init();
488
489            // Trigger any app.route.pre events
490            $this->trigger('app.route.pre');
491
492            if ((null !== $this->router)) {
493                $this->router->route($forceRoute);
494
495                // Trigger any app.dispatch.post events
496                $this->trigger('app.dispatch.pre');
497
498                if ($this->router->hasController()) {
499                    $controller = $this->router->getController();
500                    if ($this->router->getControllerClass() == 'Closure') {
501                        if ($this->router->hasRouteParams()) {
502                            call_user_func_array($controller, $this->router->getRouteParams());
503                        } else {
504                            $controller();
505                        }
506                    } else {
507                        $params = ($this->router->hasRouteParams()) ? $this->router->getRouteParams() : null;
508                        $controller->dispatch($this->router->getAction(), $params);
509                    }
510                } else {
511                    $this->router->noRouteFound($exit);
512                }
513
514                // Trigger any app.dispatch.post events
515                $this->trigger('app.dispatch.post');
516            }
517        } catch (Exception $exception) {
518            // Trigger any app.error events
519            $this->trigger('app.error', ['exception' => $exception]);
520        }
521    }
522
523    /**
524     * Set a pre-designated value in the application object
525     *
526     * @param  string $name
527     * @param  mixed $value
528     * @throws Exception
529     * @return Application
530     */
531    public function __set($name, $value)
532    {
533        switch ($name) {
534            case 'config':
535                $this->registerConfig($value);
536                break;
537            case 'router':
538                $this->registerRouter($value);
539                break;
540            case 'services':
541                $this->registerServices($value);
542                break;
543            case 'events':
544                $this->registerEvents($value);
545                break;
546            case 'modules':
547                $this->registerModules($value);
548                break;
549            case 'autoloader':
550                $this->registerAutoloader($value);
551                break;
552
553        }
554        return $this;
555    }
556
557    /**
558     * Get a pre-designated value from the application object
559     *
560     * @param  string $name
561     * @return mixed
562     */
563    public function __get($name)
564    {
565        switch ($name) {
566            case 'config':
567                return $this->config;
568                break;
569            case 'router':
570                return $this->router;
571                break;
572            case 'services':
573                return $this->services;
574                break;
575            case 'events':
576                return $this->events;
577                break;
578            case 'modules':
579                return $this->modules;
580                break;
581            case 'autoloader':
582                return $this->autoloader;
583                break;
584            default:
585                return null;
586        }
587    }
588
589    /**
590     * Determine if a pre-designated value in the application object exists
591     *
592     * @param  string $name
593     * @return boolean
594     */
595    public function __isset($name)
596    {
597        switch ($name) {
598            case 'config':
599                return (null !== $this->config);
600                break;
601            case 'router':
602                return (null !== $this->router);
603                break;
604            case 'services':
605                return (null !== $this->services);
606                break;
607            case 'events':
608                return (null !== $this->events);
609                break;
610            case 'modules':
611                return (null !== $this->modules);
612                break;
613            case 'autoloader':
614                return (null !== $this->autoloader);
615                break;
616            default:
617                return false;
618        }
619    }
620
621    /**
622     * Unset a pre-designated value in the application object
623     *
624     * @param  string $name
625     * @return Application
626     */
627    public function __unset($name)
628    {
629        switch ($name) {
630            case 'config':
631                $this->config = null;
632                break;
633            case 'router':
634                $this->router = null;
635                break;
636            case 'services':
637                $this->services = null;
638                break;
639            case 'events':
640                $this->events = null;
641                break;
642            case 'modules':
643                $this->modules = null;
644                break;
645            case 'autoloader':
646                $this->autoloader = null;
647                break;
648        }
649
650        return $this;
651    }
652
653    /**
654     * Set a pre-designated value in the application object
655     *
656     * @param  string $offset
657     * @param  mixed $value
658     * @throws Exception
659     * @return Application
660     */
661    public function offsetSet($offset, $value)
662    {
663        return $this->__set($offset, $value);
664    }
665
666    /**
667     * Get a pre-designated value from the application object
668     *
669     * @param  string $offset
670     * @return mixed
671     */
672    public function offsetGet($offset)
673    {
674        return $this->__get($offset);
675    }
676
677    /**
678     * Determine if a pre-designated value in the application object exists
679     *
680     * @param  string $offset
681     * @return boolean
682     */
683    public function offsetExists($offset)
684    {
685        return $this->__isset($offset);
686    }
687
688    /**
689     * Unset a pre-designated value in the application object
690     *
691     * @param  string $offset
692     * @return Application
693     */
694    public function offsetUnset($offset)
695    {
696        return $this->__unset($offset);
697    }
698
699}