Application::initSystems()   B
last analyzed

Complexity

Conditions 6
Paths 16

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 21
nc 16
nop 1
dl 0
loc 39
rs 8.9617
c 0
b 0
f 0
1
<?php
2
3
namespace BFW;
4
5
use \Exception;
6
use \BFW\Core\AppSystems\SystemInterface;
7
8
/**
9
 * Application class
10
 * Manage all BFW application
11
 * Load and init components, modules, ...
12
 * 
13
 * @method \Composer\Autoload\ClassLoader getComposerLoader()
14
 * @method \BFW\Config getConfig()
15
 * @method null getConstants()
16
 * @method object getCtrlRouterLink()
17
 * @method \BFW\Core\Errors getErrors()
18
 * @method \BFW\Memcached getMemcached()
19
 * @method \BFW\Core\ModuleList getModuleList()
20
 * @method \BFW\Monolog getMonolog()
21
 * @method \BFW\Core\Options getOptions()
22
 * @method \BFW\Request getRequest()
23
 * @method null getSession()
24
 * @method \BFW\Core\SubjectList getSubjectList()
25
 */
26
class Application
27
{
28
    /**
29
     * @const ERR_CALL_UNKNOWN_METHOD Exception code if __call is called with
30
     * an unmanaged method
31
     */
32
    const ERR_CALL_UNKNOWN_METHOD = 1101001;
33
    
34
    /**
35
     * @const ERR_CALL_UNKNOWN_PROPERTY Exception code if __call is called with
36
     * an unmanaged property
37
     */
38
    const ERR_CALL_UNKNOWN_PROPERTY = 1101002;
39
    
40
    /**
41
     * @const ERR_APP_SYSTEM_CLASS_NOT_EXIST Exception code if an appSystem
42
     * class not exist.
43
     */
44
    const ERR_APP_SYSTEM_CLASS_NOT_EXIST = 1101003;
45
    
46
    /**
47
     * @const ERR_APP_SYSTEM_NOT_IMPLEMENT_INTERFACE Exception code if an
48
     * AppSystem not implement \BFW\Core\AppSystems\SystemInterface.
49
     */
50
    const ERR_APP_SYSTEM_NOT_IMPLEMENT_INTERFACE = 1101004;
51
    
52
    
53
    /**
54
     * @var \BFW\Application|null $instance Application instance (Singleton)
55
     */
56
    protected static $instance = null;
57
    
58
    /**
59
     * @var \BFW\Core\AppSystems\SystemInterface[] $appSystemList A list of
60
     * all appSystem
61
     */
62
    protected $appSystemList = [];
63
    
64
    /**
65
     * @var string[] $appSystemsFromMods AppSystems added from modules
66
     */
67
    protected $appSystemsFromMods = [];
68
    
69
    /**
70
     * @var array $declaredOptions All options passed to initSystems method
71
     */
72
    protected $declaredOptions = [];
73
    
74
    /**
75
     * @var \BFW\RunTasks|null All method tu exec during run
76
     */
77
    protected $runTasks;
78
79
    /**
80
     * Constructor
81
     * Init output buffering
82
     * Declare core systems
83
     * Set UTF-8 header
84
     * 
85
     * protected for Singleton pattern
86
     */
87
    protected function __construct()
88
    {
89
        //Start the output buffering
90
        ob_start();
91
92
        //Defaut http header. Define here add possiblity to override him
93
        header('Content-Type: text/html; charset=utf-8');
94
        
95
        //Default charset to UTF-8. Define here add possiblity to override him
96
        ini_set('default_charset', 'UTF-8');
97
    }
98
99
    /**
100
     * Get the Application instance (Singleton pattern)
101
     * 
102
     * @return \BFW\Application The current instance of this class
103
     */
104
    public static function getInstance(): Application
105
    {
106
        if (self::$instance === null) {
107
            $calledClass = get_called_class(); //Autorize extends this class
108
            self::$instance = new $calledClass;
109
        }
110
111
        return self::$instance;
112
    }
113
    
114
    /**
115
     * Getter accessor to property appSystemList
116
     * 
117
     * @return \BFW\Core\AppSystems\SystemInterface[]
118
     */
119
    public function getAppSystemList(): array
120
    {
121
        return $this->appSystemList;
122
    }
123
    
124
    /**
125
     * Getter accessor to property appSystemsFromMods
126
     * 
127
     * @return string[]
128
     */
129
    public function getAppSystemsFromMods(): array
130
    {
131
        return $this->appSystemsFromMods;
132
    }
133
    
134
    /**
135
     * Getter accessor to property appSystemsFromMods
136
     * 
137
     * @param string $name The appSystem name
138
     * @param string $className The appSystem class (with namespace)
139
     * 
140
     * @return \BFW\Application
141
     */
142
    public function addAppSystemsFromMods($name, $className): Application
143
    {
144
        $this->appSystemsFromMods[$name] = $className;
145
        
146
        return $this;
147
    }
148
        
149
    /**
150
     * Getter accessor to property declaredOptions
151
     * 
152
     * @return array
153
     */
154
    public function getDeclaredOptions(): array
155
    {
156
        return $this->declaredOptions;
157
    }
158
    
159
    /**
160
     * Getter accessor to property runTasks
161
     * 
162
     * @return \BFW\RunTasks|null
163
     */
164
    public function getRunTasks()
165
    {
166
        return $this->runTasks;
167
    }
168
    
169
    /**
170
     * PHP Magic method, called when we call an unexisting method
171
     * Only method getXXX are allowed.
172
     * The property should be a key (ucfirst for camelcase) of the array
173
     * coreSystemList.
174
     * Ex: getConfig() or getModuleList()
175
     * The value returned will be the returned value of the __invoke method
176
     * into the core system class called.
177
     * 
178
     * @param string $name The method name
179
     * @param array $arguments The argument passed to the method
180
     * 
181
     * @return mixed
182
     * 
183
     * @throws \Exception If the method is not allowed or if the property
184
     * not exist.
185
     */
186
    public function __call(string $name, array $arguments)
187
    {
188
        $prefix = substr($name, 0, 3);
189
        
190
        if ($prefix !== 'get') {
191
            throw new Exception(
192
                'Unknown method '.$name,
193
                self::ERR_CALL_UNKNOWN_METHOD
194
            );
195
        }
196
        
197
        $property = lcfirst(substr($name, 3));
198
        if (!array_key_exists($property, $this->appSystemList)) {
199
            throw new Exception(
200
                'Unknown property '.$property,
201
                self::ERR_CALL_UNKNOWN_PROPERTY
202
            );
203
        }
204
        
205
        return $this->appSystemList[$property](...$arguments);
206
    }
207
    
208
    /**
209
     * Define the list of coreSystem to init and/or run.
210
     * 
211
     * @return string[]
212
     */
213
    protected function obtainAppSystemList(): array
214
    {
215
        $appSystemNS = '\BFW\Core\AppSystems\\';
216
        
217
        return [
218
            'options'        => $appSystemNS.'Options',
219
            'constants'      => $appSystemNS.'Constants',
220
            'composerLoader' => $appSystemNS.'ComposerLoader',
221
            'subjectList'    => $appSystemNS.'SubjectList',
222
            'config'         => $appSystemNS.'Config',
223
            'monolog'        => $appSystemNS.'Monolog',
224
            'request'        => $appSystemNS.'Request',
225
            'session'        => $appSystemNS.'Session',
226
            'errors'         => $appSystemNS.'Errors',
227
            'memcached'      => $appSystemNS.'Memcached',
228
            'moduleList'     => $appSystemNS.'ModuleList',
229
            'ctrlRouterLink' => $appSystemNS.'CtrlRouterLink'
230
        ];
231
    }
232
    
233
    /**
234
     * Initialize all components
235
     * 
236
     * @param array $options Options passed to application
237
     * 
238
     * @return $this
239
     */
240
    public function initSystems(array $options): self
241
    {
242
        $appSystemList         = $this->obtainAppSystemList();
243
        $this->declaredOptions = $options;
244
        $this->runTasks        = new \BFW\RunTasks([], 'BfwApp');
245
        
246
        foreach ($appSystemList as $name => $className) {
247
            if ($name === 'ctrlRouterLink') {
248
                continue;
249
            }
250
            
251
            $this->initAppSystem($name, $className);
252
            
253
            if ($name === 'subjectList') {
254
                $this->getSubjectList()->addSubject(
255
                    $this->runTasks,
256
                    'ApplicationTasks'
257
                );
258
            }
259
        }
260
        
261
        foreach ($this->appSystemsFromMods as $name => $className) {
262
            $this->initAppSystem($name, $className);
263
        }
264
        
265
        //Because the appSystemList can be overrided
266
        if (array_key_exists('ctrlRouterLink', $appSystemList)) {
267
            $this->initAppSystem(
268
                'ctrlRouterLink',
269
                $appSystemList['ctrlRouterLink']
270
            );
271
        }
272
        
273
        $this->getMonolog()
274
            ->getLogger()
275
            ->debug('Framework initializing done.')
276
        ;
277
        
278
        return $this;
279
    }
280
    
281
    /**
282
     * Instantiate the appSystem declared, only if they implement the interface.
283
     * If the system should be run, we add him to the runTasks object.
284
     * 
285
     * @param string $name The core system name
286
     * @param string $className The core system class name
287
     * instance.
288
     * 
289
     * @return void
290
     */
291
    protected function initAppSystem(string $name, string $className)
292
    {
293
        if (!class_exists($className)) {
294
            throw new Exception(
295
                'The appSystem class '.$className.' not exist.',
296
                self::ERR_APP_SYSTEM_CLASS_NOT_EXIST
297
            );
298
        }
299
        
300
        $appSystem = new $className;
301
        if ($appSystem instanceof SystemInterface === false) {
302
            throw new Exception(
303
                'The appSystem '.$className.' not implement the interface.',
304
                self::ERR_APP_SYSTEM_NOT_IMPLEMENT_INTERFACE
305
            );
306
        }
307
        
308
        $this->appSystemList[$name] = $appSystem;
309
        
310
        if ($appSystem->toRun() === true) {
311
            $this->runTasks->addToRunSteps(
312
                $name,
313
                \BFW\RunTasks::generateStepItem(null, [$appSystem, 'run'])
314
            );
315
        }
316
    }
317
318
    /**
319
     * Run the application
320
     * 
321
     * @return void
322
     */
323
    public function run()
324
    {
325
        $this->getMonolog()->getLogger()->debug('running framework');
326
        
327
        $this->runTasks->run();
328
        $this->runTasks->sendNotify('bfw_run_done');
329
    }
330
}
331