1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12: class Onion {
13: 14: 15:
16: private $flash = array(
17: 'prev' => array(),
18: 'next' => array(),
19: 'current' => array()
20: );
21:
22:
23: 24: 25:
26: private $routes = array();
27:
28:
29: 30: 31:
32: private $route;
33:
34:
35: 36: 37:
38: private $request;
39:
40:
41: 42: 43:
44: private $response;
45:
46:
47: 48: 49:
50: public $controller_name;
51:
52:
53: 54: 55:
56: public $action_name;
57:
58:
59: 60: 61:
62: private $controller;
63:
64:
65: 66: 67:
68: private $settings = array();
69:
70:
71: 72: 73:
74: private $arguments = array();
75:
76:
77: 78: 79:
80: private $user;
81:
82:
83: 84: 85:
86: private $not_found = FALSE;
87:
88:
89: 90: 91: 92: 93: 94:
95: public function __construct($settings = array())
96: {
97: ob_start();
98:
99: spl_autoload_register(array($this, 'loader'));
100:
101: $this->request = new Request;
102:
103: $this->settings($settings);
104:
105: if (defined('DEBUG') === FALSE) {
106: define('DEBUG', FALSE);
107: }
108:
109: Log::enable();
110: Log::$templates_dir = ONION_DIR . '/Templates';
111: Log::$logs_dir = TEMP_DIR . '/logs';
112: if (empty($this->settings['system']['dev_mail']) === FALSE) {
113: Log::$email = $this->settings['system']['dev_mail'];
114: }
115:
116: date_default_timezone_set($this->settings['system']['timezone']);
117:
118:
119: $this->response = new Response($this);
120: $this->response->cache_lifetime = $this->settings['system']['cache']['http']['lifetime'];
121: $this->response->public_cache = $this->settings['system']['cache']['http']['public'];
122:
123: if (empty($this->settings['db']) === FALSE) {
124: if (is_array(reset($this->settings['db'])) === TRUE) {
125: foreach ($this->settings['db'] as $connection_name => $connection_settings) {
126: Database::connection($connection_settings, $connection_name);
127: }
128:
129: } else {
130: Database::connection($this->settings['db']);
131: }
132: }
133:
134: $this->user = new Authenticator($this->settings['system']['auth']);
135:
136: Filesystem::init($this->settings['ftp']);
137:
138: $this->start_session();
139:
140: if (isset($_SESSION['flash']) === TRUE) {
141: $this->flash['prev'] = $_SESSION['flash'];
142: }
143: }
144:
145:
146: 147: 148:
149: public function run()
150: {
151: $this->add_route(array(
152: 'uri' => '/assets/(js|css)/(.+)',
153: 'name' => 'assets',
154: 'controller' => 'Assets',
155: 'action' => 'send',
156: 'uri_template' => '/#/type/name'
157: ));
158:
159: if ($this->route === NULL) {
160: $this->add_route(array(
161: 'uri' => '.*',
162: 'name' => '.*',
163: ));
164: }
165:
166: $this->hooks($this->settings['system']['hooks']['controller']['create']['before']);
167:
168: $template = APP_DIR . '/Templates/' . $this->controller_name . '.html';
169: $this->controller_name .= 'Controller';
170:
171: if (method_exists($this->controller_name, $this->action_name) === FALSE) {
172: $this->not_found(NULL, 'action');
173: }
174:
175: $this->hooks(Arrays::get_value($this->settings, array('system', 'hooks', 'controller', 'run', 'before')));
176:
177: $this->controller = new $this->controller_name($this);
178: $this->controller->view = new Template($template);
179:
180: if (method_exists($this->controller, '_before') === TRUE) {
181: call_user_func_array(array($this->controller, '_before'), $this->arguments);
182: }
183:
184: call_user_func_array(array($this->controller, $this->action_name), $this->arguments);
185:
186: if (method_exists($this->controller, '_after') === TRUE) {
187: call_user_func_array(array($this->controller, '_after'), $this->arguments);
188: }
189:
190: $this->controller->view->set_data('app', $this);
191:
192: $flash = array_merge($this->flash['prev'], $this->flash['current']);
193: $this->controller->view->set_data('flash', $flash);
194: $this->controller->view->set_data('host', $this->request->host);
195: $this->controller->view->set_data('base_uri', $this->request->base_uri);
196: $this->controller->view->set_data('base_domain', $this->request->base_domain);
197: $this->controller->view->set_data('base_domain_uri', $this->request->base_domain_uri);
198: $this->controller->view->set_data('subdomain', $this->request->subdomain);
199: $this->controller->view->set_data('uri', $this->request->uri);
200: $this->controller->view->set_data('resource', $this->request->resource);
201:
202: $this->hooks(Arrays::get_value($this->settings, array('system', 'hooks', 'view', 'before', 'render')));
203:
204: $body = $this->controller->view->render();
205: $this->response->set_body($body);
206:
207: $this->hooks(Arrays::get_value($this->settings, array('system', 'hooks', 'view', 'after', 'render')));
208:
209: $_SESSION['flash'] = $this->flash['next'];
210: $this->response->set_body($body);
211: $this->response->send();
212: }
213:
214:
215: 216: 217: 218: 219: 220:
221: private function settings($app_settings)
222: {
223: $cookie_domain = $this->request->host;
224: if (empty($app_settings['system']['multi_domain']) === FALSE) {
225: $$cookie_domain = $this->request->base_domain;
226: }
227:
228: $settings = array(
229: 'system' => array(
230: 'timezone' => 'Europe/Bratislava',
231:
232: 'cache' => array(
233: 'http' => array(
234: 'lifetime' => 3600,
235: 'public' => TRUE,
236: )
237: ),
238:
239: 'session' => array(
240: 'lifetime' => 14400,
241: 'name' => 'onion',
242: ),
243:
244: 'auth' => array(
245: 'cookie' => 'onion_autologin',
246: 'lifetime' => 31536000,
247: 'login' => array('Login', 'form'),
248: 'cookie_domain' => $this->request->host,
249: 'cookie_path' => $this->request->root,
250: ),
251: ),
252: );
253:
254: $this->settings = Arrays::merge($settings, $app_settings);
255: }
256:
257:
258: 259: 260: 261: 262: 263:
264: public function add_route($route)
265: {
266: if (isset($route['method']) === FALSE) {
267: $route['method'] = array('GET', 'POST');
268: }
269:
270: $route['method'] = (array) $route['method'];
271:
272: $name = $route['uri'];
273: if (isset($route['name']) === TRUE) {
274: $name = $route['name'];
275: }
276: $route['name'] = $name;
277:
278: $this->routes[$name] = $route;
279:
280: if ($this->route !== NULL) {
281: return;
282: }
283:
284: $method = $this->request->method;
285: $resource = $this->request->resource;
286:
287: if (empty($resource) === TRUE) {
288: $resource = '/';
289: }
290:
291: if (preg_match('@^' . $route['uri'] . '$@i', $resource, $matches) === 0) {
292: return;
293: }
294:
295: if (Arrays::in_array($method, $route['method']) === FALSE) {
296: return;
297: }
298:
299: if (empty($route['subdomains']) === FALSE
300: AND Arrays::in_array($this->request->subdomain, $route['subdomains']) === FALSE) {
301:
302: return;
303: }
304:
305: $this->route = $route;
306:
307: $arguments = $segments = explode('/', trim($matches[0], '/'));
308:
309: if (isset($route['controller']) === TRUE) {
310: if (is_numeric($route['controller']) === TRUE) {
311: $controller_name = $segments[$route['controller']];
312:
313: } else {
314: $controller_name = $route['controller'];
315: }
316:
317: } elseif (isset($segments[0]) === TRUE) {
318: $controller_name = array_shift($arguments);
319:
320: } else {
321: $controller_name = 'default';
322: }
323:
324: if (empty($controller_name) === TRUE) {
325: $controller_name = 'default';
326: }
327:
328: if (isset($route['action']) === TRUE) {
329: if (is_numeric($route['action']) === TRUE) {
330: $action_name = $segments[$route['action']];
331: array_splice($arguments, $route['action'], 1);
332:
333: } else {
334: $action_name = $route['action'];
335: }
336:
337: } elseif (isset($segments[1]) === TRUE) {
338: $action_name = array_shift($arguments);
339:
340: } else {
341: $action_name = '_default';
342: }
343:
344: if (empty($action_name) === TRUE) {
345: $action_name = '_default';
346: }
347:
348: if (isset($route['controller']) === TRUE
349: AND is_numeric($route['controller']) === TRUE) {
350:
351: array_splice($arguments, $route['controller'], 1);
352: }
353:
354: if (isset($route['uri_template']) === TRUE) {
355: $arguments_template = explode('/', trim($route['uri_template'], '/'));
356:
357: $arguments = array();
358: $i = -1;
359: foreach ($arguments_template as $key => $argument) {
360: $i++;
361:
362: if ($argument === '#') {
363: continue;
364: }
365:
366: if (isset($route['action']) === TRUE
367: AND is_numeric($route['action']) === TRUE
368: AND $i == $route['action']) {
369:
370: continue;
371: }
372:
373: if (isset($segments[$key]) === TRUE) {
374: $arguments[$argument] = $segments[$key];
375: }
376: }
377: }
378:
379: if (isset($route['name']) === TRUE
380: AND isset($route['defaults']) === TRUE) {
381:
382: foreach ($route['defaults'] as $argument => $value) {
383: if ($value === '#') {
384: $route['defaults'][$argument] = $arguments[$argument];
385: }
386: }
387:
388: $current_args = array_keys($arguments);
389: $default_args = array_keys($route['defaults']);
390:
391: sort($current_args);
392: sort($default_args);
393:
394: $uri_data = $this->request->get;
395: $new_uri = $this->create_uri($route['name'], $route['defaults'], $uri_data);
396:
397: if ($current_args !== $default_args) {
398: $this->response->redirect($new_uri, 301);
399: }
400:
401: foreach ($arguments as $argument => $value) {
402: if (strlen($value) === 0) {
403: $this->response->redirect($new_uri, 301);
404: }
405: }
406: }
407:
408: if (empty($this->route['roles']) === FALSE
409: AND $this->user->has_roles($this->route['roles']) === FALSE) {
410:
411: $controller_name = $this->settings['system']['auth']['login'][0];
412: $action_name = $this->settings['system']['auth']['login'][1];
413:
414: } elseif (empty($this->route['permissions']) === FALSE
415: AND $this->user->has_permissions($this->route['permissions']) === FALSE) {
416:
417: $controller_name = $this->settings['system']['auth']['login'][0];
418: $action_name = $this->settings['system']['auth']['login'][1];
419: }
420:
421: $controller_name = ucfirst($controller_name);
422:
423: foreach ($arguments as $key => &$value) {
424: $value = urldecode($value);
425: }
426:
427: $action_name = strtr($action_name, array('.' => '_', '-' => '_'));
428:
429: $this->controller_name = $controller_name;
430: $this->action_name = $action_name;
431: $this->arguments = $arguments;
432: }
433:
434:
435: 436: 437: 438: 439: 440:
441: public function loader($class)
442: {
443: static $paths = array(
444: 'Authenticator' => '/Core/Authenticator.php',
445: 'Controller' => '/Core/Controller.php',
446: 'Database' => '/Core/Database.php',
447: 'DatabaseResult' => '/Core/Database.php',
448: 'Log' => '/Core/Log.php',
449: 'Model' => '/Core/Model.php',
450: 'Request' => '/Core/Request.php',
451: 'Response' => '/Core/Response.php',
452: 'User' => '/Core/User.php',
453:
454: 'Form' => '/UI/Form.php',
455: 'Grid' => '/UI/Grid.php',
456: 'Html' => '/UI/Html.php',
457: 'IconsGrid' => '/UI/IconsGrid.php',
458: 'Paginator' => '/UI/Paginator.php',
459: 'Template' => '/UI/Template.php',
460: 'Widget' => '/UI/Widget.php',
461:
462: 'Arrays' => '/Utils/Arrays.php',
463: 'Assets' => '/Utils/Assets.php',
464: 'AssetsController' => '/Utils/Assets.php',
465: 'Filesystem' => '/Utils/Filesystem.php',
466: 'Image' => '/Utils/Image.php',
467: 'Languages' => '/Utils/Languages.php',
468: 'Mail' => '/Utils/Mail.php',
469: 'Secure' => '/Utils/Secure.php',
470: 'Strings' => '/Utils/Strings.php',
471: 'SWF' => '/Utils/SWF.php',
472: 'Utils' => '/Utils/Utils.php',
473: 'Validate' => '/Utils/Validate.php',
474: 'Visitor' => '/Utils/Visitor.php',
475:
476: 'JSMin' => '/Ext/JSMin/jsmin.php',
477: 'Texy' => '/Ext/Texy/texy.min.php',
478: 'PHPMailer' => '/Ext/PHPMailer/class.phpmailer.php',
479: );
480:
481: if (isset($paths[$class]) === TRUE) {
482: $class_file = ONION_DIR . $paths[$class];
483:
484: } else {
485: $id = substr($class, -5);
486:
487: switch ($id) {
488: case 'oller':
489: $class_file = APP_DIR . '/Controllers/' . $class . '.php';
490: if (file_exists($class_file) === FALSE) {
491: if ($this->not_found === TRUE) {
492: return;
493: }
494:
495: $this->not_found(NULL, 'controller');
496: }
497: break;
498:
499: case 'idget':
500: $class_file = APP_DIR . '/Widgets/' . $class . '.php';
501: break;
502:
503: case 'Model':
504: $class_file = APP_DIR . '/Models/' . $class . '.php';
505: break;
506:
507: default:
508: $class_file = LIBS_DIR . '/' . $class . '.php';
509: }
510: }
511:
512: include($class_file);
513: }
514:
515:
516: 517: 518: 519: 520:
521: private function start_session()
522: {
523: session_cache_limiter(FALSE);
524:
525: $lifetime = $this->settings['system']['session']['lifetime'];
526:
527: $host = $this->request->host;
528: if (empty($this->settings['system']['multi_domain']) === FALSE) {
529: $host = $this->request->base_domain;
530: }
531:
532: $root = $this->request->root;
533:
534: if (empty($root) === TRUE) {
535: $root = '/';
536: }
537:
538: $session_name = $this->settings['system']['session']['name'];
539:
540: session_save_path(TEMP_DIR . '/sessions');
541:
542: session_set_cookie_params($lifetime, $root, $host);
543: session_name($session_name);
544: session_start();
545:
546: if (isset($_COOKIE[$session_name]) === TRUE) {
547: setcookie(
548: $session_name,
549: $_COOKIE[$session_name],
550: time() + $lifetime,
551: $root,
552: $host
553: );
554: }
555:
556: session_regenerate_id();
557: $sid = session_id();
558: session_write_close();
559: session_id($sid);
560: session_start();
561:
562: session_set_cookie_params($lifetime, $root, $host);
563: session_name($session_name);
564: }
565:
566:
567: 568: 569: 570: 571: 572: 573: 574:
575: public function set_flash($message, $type = 'info', $request = 'next')
576: {
577: $this->flash[$request][] = array($type, $message);
578: }
579:
580:
581: 582: 583: 584: 585: 586:
587: public function get_flash($request = 'prev')
588: {
589: return $this->flash[$request];
590: }
591:
592:
593: 594: 595: 596: 597:
598: public function keep_flash()
599: {
600: $this->flash['next'] = array_merge($this->flash['next'], $this->flash['prev'], $this->flash['current']);
601: }
602:
603:
604: 605: 606: 607: 608: 609: 610: 611:
612: public function not_found($message = NULL, $type, $handler = NULL)
613: {
614: if ($message === NULL) {
615: $message = 'Not Found :: Controller: ' . $this->controller_name;
616: $message .= ' / Action: ' . $this->action_name;
617: $message .= ' / Route: ' . $this->route['name'];
618: }
619:
620: if ($handler === NULL) {
621: $handler = Arrays::get_value($this->settings['system'], 'not_found_handler');
622: }
623:
624:
625: if ($handler !== NULL) {
626: $this->controller_name = $handler[0];
627: $this->action_name = $handler[1];
628: $this->arguments = array('message' => $message, 'type' => $type);
629: }
630:
631: $this->not_found = TRUE;
632: if (method_exists($this->controller_name . 'Controller', $this->action_name) === TRUE) {
633: $this->run();
634: }
635:
636: $this->response->status_code = 404;
637: $this->response->set_body($message);
638: $this->response->send();
639: }
640:
641:
642: 643: 644: 645: 646: 647: 648: 649: 650:
651: public function create_uri($route_name, $data = array(), $uri_data = NULL, $anchor = NULL)
652: {
653: if (isset($this->routes[$route_name]) === FALSE) {
654: trigger_error('Named route <strong>"' . $route_name . '"</strong> not found!', E_USER_ERROR);
655: }
656:
657: $route = $this->routes[$route_name];
658:
659: $uri = $this->request->base_uri;
660:
661: $route_uri = str_replace(array('(', ')'), array('', ''), $route['uri']);
662:
663: $resource_current = explode('/', trim($this->request->resource, '/'));
664: $resource = explode('/', trim($route_uri, '/'));
665: $template = explode('/', trim($route['uri_template'], '/'));
666:
667: if (empty($resource) === TRUE) {
668: return $uri . '/' . implode('/', $data);
669: }
670:
671: foreach ($template as $key => $segment) {
672: $uri .= '/';
673:
674: if ($segment === '#') {
675: $uri .= $resource[$key];
676:
677: } else {
678: if (isset($data[$segment]) === FALSE) {
679: $uri .= $resource_current[$key];
680:
681: } else {
682: $uri .= $data[$segment];
683: }
684: }
685: }
686:
687: if (empty($uri_data) === FALSE) {
688: ksort($uri_data);
689:
690: $uri .= '/?' . http_build_query($uri_data);
691: }
692:
693: if ($anchor !== NULL) {
694: $uri .= '#' . $anchor;
695: }
696:
697: return $uri;
698: }
699:
700:
701: 702: 703: 704: 705: 706:
707: private function hooks($hooks)
708: {
709: if (empty($hooks) === TRUE) {
710: return;
711: }
712:
713: foreach ($hooks as $hook) {
714: if (isset($hook[0]) === TRUE
715: AND is_object($hook[0]) === FALSE) {
716:
717: $hook[0] = new $hook[0]($this);
718: }
719:
720: if (is_string($hook) === TRUE) {
721: call_user_func_array($hook, $this->arguments);
722:
723: } else {
724: call_user_func_array(array($hook[0], $hook[1]), $this->arguments);
725: }
726: }
727: }
728:
729:
730: 731: 732: 733: 734: 735:
736: public function __get($property)
737: {
738: if (isset($this->$property) === TRUE) {
739: return $this->$property;
740: }
741:
742: return NULL;
743: }
744: }