summaryrefslogtreecommitdiff
path: root/src/web/core
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-04-01 11:09:25 -0400
committerFreya Murphy <freya@freyacat.org>2024-04-01 11:09:25 -0400
commit3a82baec9d793edf81ac2b151b0f4d4159641375 (patch)
treef9d50c296b078ac48c2a2391c172c3ccf37edb3f /src/web/core
parentrefactor asset dir, refactor oberver in lib (diff)
downloadxssbook2-3a82baec9d793edf81ac2b151b0f4d4159641375.tar.gz
xssbook2-3a82baec9d793edf81ac2b151b0f4d4159641375.tar.bz2
xssbook2-3a82baec9d793edf81ac2b151b0f4d4159641375.zip
login and register, liking on homepage
Diffstat (limited to 'src/web/core')
-rw-r--r--src/web/core/_controller.php64
-rw-r--r--src/web/core/_model.php44
-rw-r--r--src/web/core/database.php189
-rw-r--r--src/web/core/loader.php101
-rw-r--r--src/web/core/router.php147
5 files changed, 545 insertions, 0 deletions
diff --git a/src/web/core/_controller.php b/src/web/core/_controller.php
new file mode 100644
index 0000000..4a788d3
--- /dev/null
+++ b/src/web/core/_controller.php
@@ -0,0 +1,64 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+abstract class Controller {
+
+ // the main model
+ public $main;
+
+ // the loader
+ public $load;
+
+ // the database
+ public $db;
+
+ /**
+ * Creates a constructor
+ * @param Loader $load - the website loaded object
+ */
+ function __construct($load) {
+ $this->load = $load;
+ $this->main = $this->load->model('main');
+ $this->db = $this->main->db;
+
+ $info = $this->main->info;
+ $lang = $info['lang'];
+ $this->load->lang($lang);
+ $app = $info['app'];
+ if ($app) {
+ $this->load->app_lang($lang, $app);
+ }
+ }
+
+ public function index() {}
+
+ public function redirect($link) {
+ header('Location: '. $link, true, 301);
+ die();
+ }
+
+ protected function view($__name, $data = array()) {
+ $__root = $GLOBALS['webroot'];
+ $__path = $__root . '/_views/' . $__name . '.php';
+ if (is_file($__path)) {
+ extract($data);
+ require($__path);
+ return;
+ }
+ }
+
+ protected function is_ajax(): bool {
+ $_POST = json_decode(
+ file_get_contents("php://input"), true
+ );
+ return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
+ }
+
+ protected function error($code): void {
+ $_GET['code'] = $code;
+ $this->main->info['app'] = 'error';
+ $error_controller = $this->load->controller('apps/error');
+ $error_controller->index();
+ die();
+ }
+
+}
+?>
diff --git a/src/web/core/_model.php b/src/web/core/_model.php
new file mode 100644
index 0000000..936fab4
--- /dev/null
+++ b/src/web/core/_model.php
@@ -0,0 +1,44 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+abstract class Model {
+ // the main model
+ // shared by all controllers and models
+ public $main;
+ public $load;
+
+ // the database
+ public $db;
+
+ private $config;
+
+ /**
+ * Creates a model
+ * @param Loader $load - the main loader object
+ */
+ function __construct($load) {
+ $this->load = $load;
+ $this->main = $this->load->model('main');
+ $this->db = $this->main->db;
+ $this->config = new Aesthetic();
+ }
+
+ /**
+ * @returns the base model data
+ */
+ public function get_data(): array {
+ $data = array();
+ $data['self'] = $this->main->user();
+
+ $info = $this->main->info;
+ $app = $info['app'];
+
+ if ($app) {
+ $files = $this->config->get_files($app);
+ $data = array_merge($data, $files);
+ } else {
+ $files = $this->config->get_files();
+ $data = array_merge($data, $files);
+ }
+
+ return $data;
+ }
+}
diff --git a/src/web/core/database.php b/src/web/core/database.php
new file mode 100644
index 0000000..81352a9
--- /dev/null
+++ b/src/web/core/database.php
@@ -0,0 +1,189 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+
+class DatabaseQuery {
+
+ private $conn;
+ private $query;
+
+ private $where;
+ private $set;
+
+ private $param;
+
+ function __construct($conn) {
+ $this->conn = $conn;
+ $this->query = '';
+
+ $this->set = FALSE;
+ $this->where = FALSE;
+ $this->param = array();
+ }
+
+ private function in($array) {
+ $in = 'IN (';
+ foreach ($array as $idx => $item) {
+ if ($idx != 0) {
+ $in .= ",";
+ }
+ $in .= "?";
+ array_push($this->param, $item);
+ }
+ $in .= ")";
+ return $in;
+ }
+
+ public function select($select) {
+ $this->query .= "SELECT $select\n";
+ return $this;
+ }
+
+ public function from($from) {
+ $this->query .= "FROM $from\n";
+ return $this;
+ }
+
+ public function where($cond) {
+ if (!$this->where) {
+ $this->where = TRUE;
+ $this->query .= "WHERE ";
+ }
+ $this->query .= "$cond ";
+ return $this;
+ }
+
+ public function like($item) {
+ $this->query .= "LIKE ?\n";
+ array_push($this->param, $item);
+ return $this;
+ }
+
+ public function eq($item) {
+ $this->query .= "= ?\n";
+ array_push($this->param, $item);
+ return $this;
+ }
+
+ public function ne($item) {
+ $this->query .= "<> ?\n";
+ array_push($this->param, $item);
+ return $this;
+ }
+
+ public function lt($item) {
+ $this->query .= "< ?\n";
+ array_push($this->param, $item);
+ return $this;
+ }
+
+ public function le($item) {
+ $this->query .= "<= ?\n";
+ array_push($this->param, $item);
+ return $this;
+ }
+
+ public function where_in($column, $array) {
+ if (!$this->where) {
+ $this->where = TRUE;
+ $this->query .= "WHERE ";
+ }
+ if (empty($array)) {
+ $this->query .= "FALSE\n";
+ return $this;
+ }
+ $in = $this->in($array);
+ $this->query .= "$column $in\n";
+ return $this;
+ }
+
+ public function and() {
+ $this->query .= "AND ";
+ return $this;
+ }
+
+ public function or() {
+ $this->query .= "OR ";
+ return $this;
+ }
+
+ public function join($table, $on, $type = 'LEFT') {
+ $this->query .= "$type JOIN $table ON $on\n";
+ return $this;
+ }
+
+ public function limit($limit) {
+ $this->query .= "LIMIT ?\n";
+ array_push($this->param, $limit);
+ return $this;
+ }
+
+ public function offset($offset) {
+ $this->query .= "OFFSET ?\n";
+ array_push($this->param, $offset);
+ return $this;
+ }
+
+ public function order_by($column, $order = 'ASC') {
+ $this->query .= "ORDER BY " . $column . ' ' . $order . ' ';
+ return $this;
+ }
+
+ public function rows() {
+ $stmt = $this->conn->prepare($this->query);
+ try {
+ $stmt->execute($this->param);
+ } catch (Exception $ex) {
+ echo $ex;
+ echo '<br> >> caused by <<<br>';
+ echo str_replace("\n", "<br>", $this->query);
+ }
+ return $stmt->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ public function row() {
+ $stmt = $this->conn->prepare($this->query);
+ $stmt->execute($this->param);
+ return $stmt->fetch(PDO::FETCH_ASSOC);
+ }
+}
+
+/**
+ * DatabaseHelper
+ * allows queries on the
+ * xssbook postgres database
+ */
+class DatabaseHelper {
+
+ private $conn;
+
+ function __construct() {
+ $this->conn = NULL;
+ }
+
+ private function connect() {
+ if ($this->conn === NULL) {
+ $user = getenv("POSTGRES_USER");
+ $pass = getenv("POSTGRES_PASSWORD");
+ $db = getenv("POSTGRES_DB");
+ $host = 'db';
+ $port = '5432';
+
+ $conn_str = sprintf("pgsql:host=%s;port=%d;dbname=%s;user=%s;password=%s",
+ $host,
+ $port,
+ $db,
+ $user,
+ $pass
+ );
+ $this->conn = new \PDO($conn_str);
+ $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ }
+ return $this->conn;
+ }
+
+ public function select($select) {
+ $conn = $this->connect();
+ $query = new DatabaseQuery($conn);
+ return $query->select($select);
+ }
+
+}
diff --git a/src/web/core/loader.php b/src/web/core/loader.php
new file mode 100644
index 0000000..2091533
--- /dev/null
+++ b/src/web/core/loader.php
@@ -0,0 +1,101 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+class Loader {
+
+ // keep track of what has been loaded
+ private $loaded;
+
+ function __construct() {
+ $this->loaded = array();
+ }
+
+ /**
+ * 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
+ * @param string $lang_code 0 the language code
+ */
+ public function lang($lang_code): void {
+ $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/';
+ $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 $lang_code - the language code
+ * @param string $name - the name of the app
+ */
+ public function app_lang($lang_code, $name): void {
+ $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/apps/';
+ $file = $dir . $name . '.php';
+ if (file_exists($file)) {
+ $lang = $GLOBALS['lang'];
+ require($dir . $name . '.php');
+ $GLOBALS['lang'] = $lang;
+ }
+ }
+
+}
diff --git a/src/web/core/router.php b/src/web/core/router.php
new file mode 100644
index 0000000..72c7674
--- /dev/null
+++ b/src/web/core/router.php
@@ -0,0 +1,147 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+class Router {
+
+ // the loader
+ private $load;
+
+ // the main model
+ private $main;
+
+ /**
+ * Creates a router
+ * @param Loader $load - the main laoder object
+ */
+ function __construct($load) {
+ $this->load = $load;
+ $this->main = $this->load->model('main');
+ }
+
+ /**
+ * @param string $path - the current request path
+ * Gets the current route
+ * @return array<string,mixed>
+ */
+ private function get_req_route($path): array {
+ // trim the path
+ $path = trim($path);
+ // remove first '/'
+ $path = substr($path, 1);
+ // get path parts
+ $parts = explode('/', $path);
+
+ $len = count($parts);
+
+ // get route info
+ $route = array();
+ // e.g. /
+ if ($path === '') {
+ $route = array(
+ 'route' => '',
+ 'slug' => 'index',
+ );
+ // e.g. /home /login
+ } else if ($len === 1) {
+ $route = array(
+ 'route' => $parts[0],
+ 'slug' => 'index',
+ );
+ // e.g. /home/posts
+ } else {
+ $route = array (
+ 'route' => implode('/', array_slice($parts, 0, -1)),
+ 'slug' => end($parts)
+ );
+ };
+
+ $route['app'] = $route['route'];
+ $routes = $GLOBALS['routes'];
+ if (array_key_exists($route['route'], $routes)) {
+ $route['route'] = $routes[$route['route']];
+ }
+
+ return $route;
+ }
+
+ /**
+ * Gets the curret request info
+ * @return array<string,mixed>
+ */
+ private function get_req(): array {
+ $method = $_SERVER['REQUEST_METHOD'];
+
+ $uri = parse_url($_SERVER['REQUEST_URI']);
+ $path = $uri['path'];
+
+ return array_merge(
+ array(
+ 'uri' => $uri,
+ 'method' => $method,
+ 'lang' => $this->get_lang(),
+ ),
+ $this->get_req_route($path),
+ );
+ }
+
+ /**
+ * Gets the current language
+ * @return string
+ */
+ private function get_lang(): string {
+ return 'en_US';
+ }
+
+ /**
+ * Handles a router error code
+ * @param int $code - the http error code
+ * @param bool $recursed
+ */
+ private function handle_error($code, $recursed): void {
+ if ($recursed) {
+ die($code . ' (recursed)');
+ }
+
+ $this->main->info['slug'] = 'index';
+ $this->main->info['app'] = 'error';
+ $this->main->info['route'] = 'apps/error';
+ $req = $this->main->info;
+ $_GET['code'] = $code;
+
+ $this->handle_req($req, TRUE);
+ }
+
+ /**
+ * @param array $req
+ * @param bool $recursed
+ */
+ private function handle_req($req, $recursed = FALSE): void {
+ $controller = $this->load->controller($req['route']);
+
+ if ($controller === NULL) {
+ $this->handle_error(404, $recursed);
+ return;
+ }
+
+ $ref = NULL;
+ try {
+ $ref = new ReflectionMethod($controller, $req['slug']);
+ } catch (Exception $_e) {}
+
+ if ($ref === NULL || !$ref->isPublic()) {
+ $this->handle_error(404, $recursed);
+ return;
+
+ }
+
+ $ref->invoke($controller);
+ }
+
+ /**
+ * Handels the incomming reuqest
+ */
+ public function handle_request(): void {
+ $req = $this->get_req();
+ $this->main->info = $req;
+ $this->handle_req($req);
+ }
+
+}