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 © 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
|
|||
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
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
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
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
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 |
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.