/** * Run application * * @staticvar boolean $is_init * @staticvar array $routes * @param $route (optional, ex: 'my/route->action') * @return void */ public function run($route = null) { static $is_init = false; static $routes = []; // routes stack for current request if (!$is_init) { $this->log->trace('Initializing', Logger::CATEGORY_DRONE); // param default values $default = [self::KEY_DEBUG => true, self::KEY_ERROR_BACKTRACE => true, self::KEY_ERROR_HANDLER => ['\\Drone\\Core', 'errorHandler'], self::KEY_ERROR_LOG => false, self::KEY_EXT_TEMPLATE => '.tpl', self::KEY_EXT_WEB => '.htm', self::KEY_PATH_CONTROLLER => PATH_ROOT . '_app/mod', self::KEY_PATH_TEMPLATE => PATH_ROOT . '_app/tpl', self::KEY_PATH_TEMPLATE_GLOBAL => PATH_ROOT . '_app/tpl/_global']; // init param default values foreach ($default as $k => $v) { if (!Registry::has($k)) { Registry::set($k, $v); } } // set default error handler if (is_array(Registry::get(self::KEY_ERROR_HANDLER))) { set_error_handler(Registry::get(self::KEY_ERROR_HANDLER)); } // init paths $this->__formatDir(Registry::get(self::KEY_PATH_CONTROLLER)); $this->__formatDir(Registry::get(self::KEY_PATH_TEMPLATE)); $this->__formatDir(Registry::get(self::KEY_PATH_TEMPLATE_GLOBAL)); $is_init = true; } Registry::set(self::KEY_ROUTE_CONTROLLER, false); // init controller if ($route !== null) { $routes[] = $route; // cache route $route = new Route(null, $route); Registry::set([self::KEY_ROUTE_CONTROLLER => $route->getController(), self::KEY_ROUTE_CLASS => $route->getClass(), self::KEY_ROUTE_TEMPLATE => $route->getController()]); if ($route->isAction()) { Registry::set(self::KEY_ROUTE_ACTION, $route->getAction()); } $this->log->trace('Route set: \'' . Registry::get(self::KEY_ROUTE_CONTROLLER) . '\'', Logger::CATEGORY_DRONE); } else { $is_index = false; $request = $_SERVER['REQUEST_URI']; $this->log->trace('Process request: \'' . $request . '\'', Logger::CATEGORY_DRONE); Registry::set(self::KEY_REQUEST, $request); if (($pos = strpos($request, '?')) !== false) { $request = substr($request, 0, $pos); } unset($pos); $routes[] = $request; if (substr($request, -1) != '/') { // ensure request has web extension if (substr($request, -strlen(Registry::get(self::KEY_EXT_WEB))) === Registry::get(self::KEY_EXT_WEB)) { // do not allow direct access to index like '/path/index.htm' if (basename($request) === 'index' . Registry::get(self::KEY_EXT_WEB)) { $this->error(self::ERROR_404); // kick direct index request return; } // rm web extension $request = substr($request, 0, strlen($request) - strlen(Registry::get(self::KEY_EXT_WEB))); } else { $this->error(self::ERROR_404); // kick request return; } } else { $is_index = true; } $r = null; foreach ($this->__routes as $r) { if ($rf = $r->matchFile($request)) { $this->log->trace('Route file loaded: \'' . $r->getController() . '\'', Logger::CATEGORY_DRONE); foreach ($rf as $k => $v) { $r = new Route($k, $v); if ($r->match($request)) { break 2; // match } } } else { if ($r->match($request)) { break; // match } } $r = null; // no match } if ($r) { $this->log->trace('Route (mapped) detected: \'' . $r->getPath() . '\'', Logger::CATEGORY_DRONE); Registry::set([self::KEY_ROUTE_CONTROLLER => $r->getController(), self::KEY_ROUTE_CLASS => $r->getClass(), self::KEY_ROUTE_TEMPLATE => $r->getController()]); if ($r->isAction()) { Registry::set(self::KEY_ROUTE_ACTION, $r->getAction()); } $this->view->setRouteParams($r->getParams()); // set route params } unset($r); // test static routes if (Registry::get(self::KEY_ROUTE_CONTROLLER) === false) { $request = str_replace('/', DIRECTORY_SEPARATOR, $request); if ($is_index) { $request .= 'index'; } Registry::set([self::KEY_ROUTE_CONTROLLER => $request, self::KEY_ROUTE_TEMPLATE => $request]); $this->log->trace('Route (static) detected: \'' . Registry::get(self::KEY_ROUTE_CONTROLLER) . '\'', Logger::CATEGORY_DRONE); } // cleanup unset($is_index, $request); } if (max(array_count_values($routes)) > 1) { $routes = []; // reset $this->error(self::ERROR_500, 'Route loop detected'); return; } // set full paths + extensions Registry::set(self::KEY_ROUTE_CONTROLLER, Registry::get(self::KEY_PATH_CONTROLLER) . ltrim(Registry::get(self::KEY_ROUTE_CONTROLLER), DIRECTORY_SEPARATOR) . '.php'); Registry::set(self::KEY_ROUTE_TEMPLATE, Registry::get(self::KEY_PATH_TEMPLATE) . ltrim(Registry::get(self::KEY_ROUTE_TEMPLATE), DIRECTORY_SEPARATOR) . Registry::get(self::KEY_EXT_TEMPLATE)); try { $this->view->resetTemplate(); // reset template (for multiple runs like errors) $this->view->setDefaultTemplate(Registry::get(self::KEY_ROUTE_TEMPLATE)); // set default template $this->error(false); // reset error flag if (is_file(Registry::get(self::KEY_ROUTE_CONTROLLER))) { ob_start(); // buffer output $this->__hooks(self::HOOK_BEFORE, 'before'); if (isset($this->__hooks[self::HOOK_BEFORE])) { foreach ($this->__hooks[self::HOOK_BEFORE] as $hook) { require $hook; } } $this->log->trace('Loading controller: \'' . Registry::get(self::KEY_ROUTE_CONTROLLER) . '\'', Logger::CATEGORY_DRONE); require_once Registry::get(self::KEY_ROUTE_CONTROLLER); $this->__headersSend(); // send headers $controller_class = Registry::get(self::KEY_ROUTE_CLASS); // call controller action if (Registry::has(self::KEY_ROUTE_ACTION)) { $this->log->trace('Calling action: \'' . Registry::get(self::KEY_ROUTE_ACTION) . '\' on controller class \'' . $controller_class . '\'', Logger::CATEGORY_DRONE); if (!class_exists($controller_class, false)) { if (count($routes) > 1) { $this->log->fatal('Multiple route controllers not found loop detected', Logger::CATEGORY_DRONE); $this->stop(); } $this->error(self::ERROR_500, 'Class \'' . $controller_class . '\' not found when calling route action'); return; } if (!method_exists($controller_class, Registry::get(self::KEY_ROUTE_ACTION))) { $this->error(self::ERROR_500, 'Method \'' . $controller_class . '::' . Registry::get(self::KEY_ROUTE_ACTION) . '\' not found when calling route action'); return; } // set controller instance $controller = new $controller_class(); if (method_exists($controller, '__before')) { $controller->__before(); } // call controller action $controller->{Registry::get(self::KEY_ROUTE_ACTION)}(); if (method_exists($controller, '__after')) { $controller->__after(); } } else { if ($this->deny(null)) { $this->error(self::ERROR_404, 'Deny no action'); return; } } unset($controller_class); // cleanup if (!Registry::get(self::KEY_DEBUG)) { $this->__bufferClean(); } $this->__hooks(self::HOOK_MIDDLE, 'middle'); if (isset($this->__hooks[self::HOOK_MIDDLE])) { foreach ($this->__hooks[self::HOOK_MIDDLE] as $hook) { require $hook; } } // view display template if (!is_null($this->view->getTemplate())) { $this->log->trace('Loading view template: \'' . $this->view->getTemplate() . '\'', Logger::CATEGORY_DRONE); if (!is_file($this->view->getTemplate())) { $this->error(self::ERROR_500, 'View template \'' . $this->view->getTemplate() . '\' not found'); return; } if (isset($controller)) { extract(get_object_vars($controller), EXTR_OVERWRITE); } // extract all view public properties for variable use in template extract(get_object_vars($this->view), EXTR_OVERWRITE); if (strlen($this->view->getTemplateHeader()) > 0) { include $this->view->getTemplateHeader(); } require $this->view->getTemplate(); if (strlen($this->view->getTemplateFooter()) > 0) { include $this->view->getTemplateFooter(); } } if (!$this->error()) { ob_end_flush(); // flush buffer $this->stop(); // finalize application } else { ob_end_clean(); // clean buffer $this->error(self::ERROR_500); // call 500 error handler } } else { $this->error(self::ERROR_404); } } catch (\Exception $ex) { $this->error($ex); } }