Completed
Push — master ( 918554...e03db7 )
by Oleg
04:19 queued 36s
created

micro/Micro.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php /** Micro */
2
3
namespace Micro;
4
5
use Micro\base\Container;
6
use Micro\base\Dispatcher;
7
use Micro\Base\Exception;
8
use Micro\Base\FatalError;
9
use Micro\Base\ICommand;
10
use Micro\base\IContainer;
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 IContainer $container Container is a container for components and options */
38
    protected $container;
39
    /** @var string $appDir */
40
    protected $appDir;
41
42
    /** @var bool $loaded Micro loaded flag */
43
    private $loaded;
44
    /** @var bool $debug Debug-mode flag */
45
    private $debug = true;
46
    /** @var string $environment Application environment */
47
    private $environment = 'devel';
48
    /** @var float $startTime Time of start framework */
49
    private $startTime;
50
51
52
    /**
53
     * Initialize application
54
     *
55
     * @access public
56
     *
57
     * @param string $environment Application environment: devel , production , test, other
58
     * @param bool $debug Debug-mode flag
59
     *
60
     * @result void
61
     */
62
    public function __construct($environment = 'devel', $debug = true)
63
    {
64
        $this->environment = (string)$environment;
65
        $this->debug = (bool)$debug;
66
        $this->loaded = false;
67
68
        ini_set('display_errors', (integer)$this->debug);
69
        ini_set('log_errors', (integer)$this->debug);
70
71
        FatalError::register();
72
73
        if ($this->debug) {
74
            ini_set('error_reporting', -1);
75
            $this->startTime = microtime(true);
76
        }
77
    }
78
79
    /**
80
     * Clone application
81
     *
82
     * @access public
83
     *
84
     * @return void
85
     */
86
    public function __clone()
87
    {
88
        if ($this->debug) { // start new timer
89
            $this->startTime = microtime(true);
90
        }
91
92
        $this->loaded = false; // deactivate loaded
93
        $this->container = null; // remove configured container
94
    }
95
96
    /**
97
     * Running application
98
     *
99
     * @access public
100
     *
101
     * @param IRequest $request Request object
102
     *
103
     * @return Response
104
     * @throws \Exception
105
     */
106
    public function run(IRequest $request)
107
    {
108
        try {
109
            return $this->doRun($request);
110
        } catch (\Exception $e) {
111
            if ($this->debug) {
112
                $this->container->dispatcher->signal('kernel.exception', ['exception' => $e]);
113
                throw $e;
114
            }
115
116
            return $this->doException($e);
117
        }
118
    }
119
120
    /**
121
     * Initialization container
122
     *
123
     * @access protected
124
     * @return void
125
     */
126
    protected function initializeContainer()
127
    {
128
        $class = $this->getContainerClass();
129
        if ($class) {
130
            $class = new $class;
131
        }
132
133
        $this->container = ($class instanceof IContainer) ? $class : new Container;
134
        $this->container->kernel = $this;
135
        $this->container->load($this->getConfig());
136
137
        if (false === $this->container->dispatcher) {
138
            $this->container->dispatcher = new Dispatcher;
139
        }
140
141
        $this->container->dispatcher->signal('kernel.boot', ['container' => $this->container]);
142
143
        $this->loaded = true;
144
    }
145
146
    /**
147
     * Get full class name
148
     * @return string
149
     */
150
    protected function getContainerClass()
151
    {
152
        return '';
153
    }
154
155
    /**
156
     * Default config path
157
     *
158
     * @return string
159
     */
160
    protected function getConfig()
161
    {
162
        return $this->getAppDir() . '/configs/index.php';
163
    }
164
165
    /**
166
     * Get application directory
167
     *
168
     * @return string
169
     */
170
    public function getAppDir()
171
    {
172
        if (!$this->appDir) {
173
            $this->appDir = realpath(dirname((new \ReflectionObject($this))->getFileName()));
174
        }
175
176
        return $this->appDir;
177
    }
178
179
    /**
180
     * Add listener on event
181
     *
182
     * @access public
183
     *
184
     * @param string $listener listener name
185
     * @param mixed $event ['Object', 'method'] or callable
186
     * @param int|null $prior priority
187
     *
188
     * @return bool
189
     */
190
    protected function addListener($listener, $event, $prior = null)
191
    {
192
        if (!is_string($listener) || !$this->container) {
193
            return false;
194
        }
195
196
        $this->container->dispatcher->addListener($listener, $event, $prior);
197
198
        return true;
199
    }
200
201
    /**
202
     * Send signal to dispatcher
203
     *
204
     * @param $signal
205
     * @param $params
206
     * @return mixed
207
     */
208
    protected function sendSignal($signal, $params)
209
    {
210
        return $this->container->dispatcher->signal($signal, $params);
211
    }
212
213
    /**
214
     * Get start time
215
     *
216
     * @access public
217
     *
218
     * @return float|null
219
     */
220
    public function getStartTime()
221
    {
222
        return $this->startTime;
223
    }
224
225
    /**
226
     * Starting ...
227
     *
228
     * @access private
229
     *
230
     * @param IRequest $request
231
     *
232
     * @return Web\IResponse|Response|string
233
     * @throws \Micro\Base\Exception
234
     */
235
    private function doRun(IRequest $request)
236
    {
237
        if (!$this->loaded) {
238
            $this->initializeContainer();
239
240
            $this->addListener('kernel.kill', function (array $params) {
241
                if ($params['container']->kernel->isDebug() && !$params['container']->request->isCli()) {
242
                    // Add timer into page
243
                    echo '<div class=debug_timer>', (microtime(true) - $params['container']->kernel->getStartTime()), '</div>';
244
                }
245
246
                if (false === $params['container']->kernel->loaded) {
247
                    return;
248
                }
249
250
                $params['container']->kernel->container = null;
251
                $params['container']->kernel->loaded = false;
252
            });
253
        }
254
255
        $this->container->request = $request;
256
        if ($output = $this->sendSignal('kernel.request', ['container' => $this->container]) instanceof IResponse) {
257
            return $output;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $output; (boolean) is incompatible with the return type documented by Micro\Micro::doRun of type Micro\Web\IResponse|string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
258
        }
259
260
        /** @var IResolver $resolver */
261
        $resolver = $this->getResolver();
262
        if ($output = $this->sendSignal('kernel.router', ['resolver' => $resolver]) instanceof IResponse) {
263
            return $output;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $output; (boolean) is incompatible with the return type documented by Micro\Micro::doRun of type Micro\Web\IResponse|string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
264
        }
265
266
        /** @var IController|ICommand $app */
267
        $app = $resolver->getApplication();
268
        if ($output = $this->sendSignal('kernel.controller', ['application' => $app]) instanceof IResponse) {
269
            return $output;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $output; (boolean) is incompatible with the return type documented by Micro\Micro::doRun of type Micro\Web\IResponse|string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
270
        }
271
272
        $output = $app->action((string)$resolver->getAction());
273
        if (!$output instanceof IOutput) {
274
            $response = $this->container->response ?: new Response;
275
            $response->setBody((string)$output);
276
            $output = $response;
277
        }
278
279
        $this->sendSignal('kernel.response', ['output' => $output]);
280
281
        return $output;
282
    }
283
284
    /**
285
     * Get resolver
286
     *
287
     * @access protected
288
     *
289
     * @return IResolver
290
     * @throws \Micro\Base\Exception
291
     */
292
    protected function getResolver()
293
    {
294
        if ($this->container->request->isCli()) {
295
            $resolver = $this->container->consoleResolver ?: '\Micro\resolver\ConsoleResolver';
296
        } else {
297
            $resolver = $this->container->resolver ?: '\Micro\Resolver\HMVCResolver';
298
        }
299
300
        if (is_string($resolver) && is_subclass_of($resolver, '\Micro\Resolver\IResolver')) {
301
            $resolver = new $resolver($this->container);
302
        }
303
304
        if (!$resolver instanceof IResolver) {
305
            throw new Exception('Resolver is not implement an IResolver');
306
        }
307
308
        return $resolver;
309
    }
310
311
    /**
312
     * Do exception
313
     *
314
     * @access private
315
     *
316
     * @param \Exception $e Exception
317
     *
318
     * @return IOutput
319
     * @throws \Micro\base\Exception
320
     */
321
    private function doException(\Exception $e)
322
    {
323
        $output = $this->container->request->isCli() ? new DefaultConsoleCommand([]) : new Response();
324
325
        if ($this->container->request->isCli()) {
326
            $output->data = '"Error #' . $e->getCode() . ' - ' . $e->getMessage() . '"';
327
            $output->execute();
328
329
            return $output;
330
        }
331
        if (!$this->container->errorController || !$this->container->errorAction) {
332
            $output->setBody('Option `errorController` or `errorAction` not configured');
333
334
            return $output;
335
        }
336
        $this->container->request->setPost('error', $e);
337
338
        $controller = $this->container->errorController;
339
340
        /** @var \Micro\mvc\controllers\IController $result */
341
        $result = new $controller($this->container, false);
342
        $result = $result->action($this->container->errorAction);
343
        if ($result instanceof IOutput) {
344
            return $result;
345
        }
346
347
        $output->setBody((string)$result);
348
349
        return $output;
350
    }
351
352
    /**
353
     * Terminate application
354
     *
355
     * @access public
356
     *
357
     * @return void
358
     */
359
    public function terminate()
360
    {
361
        $this->container->dispatcher->signal('kernel.kill', ['container' => $this->container]);
362
    }
363
364
    /**
365
     * Get status of debug
366
     *
367
     * @access public
368
     *
369
     * @return bool
370
     */
371
    public function isDebug()
372
    {
373
        return $this->debug;
374
    }
375
376
    /**
377
     * Get components container
378
     *
379
     * @access public
380
     *
381
     * @return IContainer
382
     */
383
    public function getContainer()
384
    {
385
        return $this->container;
386
    }
387
388
    /**
389
     * Get character set
390
     *
391
     * @access public
392
     *
393
     * @return string
394
     */
395
    public function getCharset()
396
    {
397
        return 'UTF-8';
398
    }
399
400
    /**
401
     * Get logs directory
402
     *
403
     * @return string
404
     */
405
    public function getLogDir()
406
    {
407
        return $this->getAppDir() . '/logs';
408
    }
409
410
    /**
411
     * Get cache directory
412
     *
413
     * @return string
414
     */
415
    public function getCacheDir()
416
    {
417
        return $this->getAppDir() . '/cache/' . $this->getEnvironment();
418
    }
419
420
    /**
421
     * Get environment name
422
     *
423
     * @access public
424
     *
425
     * @return string
426
     */
427
    public function getEnvironment()
428
    {
429
        return $this->environment;
430
    }
431
}
432