Completed
Push — master ( 929e4f...acab5f )
by Anton
07:41
created

Application   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 420
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 18

Test Coverage

Coverage 80.17%

Importance

Changes 0
Metric Value
dl 0
loc 420
ccs 97
cts 121
cp 0.8017
rs 9.52
c 0
b 0
f 0
wmc 36
lcom 2
cbo 18

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getEnvironment() 0 4 1
A getPath() 0 13 3
A isDebug() 0 4 1
A useLayout() 0 8 2
A init() 0 35 2
A initConfig() 0 25 5
A initRequest() 0 6 1
A initResponse() 0 6 1
A getResponse() 0 4 1
A getRequest() 0 4 1
A initRouter() 0 7 1
A run() 0 6 1
A process() 0 6 1
A preProcess() 0 15 3
A doProcess() 0 30 5
A postProcess() 0 4 1
A dispatch() 0 15 1
A preDispatch() 0 11 1
A doDispatch() 0 5 1
A postDispatch() 0 4 1
A render() 0 4 1
A end() 0 4 1
1
<?php
2
/**
3
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link      https://github.com/bluzphp/framework
7
 */
8
9
declare(strict_types=1);
10
11
namespace Bluz\Application;
12
13
use Bluz\Application\Exception\ApplicationException;
14
use Bluz\Config\ConfigLoader;
15
use Bluz\Http\Exception\ForbiddenException;
16
use Bluz\Http\Exception\NotAcceptableException;
17
use Bluz\Http\Exception\NotAllowedException;
18
use Bluz\Http\Exception\RedirectException;
19
use Bluz\Common;
20
use Bluz\Common\Exception\CommonException;
21
use Bluz\Common\Exception\ComponentException;
22
use Bluz\Controller\Controller;
23
use Bluz\Controller\ControllerException;
24
use Bluz\Proxy\Config;
25
use Bluz\Proxy\Layout;
26
use Bluz\Proxy\Logger;
27
use Bluz\Proxy\Messages;
28
use Bluz\Proxy\Request;
29
use Bluz\Proxy\Response;
30
use Bluz\Proxy\Router;
31
use Bluz\Proxy\Session;
32
use Bluz\Proxy\Translator;
33
use Bluz\Request\RequestFactory;
34
use Bluz\Response\Response as ResponseInstance;
35
use Zend\Diactoros\ServerRequest;
36
37
/**
38
 * Application
39
 *
40
 * @package  Bluz\Application
41
 * @link     https://github.com/bluzphp/framework/wiki/Application
42
 * @author   Anton Shevchuk
43
 * @created  06.07.11 16:25
44
 *
45
 * @method Controller error(\Exception $exception)
46
 * @method mixed forbidden(ForbiddenException $exception)
47
 * @method null redirect(RedirectException $url)
48
 */
49
class Application
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
50
{
51
    use Common\Helper;
52
    use Common\Singleton;
53
54
    /**
55
     * @var string Environment name
56
     */
57
    protected $environment = 'production';
58
59
    /**
60
     * @var string Application path
61
     */
62
    protected $path;
63
64
    /**
65
     * @var bool Debug application flag
66
     */
67
    protected $debugFlag = false;
68
69
    /**
70
     * @var bool Layout usage flag
71
     */
72
    protected $layoutFlag = true;
73
74
    /**
75
     * Get application environment
76
     *
77
     * @return string
78
     */
79 604
    public function getEnvironment(): string
80
    {
81 604
        return $this->environment;
82
    }
83
84
    /**
85
     * Get path to Application
86
     *
87
     * @return string
88
     * @throws \ReflectionException
89
     */
90 604
    public function getPath(): string
91
    {
92 604
        if (!$this->path) {
93 604
            if (\defined('PATH_APPLICATION')) {
94 604
                $this->path = PATH_APPLICATION;
95
            } else {
96
                $reflection = new \ReflectionClass($this);
97
                // 3 level up
98
                $this->path = \dirname($reflection->getFileName(), 3);
99
            }
100
        }
101 604
        return $this->path;
102
    }
103
104
    /**
105
     * Return Debug flag
106
     *
107
     * @return bool
108
     */
109 2
    public function isDebug(): bool
110
    {
111 2
        return $this->debugFlag;
112
    }
113
114
    /**
115
     * Return/setup Layout Flag
116
     *
117
     * @param  bool|null $flag
118
     *
119
     * @return bool
120
     */
121 35
    public function useLayout($flag = null): bool
122
    {
123 35
        if (\is_bool($flag)) {
124 28
            $this->layoutFlag = $flag;
125
        }
126
127 35
        return $this->layoutFlag;
128
    }
129
130
    /**
131
     * Initialize system packages
132
     *
133
     * @param  string $environment
134
     *
135
     * @throws ApplicationException
136
     * @return void
137
     */
138 604
    public function init($environment = 'production'): void
139
    {
140 604
        $this->environment = $environment;
141
142
        try {
143
            // initial default helper path
144 604
            $this->addHelperPath(__DIR__ . '/Helper/');
145
146
            // init Config
147 604
            $this->initConfig();
148
149
            // first log message
150 604
            Logger::info('app:init');
151
152
            // init Session, start inside class (if needed)
153 604
            Session::getInstance();
154
155
            // init Messages
156 604
            Messages::getInstance();
157
158
            // init Translator
159 604
            Translator::getInstance();
160
161
            // init Request
162 604
            $this->initRequest();
163
164
            // init Response
165 604
            $this->initResponse();
166
167
            // init Router
168 604
            $this->initRouter();
169
        } catch (\Exception $e) {
170
            throw new ApplicationException("Application can't be loaded: " . $e->getMessage());
171
        }
172 604
    }
173
174
    /**
175
     * Initial Request instance
176
     *
177
     * @return void
178
     * @throws \Bluz\Config\ConfigException
179
     * @throws \ReflectionException
180
     */
181 604
    protected function initConfig(): void
182
    {
183 604
        $loader = new ConfigLoader();
184 604
        $loader->setPath($this->getPath());
185 604
        $loader->setEnvironment($this->getEnvironment());
186 604
        $loader->load();
187
188 604
        $config = new \Bluz\Config\Config();
189 604
        $config->setFromArray($loader->getConfig());
190
191 604
        Config::setInstance($config);
192
193
        // setup configuration for current environment
194 604
        if ($debug = Config::get('debug')) {
195
            $this->debugFlag = (bool)$debug;
196
        }
197
198
        // initial php settings
199 604
        if ($ini = Config::get('php')) {
200
            foreach ($ini as $key => $value) {
201
                $result = ini_set($key, $value);
202
                Logger::info('app:init:php:' . $key . ':' . ($result ?: '---'));
203
            }
204
        }
205 604
    }
206
207
    /**
208
     * Initial Request instance
209
     *
210
     * @return void
211
     * @throws \InvalidArgumentException
212
     */
213 604
    protected function initRequest(): void
214
    {
215 604
        $request = RequestFactory::fromGlobals();
216
217 604
        Request::setInstance($request);
218 604
    }
219
220
    /**
221
     * Initial Response instance
222
     *
223
     * @return void
224
     */
225 604
    protected function initResponse(): void
226
    {
227 604
        $response = new ResponseInstance();
228
229 604
        Response::setInstance($response);
230 604
    }
231
232
    /**
233
     * Initial Router instance
234
     *
235
     * @return void
236
     */
237 604
    protected function initRouter(): void
238
    {
239 604
        $router = new \Bluz\Router\Router();
240 604
        $router->setOptions(Config::get('router'));
241
242 604
        Router::setInstance($router);
243 604
    }
244
245
    /**
246
     * Get Response instance
247
     *
248
     * @return \Bluz\Response\Response
249
     */
250 3
    public function getResponse(): ResponseInstance
251
    {
252 3
        return Response::getInstance();
253
    }
254
255
    /**
256
     * Get Request instance
257
     *
258
     * @return \Zend\Diactoros\ServerRequest
259
     */
260 1
    public function getRequest(): ServerRequest
261
    {
262 1
        return Request::getInstance();
263
    }
264
265
    /**
266
     * Run application
267
     *
268
     * @return void
269
     * @throws ApplicationException
270
     */
271 1
    public function run(): void
272
    {
273 1
        $this->process();
274 1
        $this->render();
275 1
        $this->end();
276 1
    }
277
278
    /**
279
     * Process application
280
     *
281
     * Note:
282
     * - Why you don't use "X-" prefix for custom headers?
283
     * - Because it deprecated ({@link http://tools.ietf.org/html/rfc6648})
284
     *
285
     * @return void
286
     */
287 9
    public function process(): void
288
    {
289 9
        $this->preProcess();
290 9
        $this->doProcess();
291 9
        $this->postProcess();
292 9
    }
293
294
    /**
295
     * Extension point: pre process
296
     *
297
     * - Router processing
298
     * - Analyse request headers
299
     *
300
     * @return void
301
     */
302 9
    protected function preProcess(): void
303
    {
304 9
        Router::process();
305
306
        // disable Layout for XmlHttpRequests
307 9
        if (Request::isXmlHttpRequest()) {
308 2
            $this->layoutFlag = false;
309
        }
310
311
        // switch to JSON response based on Accept header
312 9
        if (Request::checkAccept([Request::TYPE_HTML, Request::TYPE_JSON]) === Request::TYPE_JSON) {
313 1
            $this->layoutFlag = false;
314 1
            Response::setType('JSON');
315
        }
316 9
    }
317
318
    /**
319
     * Do process
320
     *
321
     * - Dispatch controller
322
     * - Exceptions handling
323
     * - Setup layout
324
     * - Setup response body
325
     *
326
     * @return void
327
     */
328
    protected function doProcess(): void
329
    {
330
        $module = Request::getModule();
331
        $controller = Request::getController();
332
        $params = Request::getParams();
333
334
        try {
335
            // try to dispatch controller
336
            $result = $this->dispatch($module, $controller, $params);
337
        } catch (ForbiddenException $e) {
338
            // dispatch default error controller
339
            $result = $this->forbidden($e);
340
        } catch (RedirectException $e) {
341
            // should return `null` for disable output and setup redirect headers
342
            $result = $this->redirect($e);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->redirect($e) (which targets Bluz\Application\Application::redirect()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
343
        } catch (\Exception $e) {
344
            // dispatch default error controller
345
            $result = $this->error($e);
346
        }
347
348
        // setup layout, if needed
349
        if ($this->useLayout()) {
350
            // render view to layout
351
            // needed for headScript and headStyle helpers
352
            Layout::setContent($result->render());
353
            Response::setBody(Layout::getInstance());
354
        } else {
355
            Response::setBody($result);
356
        }
357
    }
358
359
    /**
360
     * Extension point: post process
361
     *
362
     * @return void
363
     */
364 9
    protected function postProcess(): void
365
    {
366
        // nothing
367 9
    }
368
369
    /**
370
     * Dispatch controller with params
371
     *
372
     * Call dispatch from any \Bluz\Package
373
     *     Application::getInstance()->dispatch($module, $controller, array $params);
374
     *
375
     * @param  string $module
376
     * @param  string $controller
377
     * @param  array  $params
378
     *
379
     * @return Controller
380
     * @throws ComponentException
381
     * @throws CommonException
382
     * @throws ControllerException
383
     * @throws ForbiddenException
384
     * @throws NotAcceptableException
385
     * @throws NotAllowedException
386
     */
387 41
    public function dispatch($module, $controller, array $params = []): Controller
388
    {
389 41
        $instance = new Controller($module, $controller, $params);
390
391 41
        Logger::info("app:dispatch:>>>: $module/$controller");
392 41
        $this->preDispatch($instance);
393
394 40
        Logger::info("app:dispatch:===: $module/$controller");
395 40
        $this->doDispatch($instance);
396
397 38
        Logger::info("app:dispatch:<<<: $module/$controller");
398 38
        $this->postDispatch($instance);
399
400 38
        return $instance;
401
    }
402
403
    /**
404
     * Extension point: pre dispatch
405
     *
406
     * @param  Controller $controller
407
     *
408
     * @return void
409
     */
410 41
    protected function preDispatch($controller): void
411
    {
412
        // check HTTP method
413 41
        $controller->checkHttpMethod();
414
415
        // check ACL privileges
416 40
        $controller->checkPrivilege();
417
418
        // check HTTP Accept header
419 40
        $controller->checkHttpAccept();
420 40
    }
421
422
    /**
423
     * Do dispatch
424
     *
425
     * @param  Controller $controller
426
     *
427
     * @return void
428
     * @throws ComponentException
429
     * @throws ControllerException
430
     */
431 40
    protected function doDispatch($controller): void
432
    {
433
        // run controller
434 40
        $controller->run();
435 38
    }
436
437
    /**
438
     * Extension point: post dispatch
439
     *
440
     * @param  Controller $controller
441
     *
442
     * @return void
443
     */
444 38
    protected function postDispatch($controller): void
0 ignored issues
show
Unused Code introduced by
The parameter $controller is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
445
    {
446
        // nothing by default
447 38
    }
448
449
    /**
450
     * Render, is send Response
451
     *
452
     * @return void
453
     */
454 1
    public function render(): void
455
    {
456 1
        Response::send();
457 1
    }
458
459
    /**
460
     * Extension point: finally method
461
     *
462
     * @return void
463
     */
464 1
    public function end(): void
465
    {
466
        // nothing
467 1
    }
468
}
469