1 <?php
2
3 4 5 6 7 8 9 10 11 12
13
14 namespace Slimore\Mvc;
15
16 use \Slimore\Database\Manager as DB;
17 use \Illuminate\Container\Container as Container;
18 use \Illuminate\Events\Dispatcher as Dispatcher;
19 use \Slimore\Middleware\Exceptions as ExceptionsMiddleware;
20
21 22 23 24 25 26
27
28 class Application extends \Slim\Slim
29 {
30 31 32
33 public $version = '0.1.0';
34
35 36 37
38 protected $defaults = [
39 'path' => '',
40 'baseURL' => '',
41 'timezone' => 'Asia/Shanghai',
42 'templates.path' => '../app/views',
43 'cookies.encrypt' => true,
44 'cookies.secret_key' => 'vadsfas',
45 'modules' => [],
46 'defaultModule' => '',
47 'disabledModules' => '',
48 'template.suffix' => '.php',
49 'x-framework-header' => true,
50 'mvcDirs' => [
51 'controller' => 'controllers',
52 'model' => 'models',
53 'view' => 'views'
54 ],
55 'autoloads' => [],
56 'defaultAutoloads' => [
57 '../app/controllers',
58 '../app/models',
59 ],
60 'db' => []
61 ];
62
63 64 65
66
67 private $loadFiles = [];
68
69 70 71
72 public $path;
73
74 75 76
77 public $baseURL;
78
79 80 81
82 public $db;
83
84 85 86
87 public $startTime;
88
89 90 91
92 public $moduleName = '';
93
94 95 96
97 public $controllerName;
98
99 100 101
102 public $actionName;
103
104 105 106
107 protected $routeInfo;
108
109 110 111 112 113
114
115 public function __construct(array $settings = null)
116 {
117 if (version_compare(phpversion(), '5.4.0', '<'))
118 {
119 throw new \RuntimeException('Slimore require PHP version >= 5.4.0');
120 }
121
122 require __DIR__ . "/../Helper/Functions.php";
123
124 $settings = array_merge ($this->defaults, $settings);
125
126 if ($settings['x-framework-header'])
127 {
128 header('X-Framework-By: Slimore/' . $this->version);
129 }
130
131 parent::__construct($settings);
132
133 date_default_timezone_set($this->config('timezone'));
134
135 $this->init();
136 }
137
138 139 140 141 142
143
144 public function init()
145 {
146 $this->path = $this->config('path');
147 $this->baseURL = $this->config('baseURL');
148
149 $this->errorsHandle();
150 $this->autoloads();
151 }
152
153 154 155 156 157
158
159 public function run()
160 {
161 set_error_handler(array('\Slim\Slim', 'handleErrors'));
162
163
164 if ($this->config('debug')) {
165
166 $this->add(new \Slimore\Middleware\Exceptions());
167 }
168
169
170 $this->middleware[0]->call();
171
172
173 list($status, $headers, $body) = $this->response->finalize();
174
175
176 \Slim\Http\Util::serializeCookies($headers, $this->response->cookies, $this->settings);
177
178
179 if (headers_sent() === false) {
180
181 if (strpos(PHP_SAPI, 'cgi') === 0) {
182 header(sprintf('Status: %s', \Slim\Http\Response::getMessageForCode($status)));
183 } else {
184 header(sprintf('HTTP/%s %s', $this->config('http.version'), \Slim\Http\Response::getMessageForCode($status)));
185 }
186
187
188 foreach ($headers as $name => $value) {
189 $hValues = explode("\n", $value);
190 foreach ($hValues as $hVal) {
191 header("$name: $hVal", false);
192 }
193 }
194 }
195
196
197 if (!$this->request->isHead()) {
198 echo $body;
199 }
200
201 $this->applyHook('slim.after');
202
203 restore_error_handler();
204 }
205
206 207 208 209 210
211
212 protected function errorsHandle()
213 {
214 $debug = $this->config('debug');
215
216 error_reporting(($debug) ? E_ALL : E_ERROR & ~E_NOTICE);
217
218 set_error_handler(function ($type, $message, $file, $line) {
219 throw new \ErrorException($message, 0, $type, $file, $line);
220 });
221
222 set_exception_handler(function (\Exception $e) {
223
224 $exception = new ExceptionsMiddleware;
225
226 $log = $this->getLog();
227 $env = $this->environment();
228 $env['slim.log'] = $log;
229 $env['slim.log']->error($e);
230
231 $this->contentType('text/html');
232 $this->response()->status(500);
233
234 echo $this->response()->body($exception->renderBody($env, $e));
235 });
236
237 register_shutdown_function(function () {
238
239 if ( !is_null($options = error_get_last()) )
240 {
241 $title = 'Slimore Application Error Shutdown';
242
243 $html = html($title, [
244 '<meta http-equiv="X-UA-Compatible" content="IE=edge" />',
245 '<meta name="renderer" content="webkit" />',
246 '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />',
247 style([
248 '*{margin:0;padding:0;}',
249 'html {font-size: 62.5%;}',
250 'html, body {max-width: 100%;width: 100%;}',
251 'body {font-size: 1.4rem;font-family: "Microsoft Yahei", Helvetica, Tahoma, STXihei, arial,verdana,sans-serif;background:#fff;color:#555;}',
252 'img {border: none;}',
253 'pre, pre code {font-family:Consolas, arial,verdana,sans-serif;}',
254 '#layout {padding: 6rem;}',
255 '#main {margin: 0 auto;line-height: 1.5;}',
256 '#main > h1 {font-size: 10rem;margin-bottom: 1rem;}',
257 '#main > h3 {margin-bottom: 2rem; font-size: 1.8rem;}',
258 '#main > h4 {margin-bottom: 1rem; font-size: 1.8rem;}',
259 '#main pre {margin: 1.5rem 0;white-space: pre-wrap;word-wrap: break-word;}',
260 '#main > p {white-space: pre-wrap;word-wrap: break-word;line-height: 1.3;margin-bottom: 0.6rem;}',
261 '#main > p > strong {width: 7rem;display:inline-block;}',
262 '.logo {text-align: left;border-top:1px solid #eee;margin-top: 5rem;padding: 3rem 0 0;color: #ccc;}',
263 '.logo > h1 {font-weight: normal;font-size: 5rem;}',
264 '.logo img {margin-left: -2rem;}',
265 '.trace-line {padding: 0.3rem 0.6rem;margin-left:-0.6rem;-webkit-transition: background-color 300ms ease-out;transition: background-color 300ms ease-out;}',
266 '.trace-line:hover {background:#fffccc;}'
267 ])
268 ], [
269 '<div id="layout">',
270 '<div id="main">',
271 '<h1>:(</h1>',
272 '<h3>' . $title . '</h3>',
273 '<h4>Details :</h4>',
274 '<p><strong>Type: </strong> ' . $options['type'] . '</p>',
275 '<p><strong>Message: </strong> ' . $options['message'] . '</p>',
276 '<p><strong>File: </strong> ' . $options['file'] . '</p>',
277 '<p><strong>Line: </strong> ' . $options['line'] . '</p>',
278 '<div class="logo">',
279 '<h1>Slimore</h1>',
280 '<p>The fully (H)MVC framework, based on the Slim PHP Framwork.</p>',
281 '</div>',
282 '</div>',
283 '</div>'
284 ]);
285
286 echo $html;
287
288 exit;
289 }
290
291 });
292 }
293
294 295 296 297 298 299 300 301
302
303 public function dbConnection(array $configs = null, $name = 'default', $enableQueryLog = true)
304 {
305 $db = new DB();
306
307 $db->addConnection(($configs) ? $configs : $this->config('db'), $name);
308 $db->setEventDispatcher(new Dispatcher(new Container));
309 $db->setAsGlobal();
310 $db->bootEloquent();
311
312 if ($enableQueryLog)
313 {
314 DB::connection()->enableQueryLog();
315 }
316
317 $this->db = $db;
318
319 return $db;
320 }
321
322 323 324 325 326 327 328
329
330 public function moduleNamespace($namespace, callable $callback)
331 {
332 if ( is_callable($callback) )
333 {
334 $callback($namespace, $this);
335 }
336 }
337
338 339 340 341 342 343 344
345
346 public function controller($controller, callable $callback, $namespace = '')
347 {
348 if ( is_callable($callback) )
349 {
350 $callback(ucwords($controller) . 'Controller', $this, $namespace);
351 }
352 }
353
354 355 356 357 358 359
360
361 public function autoRoute($stop = false)
362 {
363 if ($stop)
364 {
365 return $this;
366 }
367
368 $app = self::getInstance();
369
370 $app->get('(/)', 'IndexController:index');
371
372 $app->get('/:action', function($action = 'index') use ($app) {
373
374 if ( !class_exists('IndexController') )
375 {
376 $app->notFound();
377 return ;
378 }
379
380 $this->controllerName = 'index';
381 $this->actionName = $action;
382
383 $controller = new \IndexController;
384
385 if ( !method_exists($controller, $action) )
386 {
387 $app->notFound();
388 return ;
389 }
390
391 $controller->$action();
392 });
393
394 $app->get('/:controller/:action', function($controller = 'index', $action = 'index') use ($app) {
395
396 $this->controllerName = $controller;
397 $this->actionName = $action;
398
399 $controller = ucwords($controller) . 'Controller';
400
401 if ( !class_exists($controller) )
402 {
403 $app->notFound();
404 return ;
405 }
406
407 $controller = new $controller();
408
409 if ( !method_exists($controller, $action) )
410 {
411 $app->notFound();
412 return ;
413 }
414
415 $controller->$action();
416 });
417
418 $app->get('/:module/:controller/:action', function($module, $controller = 'index', $action = 'index') use ($app) {
419 $this->moduleName = $module;
420 $this->controllerName = $controller;
421 $this->actionName = $action;
422
423 $controller = ucwords($module) . '\Controllers\\' . ucwords($controller) . 'Controller';
424
425 if ( !class_exists($controller) )
426 {
427 $app->notFound();
428 return ;
429 }
430
431 $controller = new $controller();
432
433 if ( !method_exists($controller, $action) )
434 {
435 $app->notFound();
436 return ;
437 }
438
439 $controller->$action();
440 });
441 }
442
443 444 445 446 447 448
449
450 protected function __autoload($className)
451 {
452
453
454 foreach ($this->config('defaultAutoloads') as $key => $dir)
455 {
456 $fileName = $dir . DIRECTORY_SEPARATOR . $className . '.php';
457 $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName);
458
459 if (file_exists($fileName) && !class_exists($className))
460 {
461 $this->loadFiles[$className] = $fileName;
462
463 require $fileName;
464 }
465 }
466
467
468
469 $modules = array_merge([], $this->config('modules'));
470
471 foreach ($modules as $key => $module)
472 {
473 $module = ($module == '') ? $module : $module . DIRECTORY_SEPARATOR;
474
475 foreach ($this->config('mvcDirs') as $k => $mvc)
476 {
477 $phpFile = explode("\\", $className);
478 $phpFile = $phpFile[count($phpFile) - 1];
479 $fileName = $this->path . str_replace('\\', DIRECTORY_SEPARATOR, strtolower(str_replace($phpFile, '', $className))). $phpFile . '.php';
480
481 if (file_exists($fileName) && !class_exists($className))
482 {
483 $this->loadFiles[$className] = $fileName;
484
485 require $fileName;
486 }
487 }
488 }
489
490
491
492 foreach ($this->config('autoloads') as $key => $dir)
493 {
494 $fileName = realpath($dir) . DIRECTORY_SEPARATOR . $className . '.php';
495 $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName);
496
497 if (file_exists($fileName) && !class_exists($className))
498 {
499 $this->loadFiles[$className] = $fileName;
500
501 require $fileName;
502 }
503 }
504 }
505
506 507 508 509 510
511
512 protected function autoloads()
513 {
514 spl_autoload_register(__CLASS__ . "::__autoload");
515 }
516
517 518 519 520 521
522
523 public function timeStart()
524 {
525 $now = explode(" ", microtime());
526 $this->startTime = $now[1] + $now[0];
527
528 return $this->startTime;
529 }
530
531 532 533 534 535 536
537
538 public function timeEnd($decimal = 6)
539 {
540 $now = explode(" ", microtime());
541 $end = $now[1] + $now[0];
542 $total = ($end - $this->startTime);
543
544 return number_format($total, $decimal);
545 }
546 }
547