Passed
Push — master ( 086231...a8f10c )
by Oleg
03:33
created

Micro::initialize()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 21
rs 8.7624
cc 5
eloc 11
nc 4
nop 0
1
<?php /** Micro */
2
3
namespace Micro;
4
5
use Micro\Base\Dispatcher;
6
use Micro\Base\Exception;
7
use Micro\Base\FatalError;
8
use Micro\Base\IDispatcher;
9
use Micro\Base\IInjector;
10
use Micro\Cli\Console;
11
use Micro\Cli\Consoles\DefaultConsoleCommand;
12
use Micro\Mvc\Controllers\IController;
13
use Micro\Resolver\IResolver;
14
use Micro\Web\IOutput;
15
use Micro\Web\IRequest;
16
use Micro\Web\IResponse;
17
use Micro\Web\Response;
18
19
/**
20
 * Micro class file.
21
 *
22
 * Base class for initialize MicroPHP, used as bootstrap framework.
23
 *
24
 * @author Oleg Lunegov <[email protected]>
25
 * @link https://github.com/lugnsk/micro
26
 * @copyright Copyright &copy; 2013 Oleg Lunegov
27
 * @license /LICENSE
28
 * @package micro
29
 * @version 1.0
30
 * @since 1.0
31
 */
32
class Micro
33
{
34
    /** @const string VERSION Version framework */
35
    const VERSION = '1.1';
36
37
    /** @var IInjector $injector */
38
    protected $injector;
39
    /** @var string $appDir */
40
    protected $appDir;
41
    /** @var string $webDir */
42
    protected $webDir;
43
44
    /** @var bool $loaded Micro loaded flag */
45
    private $loaded;
46
    /** @var bool $debug Debug-mode flag */
47
    private $debug = true;
48
    /** @var string $environment Application environment */
49
    private $environment = 'devel';
50
    /** @var float $startTime Time of start framework */
51
    private $startTime;
52
53
54
    /**
55
     * Initialize application
56
     *
57
     * @access public
58
     *
59
     * @param string $environment Application environment: devel , production , test, other
60
     * @param bool $debug Debug-mode flag
61
     *
62
     * @result void
63
     */
64
    public function __construct($environment = 'devel', $debug = true)
65
    {
66
        $this->webDir = getenv('DOCUMENT_ROOT');
67
        $this->environment = (string)$environment;
68
        $this->debug = (bool)$debug;
69
        $this->loaded = false;
70
71
        ini_set('display_errors', (integer)$this->debug);
72
        ini_set('log_errors', (integer)$this->debug);
73
74
        FatalError::register();
75
76
        if ($this->debug) {
77
            ini_set('error_reporting', -1);
78
            $this->startTime = microtime(true);
79
        }
80
    }
81
82
    /**
83
     * Clone application
84
     *
85
     * @access public
86
     *
87
     * @return void
88
     */
89
    public function __clone()
90
    {
91
        if ($this->debug) {
92
            $this->startTime = microtime(true);
93
        }
94
95
        $this->loaded = false;
96
    }
97
98
    /**
99
     * Running application
100
     *
101
     * @access public
102
     *
103
     * @param IRequest $request Request object
104
     *
105
     * @return \Micro\Web\IOutput|\Micro\Web\IResponse
106
     * @throws \Exception
107
     * @throws \Micro\Base\Exception
108
     */
109
    public function run(IRequest $request)
110
    {
111
        try {
112
            return $this->doRun($request);
113
        } catch (\Exception $e) {
114
            if ($this->debug) {
115
                if (($dispatcher = $this->injector->get('dispatcher')) && $dispatcher instanceof IDispatcher) {
116
                    $dispatcher->signal('kernel.exception', ['exception' => $e]);
117
                }
118
119
                throw $e;
120
            }
121
122
            return $this->doException($e);
123
        }
124
    }
125
126
    /**
127
     * Starting ...
128
     *
129
     * @access private
130
     *
131
     * @param IRequest $request
132
     *
133
     * @return \Micro\Web\IResponse|\Micro\Web\IOutput
134
     * @throws \Micro\Base\Exception
135
     */
136
    private function doRun(IRequest $request)
137
    {
138
        if (!$this->loaded) {
139
            $this->initialize();
140
141
            $this->addListener('kernel.kill', function () {
142
                /** @var IRequest $request */
143
                $request = $this->injector->get('request');
144
145
                if ($this->isDebug() && !$request->isCli() && !$request->isAjax()) {
146
                    echo '<div class="debug_timer">', (microtime(true) - $this->getStartTime()), '</div>';
147
                }
148
149
                if (false === $this->loaded) {
150
                    return;
151
                }
152
153
                $this->loaded = false;
154
            });
155
        }
156
157
        $this->injector->addRequirement('request', $request);
158
159
        if (($output = $this->sendSignal('kernel.request', [])) instanceof IResponse) {
160
            return $output;
161
        }
162
163
        /** @var IResolver $resolver */
164
        $resolver = $this->getResolver();
165
        if (($output = $this->sendSignal('kernel.router', ['resolver' => $resolver])) instanceof IResponse) {
166
            return $output;
167
        }
168
169
        /** @var IController|Console $app */
170
        $app = $resolver->getApplication();
171
        if (($output = $this->sendSignal('kernel.controller', ['application' => $app])) instanceof IResponse) {
172
            return $output;
173
        }
174
175
        $output = $app->action((string)$resolver->getAction());
176
        if (!$output instanceof IOutput) {
177
            $response = $this->injector->get('response') ?: new Response;
178
            $response->setBody((string)$output);
179
            $output = $response;
180
        }
181
182
        $this->sendSignal('kernel.response', ['output' => $output]);
183
184
        return $output;
185
    }
186
187
    /**
188
     * Initialization
189
     *
190
     * @access protected
191
     * @return void
192
     */
193
    protected function initialize()
194
    {
195
        $class = $this->getInjectorClass();
196
        if (!$class || !class_exists($class)) {
197
            $class = '\Micro\Base\Injector';
198
        }
199
200
        /** @var IInjector $inject */
201
        $inject = new $class($this->getConfig());
202
        $inject->addRequirement('kernel', $this);
203
204
        if (!$inject->check('dispatcher') || !($inject->get('dispatcher') instanceof IDispatcher)) {
205
            $inject->addRequirement('dispatcher', new Dispatcher);
206
        }
207
208
        /** @var IDispatcher $dispatcher */
209
        $dispatcher = $inject->get('dispatcher');
210
        $dispatcher->signal('kernel.boot', ['injector' => $inject]);
211
212
        $this->loaded = true;
213
    }
214
215
    /**
216
     * Get full class name
217
     * @return string
218
     */
219
    protected function getInjectorClass()
220
    {
221
        return '';
222
    }
223
224
    /**
225
     * Default config path
226
     *
227
     * @return string
228
     */
229
    protected function getConfig()
230
    {
231
        return $this->getAppDir().'/configs/index.php';
232
    }
233
234
    /**
235
     * Get application directory
236
     *
237
     * @return string
238
     */
239
    public function getAppDir()
240
    {
241
        if (!$this->appDir) {
242
            $this->appDir = realpath(dirname((new \ReflectionObject($this))->getFileName()));
243
        }
244
245
        return $this->appDir;
246
    }
247
248
    /**
249
     * Add listener on event
250
     *
251
     * @access public
252
     *
253
     * @param string $listener listener name
254
     * @param \Closure $event ['Object', 'method'] or callable
255
     * @param int|null $prior priority
256
     *
257
     * @return bool
258
     */
259
    protected function addListener($listener, $event, $prior = null)
260
    {
261
        if (!is_string($listener) || !$this->injector) {
262
            return false;
263
        }
264
265
        if (($dispatcher = $this->injector->get('dispatcher')) && $dispatcher instanceof IDispatcher) {
266
            return $dispatcher->addListener($listener, $event, $prior);
267
        }
268
269
        return null;
270
    }
271
272
    /**
273
     * Get status of debug
274
     *
275
     * @access public
276
     *
277
     * @return bool
278
     */
279
    public function isDebug()
280
    {
281
        return $this->debug;
282
    }
283
284
    /**
285
     * Get start time
286
     *
287
     * @access public
288
     *
289
     * @return double
290
     */
291
    public function getStartTime()
292
    {
293
        return $this->startTime;
294
    }
295
296
    /**
297
     * Send signal to dispatcher
298
     *
299
     * @param string $signal
300
     * @param $params
301
     * @return mixed
302
     */
303
    protected function sendSignal($signal, $params)
304
    {
305
        if (($dispatcher = $this->injector->get('dispatcher')) && $dispatcher instanceof IDispatcher) {
306
            return $dispatcher->signal($signal, $params);
307
        }
308
309
        return null;
310
    }
311
312
    /**
313
     * Get resolver
314
     *
315
     * @access protected
316
     *
317
     * @return IResolver
318
     * @throws \Micro\Base\Exception
319
     */
320
    protected function getResolver()
321
    {
322
        /** @var IRequest $request */
323
        $request = $this->injector->get('request');
324
325
        if ($request->isCli()) {
326
            $resolver = $this->injector->get('consoleResolver') ?: '\Micro\Resolver\ConsoleResolver';
327
        } else {
328
            $resolver = $this->injector->get('resolver') ?: '\Micro\Resolver\HMVCResolver';
329
        }
330
331
        if (is_string($resolver) && is_subclass_of($resolver, '\Micro\Resolver\IResolver')) {
332
            $resolver = new $resolver();
333
        }
334
335
        if (!$resolver instanceof IResolver) {
336
            throw new Exception('Resolver is not implement an IResolver');
337
        }
338
339
        return $resolver;
340
    }
341
342
    /**
343
     * Do exception
344
     *
345
     * @access private
346
     *
347
     * @param \Exception $e Exception
348
     *
349
     * @return \Micro\Web\IOutput|\Micro\Web\IResponse
350
     * @throws \Micro\Base\Exception
351
     */
352
    private function doException(\Exception $e)
353
    {
354
        /** @var IRequest $request */
355
        $request = $this->injector->get('request');
356
357
        $output = $request->isCli() ? new DefaultConsoleCommand([]) : new Response();
358
359
        if ($request->isCli()) {
360
            $output->data = '"Error #'.$e->getCode().' - '.$e->getMessage().'"';
361
            $output->execute();
0 ignored issues
show
Bug introduced by
The method execute does only exist in Micro\Cli\Consoles\DefaultConsoleCommand, but not in Micro\Web\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
362
363
            return $output;
364
        }
365
        if (!$this->injector->get('errorController') || !$this->injector->get('errorAction')) {
366
            $output->setBody('Option `errorController` or `errorAction` not configured');
0 ignored issues
show
Bug introduced by
The method setBody does only exist in Micro\Web\Response, but not in Micro\Cli\Consoles\DefaultConsoleCommand.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
367
368
            return $output;
369
        }
370
371
        $request->setPost('error', $e);
372
        $controller = $this->injector->get('errorController');
373
374
        /** @var \Micro\Mvc\Controllers\IController $result */
375
        $result = new $controller(false);
376
        $result = $result->action($this->injector->get('errorAction'));
0 ignored issues
show
Documentation introduced by
$this->injector->get('errorAction') is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
377
        if ($result instanceof IOutput) {
378
            return $result;
379
        }
380
381
        $output->setBody((string)$result);
382
383
        return $output;
384
    }
385
386
    /**
387
     * Get web root directory
388
     *
389
     * @return string
390
     */
391
    public function getWebDir()
392
    {
393
        return $this->webDir;
394
    }
395
396
    /**
397
     * Terminate application
398
     *
399
     * @access public
400
     *
401
     * @return void
402
     */
403
    public function terminate()
404
    {
405
        if (($dispatcher = $this->injector->get('dispatcher')) && $dispatcher instanceof IDispatcher) {
406
            $dispatcher->signal('kernel.kill', []);
407
        }
408
    }
409
410
    /**
411
     * Get character set
412
     *
413
     * @access public
414
     *
415
     * @return string
416
     */
417
    public function getCharset()
418
    {
419
        return 'UTF-8';
420
    }
421
422
    /**
423
     * Get logs directory
424
     *
425
     * @return string
426
     */
427
    public function getLogDir()
428
    {
429
        return $this->getAppDir().'/logs';
430
    }
431
432
    /**
433
     * Get cache directory
434
     *
435
     * @return string
436
     */
437
    public function getCacheDir()
438
    {
439
        return $this->getAppDir().'/cache/'.$this->getEnvironment();
440
    }
441
442
    /**
443
     * Get environment name
444
     *
445
     * @access public
446
     *
447
     * @return string
448
     */
449
    public function getEnvironment()
450
    {
451
        return $this->environment;
452
    }
453
}
454