Passed
Push — master ( 66bd01...23d11d )
by Oleg
04:20
created

Micro::terminate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
350
    {
351
        /** @var IRequest $request */
352
        $request = (new RequestInjector)->get();
353
354
        $output = $request->isCli() ? new DefaultConsoleCommand([]) : new Response();
355
356
        if ($request->isCli()) {
357
            $output->data = '"Error #'.$e->getCode().' - '.$e->getMessage().'"';
358
            $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...
359
360
            return $output;
361
        }
362
363
        $errorController = (new Injector)->param('errorController');
364
        $errorAction = (new Injector)->param('errorAction');
365
366
        if (!$errorController || !$errorAction) {
367
            $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...
368
369
            return $output;
370
        }
371
372
        $_POST['error'] = $e;
373
374
        $controller = $errorController;
375
376
        /** @var \Micro\Mvc\Controllers\IController $result */
377
        $result = new $controller(false);
378
        $result = $result->action($errorAction);
379
380
        if ($result instanceof IOutput) {
381
            return $result;
382
        }
383
384
        $output->setBody((string)$result);
385
386
        return $output;
387
    }
388
389
    /**
390
     * Get web root directory
391
     *
392
     * @return string
393
     */
394
    public function getWebDir()
395
    {
396
        return $this->webDir;
397
    }
398
399
    /**
400
     * Terminate application
401
     *
402
     * @access public
403
     *
404
     * @return void
405
     */
406
    public function terminate()
407
    {
408
        try {
409
            (new DispatcherInjector)->get()->signal('kernel.kill', []);
410
        } catch (Exception $e) {
411
            (new Dispatcher)->signal('kernel.kill', []);
412
        }
413
    }
414
415
    /**
416
     * Get character set
417
     *
418
     * @access public
419
     *
420
     * @return string
421
     */
422
    public function getCharset()
423
    {
424
        return 'UTF-8';
425
    }
426
427
    /**
428
     * Get logs directory
429
     *
430
     * @return string
431
     */
432
    public function getLogDir()
433
    {
434
        return $this->getAppDir().'/logs';
435
    }
436
437
    /**
438
     * Get cache directory
439
     *
440
     * @return string
441
     */
442
    public function getCacheDir()
443
    {
444
        return $this->getAppDir().'/cache/'.$this->getEnvironment();
445
    }
446
447
    /**
448
     * Get environment name
449
     *
450
     * @access public
451
     *
452
     * @return string
453
     */
454
    public function getEnvironment()
455
    {
456
        return $this->environment;
457
    }
458
}
459