diff options
Diffstat (limited to 'src/web/core')
-rw-r--r-- | src/web/core/_controller.php | 54 | ||||
-rw-r--r-- | src/web/core/_model.php | 51 | ||||
-rw-r--r-- | src/web/core/component.php | 119 | ||||
-rw-r--r-- | src/web/core/controller.php | 43 | ||||
-rw-r--r-- | src/web/core/core.php | 85 | ||||
-rw-r--r-- | src/web/core/loader.php | 112 | ||||
-rw-r--r-- | src/web/core/model.php | 29 | ||||
-rw-r--r-- | src/web/core/router.php | 227 |
8 files changed, 380 insertions, 340 deletions
diff --git a/src/web/core/_controller.php b/src/web/core/_controller.php deleted file mode 100644 index 1da5a96..0000000 --- a/src/web/core/_controller.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php /* Copyright (c) 2024 Freya Murphy */ -abstract class Controller { - - // the main model - public Main_model $main; - - // the loader - public Loader $load; - - /** - * Creates a constructor - * @param Loader $load - the website loaded object - */ - function __construct($load) { - $this->load = $load; - $this->main = $this->load->model('main'); - - $this->load->lang(); - $info = $this->main->info; - $app = $info['app']; - if ($app) { - $this->load->app_lang($app); - } - } - - public function index(): void {} - - public function redirect(string $link): void { - header('Location: '. $link, true, 301); - die(); - } - - /** - * @param array<int,mixed> $data - */ - protected function view(string $__name, array $data = array()): void { - $__root = $GLOBALS['webroot']; - $__path = $__root . '/_views/' . $__name . '.php'; - if (is_file($__path)) { - extract($data); - require($__path); - return; - } - } - - protected function error(int $code): void { - $_GET['code'] = $code; - $this->main->info['app'] = 'error'; - $error_controller = $this->load->controller('error'); - $error_controller->index(); - die(); - } - -} diff --git a/src/web/core/_model.php b/src/web/core/_model.php deleted file mode 100644 index 57127de..0000000 --- a/src/web/core/_model.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php /* Copyright (c) 2024 Freya Murphy */ -abstract class Model { - // the main model - // shared by all controllers and models - public Main_model $main; - public Loader $load; - - // the database - public DatabaseHelper $db; - - private mixed $config; - - /** - * Creates a model - * @param Loader $load - the main loader object - * @param ?Main_model $main - */ - function __construct(Loader $load, bool $main = FALSE) { - $this->load = $load; - if ($main) { - $this->main = $this; - } else { - $this->main = $this->load->model('main'); - } - $this->db = $this->load->db(); - } - - /** - * @returns the base model data - */ - public function get_data(): ?array { - $data = array(); - - $info = $this->main->info; - $app = $info['app']; - - $data['title'] = lang('first_name'); - $data['desc'] = lang('default_short_desc'); - $data['css'] = array(); - - $style = $GLOBALS['style']; - if (isset($style[$app])) { - $css = $style[$app]; - if (!is_array($css)) - $css = array($css); - $data['css'] = $css; - } - - return $data; - } -} diff --git a/src/web/core/component.php b/src/web/core/component.php new file mode 100644 index 0000000..376e24d --- /dev/null +++ b/src/web/core/component.php @@ -0,0 +1,119 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ + +/** + * Gives access to imporant + * needed utility functions for + * accessing everything else! + */ +abstract class Component extends Core { + + // keep track of what has been loaded + private static array $loaded = array(); + +// ============================= LOADABLE OBJECTS == + + /** + * Loads a $type of object from a $dir with a given $name + * @param string $name - the name of the object to load + * @param string $dir - the directory theese objects are stored in + * @param string $type - the type of the object + */ + private function load_type($name, $dir, $type): object|NULL + { + + $path = $dir . '/' . $name . '.php'; + + // dont reload an ohject + if (array_key_exists($path, Component::$loaded)) + return Component::$loaded[$path]; + + // only load a object if it exists + if (!file_exists($path)) + return NULL; + + + $parts = explode('/', $name); + $part = end($parts); + $class = ucfirst($part) . '_' . $type; + require($path); + + $ref = NULL; + try { + $ref = new ReflectionClass($class); + } catch (Exception $_e) {} + + if ($ref === NULL) + return NULL; + + $obj = $ref->newInstance(); + Component::$loaded[$path] = $obj; + + return $obj; + } + + /** + * Loads a model + * @param string $name - the name of the model to load + */ + protected function load_model($name): Model|NULL + { + $dir = WEB_ROOT . '/_model'; + return $this->load_type($name, $dir, 'model'); + } + + /** + * Loads a controller + * @param string $name - the name of the controller to load + */ + public function load_controller($name): Controller|NULL + { + $dir = WEB_ROOT . '/_controller'; + return $this->load_type($name, $dir, 'controller'); + } + +// ========================================= LANG == + + /** + * Loads a php lang file into the lang array + */ + private static function load_lang_file(string $file): void + { + $lang = $GLOBALS['__lang']; + require($file); + $GLOBALS['__lang'] = $lang; + } + + /** + * Loads each php file lang strings in a directory + */ + private static function load_lang_dir(string $dir): void + { + if ($handle = opendir($dir)) { + while (false !== ($entry = readdir($handle))) { + if ($entry === '.' || $entry === '..') + continue; + Component::load_lang_file($entry); + } + } + } + + /** + * Loads the given common lang + */ + protected static function load_lang(string ...$langs): void + { + $root = WEB_ROOT . '/lang'; + + foreach ($langs as $lang) { + $file = "{$root}/{$lang}.php"; + $dir = "{$root}/{$lang}"; + + if (file_exists($file)) + Component::load_lang_file($file); + else if (is_dir($dir)) + Component::load_lang_dir($dir); + + } + } + +} diff --git a/src/web/core/controller.php b/src/web/core/controller.php new file mode 100644 index 0000000..340bbb1 --- /dev/null +++ b/src/web/core/controller.php @@ -0,0 +1,43 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ + +abstract class Controller extends Component { + + /** + * Default index for a app, empty + */ + public function index(): void {} + + /** + * Redirectes to a link + */ + public function redirect(string $link): void + { + header('Location: '. $link, true, 301); + die(); + } + + /** + * Lodas a view + */ + protected function view(string $__name, array $data = array()): void + { + $__path = WEB_ROOT . '/_views/' . $__name . '.php'; + if (is_file($__path)) { + extract($data); + require($__path); + return; + } + } + + /** + * Loads a erorr page with a given + * error code + */ + protected function error(int $code): void + { + $error_controller = $this->load_controller('error'); + $error_controller->code($code); + die(); + } + +} diff --git a/src/web/core/core.php b/src/web/core/core.php new file mode 100644 index 0000000..d71870e --- /dev/null +++ b/src/web/core/core.php @@ -0,0 +1,85 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ + +/** + * Core functions needed everywhere + */ +abstract class Core { + + private static ?DatabaseHelper $db = NULL; + + /** + * Loads the database + */ + public static function db(): DatabaseHelper + { + if (!Component::$db) + Component::$db = new DatabaseHelper(); + return Component::$db; + } + + /** + * Gets the stamp for a asset path + * @param string $path + */ + public static function asset_stamp(string $path): int + { + $path = PUBLIC_ROOT . '/' . $path; + return @filemtime($path); + } + + /** + * Gets a full path url from a relative path + * @param string $path + * @param bool $timestamp + */ + public static function get_url(string $path, bool $timestamp = FALSE): string + { + $host = $_SERVER['HTTP_HOST']; + + if (ENVIRONMENT == 'production') + $host = lang('domain'); + + $base = lang('base_path'); + $url = "http://{$host}{$base}{$path}"; + if ($timestamp) { + $time = @filemtime(PHP_ROOT . '/' . $path); + $url .= "?timestamp={$time}"; + } + return $url; + } + + /** + * Loads a css html link + * @param string $path - the path to the css file + */ + public static function link_css(string $path): string + { + $stamp = Core::asset_stamp($path); + $href = Core::get_url("public/{$path}?stamp={$stamp}"); + return '<link rel="stylesheet" href="'. $href .'">'; + } + + /** + * Loads a css html link + * @param string $path - the path to the css file + */ + public static function embed_css(string $path): string + { + $file = PUBLIC_ROOT . '/' . $path; + if (file_exists($file)) { + $text = file_get_contents($file); + return "<style>{$text}</style>"; + } else { + return ""; + } + } + + /** + * Formats a ISO date + * @param $iso_date the ISO date + */ + public static function format_date(string $iso_date): string + { + return date("Y-m-d D H:m", strtotime($iso_date)); + } +} diff --git a/src/web/core/loader.php b/src/web/core/loader.php deleted file mode 100644 index 101abef..0000000 --- a/src/web/core/loader.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php /* Copyright (c) 2024 Freya Murphy */ -class Loader { - - // keep track of what has been loaded - private array $loaded; - - // the database - private ?DatabaseHelper $db; - - function __construct() { - $this->loaded = array(); - $this->db = NULL; - } - - /** - * Loads a $type of object from a $dir with a given $name - * @param string $name - the name of the object to load - * @param string $dir - the directory theese objects are stored in - * @param string $type - the type of the object - */ - private function load_type($name, $dir, $type): object|NULL { - $path = $dir . '/' . $name . '.php'; - if (array_key_exists($path, $this->loaded)) { - return $this->loaded[$path]; - } - - if (!file_exists($path)) { - return NULL; - } - - $parts = explode('/', $name); - $part = end($parts); - $class = ucfirst($part) . '_' . $type; - require($path); - - $ref = NULL; - try { - $ref = new ReflectionClass($class); - } catch (Exception $_e) {} - - if ($ref === NULL) { - return NULL; - } - - $obj = $ref->newInstance($this); - $this->loaded[$path] = $obj; - - return $obj; - } - - /** - * Loads a model - * @param string $name - the name of the model to load - */ - public function model($name): object|NULL { - $root = $GLOBALS['webroot']; - $dir = $root . '/_model'; - return $this->load_type($name, $dir, 'model'); - } - - /** - * Loads a controller - * @param string $name - the name of the controller to load - */ - public function controller($name): Controller|NULL { - $root = $GLOBALS['webroot']; - $dir = $root . '/_controller'; - return $this->load_type($name, $dir, 'controller'); - } - - /** - * Loads the given common lang - */ - public function lang(): void { - $dir = $GLOBALS['webroot'] . '/lang/'; - $lang = $GLOBALS['lang']; - if ($handle = opendir($dir)) { - while (false !== ($entry = readdir($handle))) { - if ($entry === '.' || $entry === '..' || $entry === 'apps') { - continue; - } - $path = $dir . $entry; - require($path); - } - } - $GLOBALS['lang'] = $lang; - } - - /** - * Loads a given app specific lang - * @param string $name - the name of the app - */ - public function app_lang($name): void { - $dir = $GLOBALS['webroot'] . '/lang/apps/'; - $file = $dir . $name . '.php'; - if (file_exists($file)) { - $lang = $GLOBALS['lang']; - require($dir . $name . '.php'); - $GLOBALS['lang'] = $lang; - } - } - - public function db(): DatabaseHelper { - if ($this->db) { - return $this->db; - } else { - $this->db = new DatabaseHelper(); - return $this->db; - } - } - -} diff --git a/src/web/core/model.php b/src/web/core/model.php new file mode 100644 index 0000000..8e105da --- /dev/null +++ b/src/web/core/model.php @@ -0,0 +1,29 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ + +abstract class Model extends Component { + + public static function get_base_data(?string $app = NULL): array + { + $data = array(); + $data['title'] = lang('first_name'); + $data['desc'] = lang('default_short_desc'); + $data['css'] = array(); + + $style = $GLOBALS['style']; + + if (!$app) + $app = CONTEXT['app']; + + if (isset($style[$app])) { + $css = $style[$app]; + if (!is_array($css)) + $css = array($css); + else + $css = $style['app']; + $data['css'] = $css; + } + + return $data; + } + +} diff --git a/src/web/core/router.php b/src/web/core/router.php index 1ad6cb5..2cda384 100644 --- a/src/web/core/router.php +++ b/src/web/core/router.php @@ -1,68 +1,91 @@ <?php /* Copyright (c) 2024 Freya Murphy */ -class Router { - // the loader - private Loader $load; - - // the main model - private Main_model $main; - - // the database - private DatabaseHelper $db; +class Router extends Component { private bool $db_ready; + private bool $recursed; + private array $req; /** * Creates a router * @param Loader $load - the main laoder object */ - function __construct($load) { - $this->load = $load; - $this->db = $load->db(); - $this->main = $this->load->model('main'); + function __construct() + { $this->db_ready = file_exists('/status/ready'); + $this->recursed = FALSE; + $this->req = $this->get_req(); } /** - * @param string $path - the current request path + * @param string $path - the current request path * Gets the current route * @return array<string,mixed> */ - private function get_req_route($path): 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 === '') { + if ($path === '') $route = array( - 'app' => '', + 'app' => 'index', 'slug' => 'index', + 'args' => array(), ); // e.g. /home /login - } else if ($len === 1) { + else if ($len === 1) $route = array( 'app' => $parts[0], 'slug' => 'index', + 'args' => array(), ); // e.g. /home/posts - } else { + else if ($len === 2) $route = array ( - 'app' => implode('/', array_slice($parts, 0, -1)), - 'slug' => end($parts) + '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 (array_key_exists($route['app'], $routes)) { - $parts = explode('/', $routes[$route['app']]); + if (isset($routes[$path])) { + $parts = explode('/', $routes[$path]); if (count($parts) == 1) { $route['app'] = $parts[0]; } else { @@ -77,7 +100,8 @@ class Router { /** * Gets the users ip */ - private function get_ip(): string { + private function get_ip(): string + { $ip = ''; if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; @@ -93,13 +117,14 @@ class Router { * Gets the curret request info * @return array<string,mixed> */ - private function get_req(): array|bool { + private function get_req(): array + { $method = $_SERVER['REQUEST_METHOD']; - $uri_str = $_SERVER['REQUEST_URI']; $uri = parse_url($uri_str); + if (!$uri) { - return FALSE; + $uri = array('path' => '/error'); } $path = ''; @@ -121,138 +146,94 @@ class Router { /** * Handles a router error code * @param int $code - the http error code - * @param bool $recursed */ - private function handle_error(int $code, bool $recursed): void { - if ($recursed) { + private function handle_error(int $code): void + { + if ($this->recursed) die($code . ' (recursed)'); - } - $uri_str = $_SERVER['REQUEST_URI']; - $req = array(); - $req['slug'] = 'index'; - $req['app'] = 'error'; - $req['uri_str'] = $uri_str; - $this->main->info = $req; - $_GET['code'] = $code; - $this->handle_req($req, TRUE); + + $this->recursed = TRUE; + $this->req['app'] = 'error'; + $this->req['slug'] = 'code'; + $this->req['args'] = array($code); + $this->handle_req(); } - /** - * @param array<int,mixed> $req - */ - private function load_htc(array $req, bool $recursed): void { + /** + * @param array<int,mixed> $req + */ + private function load_htc(array $req): void + { $parts = explode('/', $req['uri_str']); $file = end($parts); - $path = $GLOBALS['publicroot'] . '/polyfills/' . $file; + $path = PUBLIC_ROOT . '/polyfills/' . $file; if (file_exists($path)) { header('Content-type: text/x-component'); include($path); } else { - $this->handle_error(400, $recursed); + $this->handle_error(400); } } /** * @param array $req - * @param array<int,mixed> $req - * @param bool $recursed + * @param array<int,mixed> $req */ - private function handle_req(array $req, bool $recursed = FALSE): void { - - if ($recursed === false) { - if ( - $this->db_ready === false && - in_array($req['app'], $GLOBALS['serviceable']) === false - ) { - $this->handle_error(503, $recursed); - return; - } - - if ($this->check_banned($req)) { - $this->handle_error(401, $recursed); + 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; } } - if (!$req) { - $this->handle_error(500, $recursed); + // server error if we cannot parse url + if (!$this->req) { + $this->handle_error(500); return; } - if (str_ends_with($req['uri_str'], '.htc')) { - $this->load_htc($req, $recursed); + // 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; } - $controller = $this->load->controller($req['app']); - + // load the controller + $controller = $this->load_controller($this->req['app']); if ($controller === NULL) { - $this->handle_error(404, $recursed); + $this->handle_error(404); return; } $ref = NULL; try { - $ref = new ReflectionMethod($controller, $req['slug']); + $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 || !$ref->isPublic()) { - $this->handle_error(404, $recursed); + if ($ref === NULL) { + $this->handle_error(404); return; - } - $ref->invoke($controller); - } - - /** - * @param array<int,mixed> $req - */ - private function log_request(array $req): void { - if ( - $req === FALSE || - $this->db_ready === FALSE || - in_array($req['app'], $GLOBALS['serviceable']) - ) { - return; - } - - $query = $this->db - ->insert_into('admin.request_log', - 'ip', 'method', 'uri') - ->values( - $req['ip'], $req['method'], $req['uri_str']); - - $query->execute(); - } - - /** - * @param array<int,mixed> $req - */ - private function check_banned(array $req): bool { - $ip = FALSE; - if ($req) { - $ip = $req['ip']; - } else { - $ip = $this->get_ip(); - } - $query = $this->db - ->select('TRUE') - ->from('admin.banned') - ->where('ip')->eq($ip); - - return !!($query->row()); - } - - /** - * Handels the incomming reuqest - */ - public function handle_request(): void { - $req = $this->get_req(); - $this->log_request($req); - $this->main->info = $req; - $this->handle_req($req); + define('CONTEXT', $this->req); + Component::load_lang('common', $this->req['app']); + $ref->invokeArgs($controller, $this->req['args']); } } |