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\IContainer; |
10
|
|
|
use Micro\Base\IDispatcher; |
11
|
|
|
use Micro\Cli\Console; |
12
|
|
|
use Micro\Cli\Consoles\DefaultConsoleCommand; |
13
|
|
|
use Micro\Mvc\Controllers\IController; |
14
|
|
|
use Micro\Resolver\IResolver; |
15
|
|
|
use Micro\Web\IOutput; |
16
|
|
|
use Micro\Web\IRequest; |
17
|
|
|
use Micro\Web\IResponse; |
18
|
|
|
use Micro\Web\Response; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Micro class file. |
22
|
|
|
* |
23
|
|
|
* Base class for initialize MicroPHP, used as bootstrap framework. |
24
|
|
|
* |
25
|
|
|
* @author Oleg Lunegov <[email protected]> |
26
|
|
|
* @link https://github.com/lugnsk/micro |
27
|
|
|
* @copyright Copyright © 2013 Oleg Lunegov |
28
|
|
|
* @license /LICENSE |
29
|
|
|
* @package micro |
30
|
|
|
* @version 1.0 |
31
|
|
|
* @since 1.0 |
32
|
|
|
*/ |
33
|
|
|
class Micro |
34
|
|
|
{ |
35
|
|
|
/** @const string VERSION Version framework */ |
36
|
|
|
const VERSION = '1.1'; |
37
|
|
|
|
38
|
|
|
/** @var IContainer $container Container is a container for components and options */ |
39
|
|
|
protected $container; |
40
|
|
|
/** @var string $appDir */ |
41
|
|
|
protected $appDir; |
42
|
|
|
/** @var string $webDir */ |
43
|
|
|
protected $webDir; |
44
|
|
|
|
45
|
|
|
/** @var bool $loaded Micro loaded flag */ |
46
|
|
|
private $loaded; |
47
|
|
|
/** @var bool $debug Debug-mode flag */ |
48
|
|
|
private $debug = true; |
49
|
|
|
/** @var string $environment Application environment */ |
50
|
|
|
private $environment = 'devel'; |
51
|
|
|
/** @var float $startTime Time of start framework */ |
52
|
|
|
private $startTime; |
53
|
|
|
|
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Initialize application |
57
|
|
|
* |
58
|
|
|
* @access public |
59
|
|
|
* |
60
|
|
|
* @param string $environment Application environment: devel , production , test, other |
61
|
|
|
* @param bool $debug Debug-mode flag |
62
|
|
|
* |
63
|
|
|
* @result void |
64
|
|
|
*/ |
65
|
|
|
public function __construct($environment = 'devel', $debug = true) |
66
|
|
|
{ |
67
|
|
|
$this->webDir = getenv('DOCUMENT_ROOT'); |
68
|
|
|
$this->environment = (string)$environment; |
69
|
|
|
$this->debug = (bool)$debug; |
70
|
|
|
$this->loaded = false; |
71
|
|
|
|
72
|
|
|
ini_set('display_errors', (integer)$this->debug); |
73
|
|
|
ini_set('log_errors', (integer)$this->debug); |
74
|
|
|
|
75
|
|
|
FatalError::register(); |
76
|
|
|
|
77
|
|
|
if ($this->debug) { |
78
|
|
|
ini_set('error_reporting', -1); |
79
|
|
|
$this->startTime = microtime(true); |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Clone application |
85
|
|
|
* |
86
|
|
|
* @access public |
87
|
|
|
* |
88
|
|
|
* @return void |
89
|
|
|
*/ |
90
|
|
|
public function __clone() |
91
|
|
|
{ |
92
|
|
|
if ($this->debug) { // start new timer |
93
|
|
|
$this->startTime = microtime(true); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$this->loaded = false; // deactivate loaded |
97
|
|
|
$this->container = null; // remove configured container |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Running application |
102
|
|
|
* |
103
|
|
|
* @access public |
104
|
|
|
* |
105
|
|
|
* @param IRequest $request Request object |
106
|
|
|
* |
107
|
|
|
* @return \Micro\Web\IOutput|\Micro\Web\IResponse |
108
|
|
|
* @throws \Exception |
109
|
|
|
* @throws \Micro\Base\Exception |
110
|
|
|
*/ |
111
|
|
|
public function run(IRequest $request) |
112
|
|
|
{ |
113
|
|
|
try { |
114
|
|
|
return $this->doRun($request); |
115
|
|
|
} catch (\Exception $e) { |
116
|
|
View Code Duplication |
if ($this->debug) { |
|
|
|
|
117
|
|
|
if (($dispatcher = $this->container->dispatcher) && $dispatcher instanceof IDispatcher) { |
|
|
|
|
118
|
|
|
$dispatcher->signal('kernel.exception', ['exception' => $e]); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
throw $e; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
return $this->doException($e); |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Starting ... |
130
|
|
|
* |
131
|
|
|
* @access private |
132
|
|
|
* |
133
|
|
|
* @param IRequest $request |
134
|
|
|
* |
135
|
|
|
* @return \Micro\Web\IResponse|\Micro\Web\IOutput |
136
|
|
|
* @throws \Micro\Base\Exception |
137
|
|
|
*/ |
138
|
|
|
private function doRun(IRequest $request) |
139
|
|
|
{ |
140
|
|
|
if (!$this->loaded) { |
141
|
|
|
$this->initializeContainer(); |
142
|
|
|
|
143
|
|
|
$this->addListener('kernel.kill', function(array $params) { |
144
|
|
|
if ( |
145
|
|
|
$params['container']->kernel->isDebug() && |
146
|
|
|
!$params['container']->request->isCli() && |
147
|
|
|
!$params['container']->request->isAjax() |
148
|
|
|
) { |
149
|
|
|
// Add timer into page |
150
|
|
|
echo '<div class=debug_timer>', (microtime(true) - $params['container']->kernel->getStartTime()), '</div>'; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
if (false === $params['container']->kernel->loaded) { |
154
|
|
|
return; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
$params['container']->kernel->container = null; |
158
|
|
|
$params['container']->kernel->loaded = false; |
159
|
|
|
}); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
$this->container->request = $request; |
|
|
|
|
163
|
|
|
if (($output = $this->sendSignal('kernel.request', ['container' => $this->container])) instanceof IResponse) { |
164
|
|
|
return $output; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** @var IResolver $resolver */ |
168
|
|
|
$resolver = $this->getResolver(); |
169
|
|
|
if (($output = $this->sendSignal('kernel.router', ['resolver' => $resolver])) instanceof IResponse) { |
170
|
|
|
return $output; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** @var IController|Console $app */ |
174
|
|
|
$app = $resolver->getApplication(); |
175
|
|
|
if (($output = $this->sendSignal('kernel.controller', ['application' => $app])) instanceof IResponse) { |
176
|
|
|
return $output; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
$output = $app->action((string)$resolver->getAction()); |
180
|
|
|
if (!$output instanceof IOutput) { |
181
|
|
|
$response = $this->container->response ?: new Response; |
|
|
|
|
182
|
|
|
$response->setBody((string)$output); |
183
|
|
|
$output = $response; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
$this->sendSignal('kernel.response', ['output' => $output]); |
187
|
|
|
|
188
|
|
|
return $output; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Initialization container |
193
|
|
|
* |
194
|
|
|
* @access protected |
195
|
|
|
* @return void |
196
|
|
|
*/ |
197
|
|
|
protected function initializeContainer() |
198
|
|
|
{ |
199
|
|
|
$class = $this->getContainerClass(); |
200
|
|
|
if ($class) { |
201
|
|
|
$class = new $class; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
$this->container = ($class instanceof IContainer) ? $class : new Container; |
205
|
|
|
$this->container->kernel = $this; |
|
|
|
|
206
|
|
|
$this->container->load($this->getConfig()); |
207
|
|
|
|
208
|
|
|
if (false === $this->container->dispatcher || !($this->container->dispatcher instanceof IDispatcher)) { |
|
|
|
|
209
|
|
|
$this->container->dispatcher = new Dispatcher; |
|
|
|
|
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
$this->container->dispatcher->signal('kernel.boot', ['container' => $this->container]); |
|
|
|
|
213
|
|
|
|
214
|
|
|
$this->loaded = true; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Get full class name |
219
|
|
|
* @return string |
220
|
|
|
*/ |
221
|
|
|
protected function getContainerClass() |
222
|
|
|
{ |
223
|
|
|
return ''; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Default config path |
228
|
|
|
* |
229
|
|
|
* @return string |
230
|
|
|
*/ |
231
|
|
|
protected function getConfig() |
232
|
|
|
{ |
233
|
|
|
return $this->getAppDir().'/configs/index.php'; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Get application directory |
238
|
|
|
* |
239
|
|
|
* @return string |
240
|
|
|
*/ |
241
|
|
|
public function getAppDir() |
242
|
|
|
{ |
243
|
|
|
if (!$this->appDir) { |
244
|
|
|
$this->appDir = realpath(dirname((new \ReflectionObject($this))->getFileName())); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
return $this->appDir; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Add listener on event |
252
|
|
|
* |
253
|
|
|
* @access public |
254
|
|
|
* |
255
|
|
|
* @param string $listener listener name |
256
|
|
|
* @param \Closure $event ['Object', 'method'] or callable |
257
|
|
|
* @param int|null $prior priority |
258
|
|
|
* |
259
|
|
|
* @return bool |
260
|
|
|
*/ |
261
|
|
|
protected function addListener($listener, $event, $prior = null) |
262
|
|
|
{ |
263
|
|
|
if (!is_string($listener) || !$this->container) { |
264
|
|
|
return false; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
if (($dispatcher = $this->container->dispatcher) && $dispatcher instanceof IDispatcher) { |
|
|
|
|
268
|
|
|
return $dispatcher->addListener($listener, $event, $prior); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
return null; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Send signal to dispatcher |
276
|
|
|
* |
277
|
|
|
* @param string $signal |
278
|
|
|
* @param $params |
279
|
|
|
* @return mixed |
280
|
|
|
*/ |
281
|
|
|
protected function sendSignal($signal, $params) |
282
|
|
|
{ |
283
|
|
|
if (($dispatcher = $this->container->dispatcher) && $dispatcher instanceof IDispatcher) { |
|
|
|
|
284
|
|
|
return $dispatcher->signal($signal, $params); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
return null; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Get resolver |
292
|
|
|
* |
293
|
|
|
* @access protected |
294
|
|
|
* |
295
|
|
|
* @return IResolver |
296
|
|
|
* @throws \Micro\Base\Exception |
297
|
|
|
*/ |
298
|
|
|
protected function getResolver() |
299
|
|
|
{ |
300
|
|
|
if ($this->container->request->isCli()) { |
|
|
|
|
301
|
|
|
$resolver = $this->container->consoleResolver ?: '\Micro\Resolver\ConsoleResolver'; |
|
|
|
|
302
|
|
|
} else { |
303
|
|
|
$resolver = $this->container->resolver ?: '\Micro\Resolver\HMVCResolver'; |
|
|
|
|
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
if (is_string($resolver) && is_subclass_of($resolver, '\Micro\Resolver\IResolver')) { |
307
|
|
|
$resolver = new $resolver($this->container); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
if (!$resolver instanceof IResolver) { |
311
|
|
|
throw new Exception('Resolver is not implement an IResolver'); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
return $resolver; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Do exception |
319
|
|
|
* |
320
|
|
|
* @access private |
321
|
|
|
* |
322
|
|
|
* @param \Exception $e Exception |
323
|
|
|
* |
324
|
|
|
* @return \Micro\Web\IOutput|\Micro\Web\IResponse |
325
|
|
|
* @throws \Micro\Base\Exception |
326
|
|
|
*/ |
327
|
|
|
private function doException(\Exception $e) |
328
|
|
|
{ |
329
|
|
|
$output = $this->container->request->isCli() ? new DefaultConsoleCommand([]) : new Response(); |
|
|
|
|
330
|
|
|
|
331
|
|
|
if ($this->container->request->isCli()) { |
|
|
|
|
332
|
|
|
$output->data = '"Error #'.$e->getCode().' - '.$e->getMessage().'"'; |
333
|
|
|
$output->execute(); |
|
|
|
|
334
|
|
|
|
335
|
|
|
return $output; |
336
|
|
|
} |
337
|
|
|
if (!$this->container->errorController || !$this->container->errorAction) { |
|
|
|
|
338
|
|
|
$output->setBody('Option `errorController` or `errorAction` not configured'); |
|
|
|
|
339
|
|
|
|
340
|
|
|
return $output; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
if (($request = $this->container->request) && $request instanceof IRequest) { |
|
|
|
|
344
|
|
|
$request->setPost('error', $e); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
$controller = $this->container->errorController; |
|
|
|
|
348
|
|
|
|
349
|
|
|
/** @var \Micro\mvc\controllers\IController $result */ |
350
|
|
|
$result = new $controller($this->container, false); |
351
|
|
|
$result = $result->action($this->container->errorAction); |
|
|
|
|
352
|
|
|
if ($result instanceof IOutput) { |
353
|
|
|
return $result; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
$output->setBody((string)$result); |
357
|
|
|
|
358
|
|
|
return $output; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* Get web root directory |
363
|
|
|
* |
364
|
|
|
* @return string |
365
|
|
|
*/ |
366
|
|
|
public function getWebDir() |
367
|
|
|
{ |
368
|
|
|
return $this->webDir; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* Get start time |
373
|
|
|
* |
374
|
|
|
* @access public |
375
|
|
|
* |
376
|
|
|
* @return double |
377
|
|
|
*/ |
378
|
|
|
public function getStartTime() |
379
|
|
|
{ |
380
|
|
|
return $this->startTime; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* Terminate application |
385
|
|
|
* |
386
|
|
|
* @access public |
387
|
|
|
* |
388
|
|
|
* @return void |
389
|
|
|
*/ |
390
|
|
|
public function terminate() |
391
|
|
|
{ |
392
|
|
View Code Duplication |
if (($dispatcher = $this->container->dispatcher) && $dispatcher instanceof IDispatcher) { |
|
|
|
|
393
|
|
|
$dispatcher->signal('kernel.kill', ['container' => $this->container]); |
394
|
|
|
} |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Get status of debug |
399
|
|
|
* |
400
|
|
|
* @access public |
401
|
|
|
* |
402
|
|
|
* @return bool |
403
|
|
|
*/ |
404
|
|
|
public function isDebug() |
405
|
|
|
{ |
406
|
|
|
return $this->debug; |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Get components container |
411
|
|
|
* |
412
|
|
|
* @access public |
413
|
|
|
* |
414
|
|
|
* @return IContainer |
415
|
|
|
*/ |
416
|
|
|
public function getContainer() |
417
|
|
|
{ |
418
|
|
|
return $this->container; |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Get character set |
423
|
|
|
* |
424
|
|
|
* @access public |
425
|
|
|
* |
426
|
|
|
* @return string |
427
|
|
|
*/ |
428
|
|
|
public function getCharset() |
429
|
|
|
{ |
430
|
|
|
return 'UTF-8'; |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
/** |
434
|
|
|
* Get logs directory |
435
|
|
|
* |
436
|
|
|
* @return string |
437
|
|
|
*/ |
438
|
|
|
public function getLogDir() |
439
|
|
|
{ |
440
|
|
|
return $this->getAppDir().'/logs'; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* Get cache directory |
445
|
|
|
* |
446
|
|
|
* @return string |
447
|
|
|
*/ |
448
|
|
|
public function getCacheDir() |
449
|
|
|
{ |
450
|
|
|
return $this->getAppDir().'/cache/'.$this->getEnvironment(); |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* Get environment name |
455
|
|
|
* |
456
|
|
|
* @access public |
457
|
|
|
* |
458
|
|
|
* @return string |
459
|
|
|
*/ |
460
|
|
|
public function getEnvironment() |
461
|
|
|
{ |
462
|
|
|
return $this->environment; |
463
|
|
|
} |
464
|
|
|
} |
465
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.