db_ready = file_exists('/status/ready'); $this->recursed = FALSE; $this->req = $this->get_req(); } /** * @param string $path - the current request path * Gets the current route * @return array */ private function get_req_route($path): array { // trim the path $path = trim($path); // remove first '/' $path = substr($path, 1); // get modified route foreach ($GLOBALS['routes'] as $key => $value) { $key = "/^{$key}$/"; if (!preg_match($key, $path, $matches)) continue; $path = $value; for ($i = 1; $i < count($matches); $i++) { $path = str_replace( "\\{$i}", $matches[$i], $path); } break; } // get path parts $parts = explode('/', $path); // get the length $len = count($parts); // get route info $route = array(); // e.g. / if ($path === '') $route = array( 'app' => 'index', 'slug' => 'index', 'args' => array(), ); // e.g. /home /login else if ($len === 1) $route = array( 'app' => $parts[0], 'slug' => 'index', 'args' => array(), ); // e.g. /home/posts else if ($len === 2) $route = array ( 'app' => $parts[0], 'slug' => $parts[1], 'args' => array(), ); // e.g. /home/posts/joe else $route = array ( 'app' => $parts[0], 'slug' => $parts[1], 'args' => array_slice($parts, 2), ); $routes = $GLOBALS['routes']; if (isset($routes[$path])) { $parts = explode('/', $routes[$path]); if (count($parts) == 1) { $route['app'] = $parts[0]; } else { $route['app'] = $parts[0]; $route['slug'] = $parts[1]; } } return $route; } /** * Gets the users ip */ private function get_ip(): string { $ip = ''; if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip = $_SERVER['REMOTE_ADDR']; } return $ip; } /** * Gets the curret request info * @return array */ private function get_req(): array { $method = $_SERVER['REQUEST_METHOD']; $uri_str = $_SERVER['REQUEST_URI']; $uri = parse_url($uri_str); if (!$uri) { $uri = array('path' => '/error'); } $path = ''; if (array_key_exists('path', $uri)) { $path = $uri['path']; } return array_merge( array( 'uri' => $uri, 'uri_str' => $uri_str, 'method' => $method, 'ip' => $this->get_ip() ), $this->get_req_route($path), ); } /** * Handles a router error code * @param int $code - the http error code */ private function handle_error(int $code): void { if ($this->recursed) die($code . ' (recursed)'); $this->recursed = TRUE; $this->req['app'] = 'error'; $this->req['slug'] = 'code'; $this->req['args'] = array($code); $this->handle_req(); } /** * @param array $req */ private function load_htc(array $req): void { $parts = explode('/', $req['uri_str']); $file = end($parts); $path = PUBLIC_ROOT . '/polyfills/' . $file; if (file_exists($path)) { header('Content-type: text/x-component'); include($path); } else { $this->handle_error(400); } } /** * @param array $req * @param array $req */ public function handle_req(): void { if ($this->recursed === FALSE) { // if we are in a recursing error handler // we dont want to trigger a db 503 forever // since its already active if ($this->db_ready === FALSE) { $this->handle_error(503); return; } } // server error if we cannot parse url if (!$this->req) { $this->handle_error(500); return; } // load htc if someone is requesting it (hi IE6 :3) if (str_ends_with($this->req['uri_str'], '.htc')) { $this->load_htc($this->req); return; } // load the controller $controller = $this->load_controller($this->req['app']); if ($controller === NULL) { $this->handle_error(404); return; } $ref = NULL; try { $cls = new ReflectionClass($controller); $mds = $cls->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($mds as $md) { if ($md->name !== $this->req['slug']) continue; if (count($md->getParameters()) != count($this->req['args'])) continue; $ref = $md; break; } } catch (Exception $_e) {} if ($ref === NULL) { $this->handle_error(404); return; } define('CONTEXT', $this->req); Component::load_lang('common', $this->req['app']); $ref->invokeArgs($controller, $this->req['args']); } }