Completed
Push — 3.0 ( 5276e1...94e02b )
by Vermeulen
01:25
created

Application::obtainAppSystemList()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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