GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — 3.0 ( ea7fbc...4ac4b7 )
by Vermeulen
02:11
created

Application::obtainCtrlRouterLinkTasks()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 2
nc 1
nop 0
1
<?php
2
3
namespace BFW;
4
5
use \Exception;
6
use \BFW\Helpers\Constants;
7
8
/**
9
 * Application class
10
 * Manage all BFW application
11
 * Load and init components, modules, ...
12
 */
13
class Application
14
{
15
    /**
16
     * @const ERR_MEMCACHED_NOT_CLASS_DEFINED Exception code if memcache(d) is
17
     * enabled but the class to use is not defined.
18
     */
19
    const ERR_MEMCACHED_NOT_CLASS_DEFINED = 1301001;
20
    
21
    /**
22
     * @const ERR_MEMCACHED_CLASS_NOT_FOUND Exception code if the memcache(d)
23
     * class is not found.
24
     */
25
    const ERR_MEMCACHED_CLASS_NOT_FOUND = 1301002;
26
    
27
    /**
28
     * @const ERR_MEMCACHED_NOT_IMPLEMENT_INTERFACE Exception code the
29
     * memcache(d) class not implement the interface.
30
     */
31
    const ERR_MEMCACHED_NOT_IMPLEMENT_INTERFACE = 1301003;
32
    
33
    /**
34
     * @var \BFW\Application|null $instance Application instance (Singleton)
35
     */
36
    protected static $instance = null;
37
38
    /**
39
     * @var string $rootDir Path to the application project directory
40
     */
41
    protected $rootDir = '';
42
43
    /**
44
     * @var \BFW\Config $config Config's instance for BFW
45
     */
46
    protected $config;
47
48
    /**
49
     * @var \BFW\Core\Options $options Option's instance for the core
50
     */
51
    protected $options;
52
53
    /**
54
     * @var \Composer\Autoload\ClassLoader $composerLoader Loader used by
55
     *  composer.
56
     */
57
    protected $composerLoader;
58
59
    /**
60
     * @var array[] $runSteps All steps used for run the application
61
     */
62
    protected $runSteps = [];
63
64
    /**
65
     * @var Object $memcached The class used to connect to memcache(d) server.
66
     * The class name should be declared into config file.
67
     */
68
    protected $memcached;
69
70
    /**
71
     * @var \BFW\Request $request Informations about the http request
72
     */
73
    protected $request;
74
75
    /**
76
     * @var \BFW\ModuleList $moduleList System who manage all modules
77
     */
78
    protected $moduleList;
79
    
80
    /**
81
     * @var \BFW\Core\Errors $errors System who manage personal errors page
82
     */
83
    protected $errors;
84
    
85
    /**
86
     * @var \BFW\Core\Cli $cli Cli system
87
     */
88
    protected $cli;
89
    
90
    /**
91
     * @var \BFW\SubjectList $subjectList System who manage subjects list
92
     */
93
    protected $subjectList;
94
    
95
    /**
96
     * @var \stdClass $ctrlRouterInfos Infos from router for controller system
97
     */
98
    protected $ctrlRouterInfos;
99
    
100
    /**
101
     * @var \BFW\Monolog $monolog Monolog system for bfw debug
102
     */
103
    protected $monolog;
104
105
    /**
106
     * Constructor
107
     * Init output buffering
108
     * Declare run steps
109
     * Set UTF-8 header
110
     * 
111
     * protected for Singleton pattern
112
     */
113
    protected function __construct()
114
    {
115
        //Start the output buffering
116
        ob_start();
117
118
        $this->declareRunSteps();
119
120
        //Defaut http header. Define here add possiblity to override him
121
        header('Content-Type: text/html; charset=utf-8');
122
        
123
        //Default charset to UTF-8. Define here add possiblity to override him
124
        ini_set('default_charset', 'UTF-8');
125
    }
126
127
    /**
128
     * Get the Application instance (Singleton pattern)
129
     * 
130
     * @return \BFW\Application The current instance of this class
131
     */
132 View Code Duplication
    public static function getInstance()
133
    {
134
        if (self::$instance === null) {
135
            $calledClass = get_called_class(); //Autorize extends this class
136
            self::$instance = new $calledClass;
137
        }
138
139
        return self::$instance;
140
    }
141
142
    /**
143
     * Getter to access to cli property
144
     * 
145
     * @return \BFW\Core\Cli
146
     */
147
    public function getCli()
148
    {
149
        return $this->cli;
150
    }
151
152
    /**
153
     * Getter to access to composerLoader property
154
     * 
155
     * @return \Composer\Autoload\ClassLoader The composer class loader
156
     */
157
    public function getComposerLoader()
158
    {
159
        return $this->composerLoader;
160
    }
161
162
    /**
163
     * Getter to access to the config instance
164
     * 
165
     * @return \BFW\Config
166
     */
167
    public function getConfig()
168
    {
169
        return $this->config;
170
    }
171
    
172
    /**
173
     * Getter to access to the errors instance
174
     * 
175
     * @return \BFW\Errors
176
     */
177
    public function getErrors()
178
    {
179
        return $this->errors;
180
    }
181
    
182
    /**
183
     * Getter to access to the ctrlRouterInfos property
184
     * 
185
     * @return null|\stdClass
186
     */
187
    public function getCtrlRouterInfos()
188
    {
189
        return $this->ctrlRouterInfos;
190
    }
191
    
192
    /**
193
     * Getter to access to memcache instance
194
     * 
195
     * @return Object|null
196
     */
197
    public function getMemcached()
198
    {
199
        return $this->memcached;
200
    }
201
    
202
    /**
203
     * Getter to access to moduleList system
204
     * 
205
     * @return \BFW\ModuleList
206
     */
207
    public function getModuleList()
208
    {
209
        return $this->moduleList;
210
    }
211
    
212
    /**
213
     * Getter to access to a module
214
     * 
215
     * @param string $moduleName The module name to access
216
     * 
217
     * @return \BFW\Module
218
     */
219
    public function getModuleForName($moduleName)
220
    {
221
        return $this->moduleList->getModuleForName($moduleName);
222
    }
223
    
224
    /**
225
     * Getter to access to Monolog system
226
     * 
227
     * @return \BFW\Monolog
228
     */
229
    public function getMonolog()
230
    {
231
        return $this->monolog;
232
    }
233
    
234
    /**
235
     * Getter to access to the options system
236
     * 
237
     * @return mixed
238
     */
239
    public function getOptions()
240
    {
241
        return $this->options;
242
    }
243
    
244
    /**
245
     * Getter to access to the Request instance
246
     * 
247
     * @return \BFW\Request
248
     */
249
    public function getRequest()
250
    {
251
        return $this->request;
252
    }
253
    
254
    /**
255
     * Getter to access to the run step array
256
     * 
257
     * @return array
258
     */
259
    public function getRunSteps()
260
    {
261
        return $this->runSteps;
262
    }
263
264
    /**
265
     * Getter to access to the subjects list
266
     * 
267
     * @return \BFW\SubjectList
268
     */
269
    public function getSubjectList()
270
    {
271
        return $this->subjectList;
272
    }
273
    
274
    /**
275
     * Initialize all components
276
     * 
277
     * @param array $options Options passed to application
278
     * 
279
     * @return void
280
     */
281
    public function initSystem($options)
282
    {
283
        $this->initOptions($options);
284
        $this->initConstants();
285
        $this->initComposerLoader();
286
        $this->initSubjectList();
287
        $this->initConfig();
288
        $this->initMonolog();
289
        $this->initRequest();
290
        $this->initSession();
291
        $this->initErrors();
292
        $this->initCli();
293
        $this->initRunTasks();
294
        $this->initModuleList();
295
        
296
        $this->monolog->getLogger()->debug('Framework initializing done.');
297
        
298
        return $this;
299
    }
300
301
    /**
302
     * Initialize options with the class \BFW\Core\Options
303
     * 
304
     * @param array $options The option passed when initialize this class
305
     */
306
    protected function initOptions($options)
307
    {
308
        $defaultOptions = [
309
            'rootDir'    => null,
310
            'vendorDir'  => null,
311
            'runSession' => true
312
        ];
313
314
        $this->options = new \BFW\Core\Options($defaultOptions, $options);
315
        $this->options
316
            ->searchPaths()
317
            ->checkPaths()
318
        ;
319
    }
320
321
    /**
322
     * Initialize all constants used by framework
323
     * Use helper Constants::create to allow override of constants
324
     * 
325
     * @return void
326
     */
327
    protected function initConstants()
328
    {
329
        Constants::create('ROOT_DIR', $this->options->getValue('rootDir'));
330
331
        Constants::create('APP_DIR', ROOT_DIR.'app/');
332
        Constants::create('SRC_DIR', ROOT_DIR.'src/');
333
        Constants::create('WEB_DIR', ROOT_DIR.'web/');
334
335
        Constants::create('CONFIG_DIR', APP_DIR.'config/');
336
        Constants::create('MODULES_DIR', APP_DIR.'modules/');
337
338
        Constants::create('CLI_DIR', SRC_DIR.'cli/');
339
        Constants::create('CTRL_DIR', SRC_DIR.'controllers/');
340
        Constants::create('MODELES_DIR', SRC_DIR.'modeles/');
341
        Constants::create('VIEW_DIR', SRC_DIR.'view/');
342
    }
343
344
    /**
345
     * Initialize composer loader
346
     * Obtain the composerLoader instance
347
     * Call addComposerNamespaces method to add Application namespaces
348
     * 
349
     * @return void
350
     */
351
    protected function initComposerLoader()
352
    {
353
        $this->composerLoader = require(
354
            $this->options->getValue('vendorDir').'autoload.php'
355
        );
356
        $this->addComposerNamespaces();
357
    }
358
    
359
    /**
360
     * Initialize the subjectList object
361
     * 
362
     * @return void
363
     */
364
    protected function initSubjectList()
365
    {
366
        $this->subjectList = new \BFW\SubjectList;
367
    }
368
369
    /**
370
     * Initialize the property config with \BFW\Config instance
371
     * The config class will search all file in "bfw" directory and load files
372
     * 
373
     * @return void
374
     */
375
    protected function initConfig()
376
    {
377
        $this->config = new \BFW\Config('bfw');
378
        $this->config->loadFiles();
379
    }
380
    
381
    /**
382
     * Initialize the property monolog with \BFW\Monolog instance
383
     * 
384
     * @return void
385
     */
386
    protected function initMonolog()
387
    {
388
        $this->monolog = new \BFW\Monolog('bfw', $this->config);
389
        $this->monolog->addAllHandlers('handlers', 'monolog.php');
390
        
391
        $this->monolog->getLogger()->debug(
392
            'Currently during the initialization framework step.'
393
        );
394
    }
395
396
    /**
397
     * Initialize request property with the \BFW\Request class
398
     * 
399
     * @return void
400
     */
401
    protected function initRequest()
402
    {
403
        $this->request = \BFW\Request::getInstance();
404
        $this->request->runDetect();
405
    }
406
407
    /**
408
     * Initiliaze php session if option "runSession" is not (bool) false
409
     * 
410
     * @return void
411
     */
412
    protected function initSession()
413
    {
414
        if ($this->options->getValue('runSession') === false) {
415
            return;
416
        }
417
418
        //Destroy session cookie if browser quit
419
        session_set_cookie_params(0);
420
421
        //Run session
422
        session_start();
423
    }
424
425
    /**
426
     * Initialize errors property with the \BFW\Core\Errors class
427
     * 
428
     * @return void
429
     */
430
    protected function initErrors()
431
    {
432
        $this->errors = new \BFW\Core\Errors;
433
    }
434
435
    /**
436
     * Initialize cli property with the \BFW\Core\Cli class
437
     * 
438
     * @return void
439
     */
440
    protected function initCli()
441
    {
442
        $this->cli = new \BFW\Core\Cli;
443
    }
444
    
445
    /**
446
     * Initialize taskers
447
     * 
448
     * @return void
449
     */
450
    protected function initRunTasks()
451
    {
452
        $stepsToRun = [];
453
        $closureNb  = 0;
454
        
455
        foreach ($this->runSteps as $step) {
456
            if ($step instanceof \Closure) {
457
                $stepName = 'closure_'.$closureNb;
458
                $closureNb++;
459
            } else {
460
                $stepName = $step[1];
461
            }
462
            
463
            //To keep methods to run protected
464
            $stepsToRun[$stepName] = (object) [
465
                'callback' => function() use ($step) {
466
                    $step();
467
                }
468
            ];
469
        }
470
        
471
        $runTasks = new \BFW\RunTasks($stepsToRun, 'BfwApp');
472
        $this->subjectList->addSubject($runTasks, 'ApplicationTasks');
473
    }
474
475
    /**
476
     * Initialize moduleList property with the \BFW\ModuleList class
477
     * 
478
     * @return void
479
     */
480
    protected function initModuleList()
481
    {
482
        $this->moduleList = new \BFW\ModuleList;
483
    }
484
485
    /**
486
     * Add namespaces used by a BFW Application to composer
487
     * 
488
     * @return void
489
     */
490
    protected function addComposerNamespaces()
491
    {
492
        $this->composerLoader->addPsr4('Controller\\', CTRL_DIR);
493
        $this->composerLoader->addPsr4('Modules\\', MODULES_DIR);
494
        $this->composerLoader->addPsr4('Modeles\\', MODELES_DIR);
495
    }
496
497
    /**
498
     * Declare all steps to run the application
499
     * 
500
     * @return void
501
     */
502
    protected function declareRunSteps()
503
    {
504
        $this->runSteps = [
505
            [$this, 'loadMemcached'],
506
            [$this, 'loadAllModules'],
507
            [$this, 'runAllCoreModules'],
508
            [$this, 'runAllAppModules'],
509
            [$this, 'runCliFile'],
510
            [$this, 'initCtrlRouterLink'],
511
            [$this, 'runCtrlRouterLink']
512
        ];
513
    }
514
515
    /**
516
     * Run the application
517
     * 
518
     * @return void
519
     */
520 View Code Duplication
    public function run()
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...
521
    {
522
        $this->monolog->getLogger()->debug('running framework');
523
        
524
        $runTasks = $this->subjectList->getSubjectForName('ApplicationTasks');
525
        
526
        $runTasks->run();
527
        $runTasks->sendNotify('bfw_run_done');
0 ignored issues
show
Bug introduced by
The method sendNotify() does not exist on SplSubject. Did you maybe mean notify()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
528
    }
529
530
    /**
531
     * Connect to memcache(d) server with the class declared in config file
532
     * 
533
     * @return Object
534
     * 
535
     * @throws Exception If memcached is enabled but no class is define. Or if
536
     *  The class declared into the config is not found.
537
     */
538
    protected function loadMemcached()
539
    {
540
        $memcachedConfig = $this->config->getValue('memcached', 'memcached.php');
541
542
        if ($memcachedConfig['enabled'] === false) {
543
            return;
544
        }
545
546
        $class = $memcachedConfig['class'];
547
        if (empty($class)) {
548
            throw new Exception(
549
                'Memcached is active but no class is define',
550
                $this::ERR_MEMCACHED_NOT_CLASS_DEFINED
551
            );
552
        }
553
554
        if (class_exists($class) === false) {
555
            throw new Exception(
556
                'Memcache class '.$class.' not found.',
557
                $this::ERR_MEMCACHED_CLASS_NOT_FOUND
558
            );
559
        }
560
561
        $this->memcached = new $class;
562
        
563
        if (!($this->memcached instanceof \BFW\Memcache\MemcacheInterface)) {
564
            throw new Exception(
565
                'Memcache class '.$class.' not implement the interface.',
566
                $this::ERR_MEMCACHED_NOT_IMPLEMENT_INTERFACE
567
            );
568
        }
569
        
570
        $this->memcached->connectToServers();
571
    }
572
573
    /**
574
     * Read all directories in modules directory and add each module to Modules
575
     * class.
576
     * Generate the load tree.
577
     * Not initialize modules !
578
     * 
579
     * @return void
580
     */
581
    protected function loadAllModules()
582
    {
583
        $listModules = array_diff(scandir(MODULES_DIR), ['.', '..']);
584
585
        foreach ($listModules as $moduleName) {
586
            $modulePath = realpath(MODULES_DIR.$moduleName); //Symlink
587
588
            if (!is_dir($modulePath)) {
589
                continue;
590
            }
591
592
            $this->moduleList->addModule($moduleName);
593
        }
594
595
        $this->moduleList->readNeedMeDependencies();
596
        $this->moduleList->generateTree();
597
    }
598
599
    /**
600
     * Load core modules defined into config bfw file.
601
     * Only module for controller, router, database and template only.
602
     * 
603
     * @return void
604
     */
605
    protected function runAllCoreModules()
606
    {
607
        $moduleList = $this->config->getValue('modules', 'modules.php');
608
        foreach ($moduleList as $moduleInfos) {
609
            $moduleName    = $moduleInfos['name'];
610
            $moduleEnabled = $moduleInfos['enabled'];
611
612
            if (empty($moduleName) || $moduleEnabled === false) {
613
                continue;
614
            }
615
616
            $this->runModule($moduleName);
617
        }
618
    }
619
620
    /**
621
     * Load all modules (except core).
622
     * Get the load tree, read him and load all modules with the order
623
     * declared into the tree.
624
     * 
625
     * @return void
626
     */
627
    protected function runAllAppModules()
628
    {
629
        $tree = $this->moduleList->getLoadTree();
630
631
        foreach ($tree as $firstLine) {
632
            foreach ($firstLine as $secondLine) {
633
                foreach ($secondLine as $moduleName) {
634
                    $this->runModule($moduleName);
635
                }
636
            }
637
        }
638
    }
639
640
    /**
641
     * Load a module
642
     * 
643
     * @param string $moduleName The module's name to load
644
     * 
645
     * @return void
646
     */
647
    protected function runModule($moduleName)
648
    {
649
        $this->subjectList->getSubjectForName('ApplicationTasks')
0 ignored issues
show
Bug introduced by
The method sendNotify() does not exist on SplSubject. Did you maybe mean notify()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
650
            ->sendNotify('BfwApp_run_module_'.$moduleName);
651
        
652
        $this->moduleList->getModuleForName($moduleName)->runModule();
653
    }
654
655
    /**
656
     * Run the cli file if we're in cli mode
657
     * 
658
     * @return void
659
     * 
660
     * @throws Exception If no file is specified or if the file not exist.
661
     */
662
    protected function runCliFile()
663
    {
664
        if (PHP_SAPI !== 'cli') {
665
            return;
666
        }
667
668
        $this->subjectList->getSubjectForName('ApplicationTasks')
0 ignored issues
show
Bug introduced by
The method sendNotify() does not exist on SplSubject. Did you maybe mean notify()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
669
            ->sendNotify('run_cli_file');
670
        
671
        $fileToExec = $this->cli->obtainFileFromArg();
672
        $this->cli->run($fileToExec);
673
    }
674
    
675
    /**
676
     * Create a new observer to controller and router module.
677
     * 
678
     * @return void
679
     */
680
    protected function initCtrlRouterLink()
681
    {
682
        if (PHP_SAPI === 'cli') {
683
            return;
684
        }
685
686
        //Others properties can be dynamically added by modules
687
        $this->ctrlRouterInfos = (object) [
688
            'isFound' => false,
689
            'forWho'  => null,
690
            'target'  => null,
691
            'datas'   => null
692
        ];
693
        
694
        $ctrlRouterTask = new RunTasks(
695
            $this->obtainCtrlRouterLinkTasks(),
696
            'ctrlRouterLink'
697
        );
698
        
699
        $this->subjectList->addSubject($ctrlRouterTask, 'ctrlRouterLink');
700
        
701
        $runTasks = $this->subjectList->getSubjectForName('ApplicationTasks');
702
        $runTasks->sendNotify('bfw_ctrlRouterLink_subject_added');
0 ignored issues
show
Bug introduced by
The method sendNotify() does not exist on SplSubject. Did you maybe mean notify()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
703
    }
704
    
705
    /**
706
     * List all tasks runned by ctrlRouterLink
707
     * 
708
     * @return array
709
     */
710
    protected function obtainCtrlRouterLinkTasks()
711
    {
712
        return [
713
            'searchRoute'     => (object) [
714
                'context' => $this->ctrlRouterInfos
715
            ],
716
            'checkRouteFound' => (object) [
717
                'callback' => function() {
718
                    if ($this->ctrlRouterInfos->isFound === false) {
719
                        http_response_code(404);
720
                    }
721
                }
722
            ],
723
            'execRoute'       => (object) [
724
                'context' => $this->ctrlRouterInfos
725
            ]
726
        ];
727
    }
728
    
729
    /**
730
     * Execute the ctrlRouter task to find the route and the controller.
731
     * If nothing is found (context object), return an 404 error.
732
     * Not executed in cli.
733
     * 
734
     * @return void
735
     */
736
    protected function runCtrlRouterLink()
737
    {
738
        if (PHP_SAPI === 'cli') {
739
            return;
740
        }
741
        
742
        $this->subjectList->getSubjectForName('ctrlRouterLink')->run();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SplSubject as the method run() does only exist in the following implementations of said interface: BFW\RunTasks.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
743
    }
744
}
745