diff --git a/hanyuu.php b/hanyuu.php index b73702c..da8ea05 100644 --- a/hanyuu.php +++ b/hanyuu.php @@ -14,6 +14,7 @@ define('HAU_DIR_SOURCE', HAU_ROOT . '/src'); define('HAU_DIR_LIBRARIES', HAU_ROOT . '/lib'); define('HAU_DIR_CONFIG', HAU_ROOT . '/config'); define('HAU_DIR_MIGRATIONS', HAU_ROOT . '/database'); +define('HAU_DIR_TEMPLATES', HAU_ROOT . '/templates'); define('HAU_NDX_PATH', HAU_DIR_LIBRARIES . '/index'); define('HAU_NDX_PATH_DEV', HAU_DIR_LIBRARIES . '/index-dev'); diff --git a/public/index.php b/public/index.php index b118c11..6a3d888 100644 --- a/public/index.php +++ b/public/index.php @@ -3,7 +3,7 @@ namespace Hanyuu; require_once __DIR__ . '/../hanyuu.php'; -$request = \Index\Http\HttpRequest::fromRequest(); - $hau->setUpHttp(); -$hau->dispatchHttp($request); +$hau->dispatchHttp( + \Index\Http\HttpRequest::fromRequest() +); diff --git a/src/HanyuuContext.php b/src/HanyuuContext.php index 4d93afd..f64ff92 100644 --- a/src/HanyuuContext.php +++ b/src/HanyuuContext.php @@ -1,7 +1,6 @@ config = $config; } + public function getSiteName(): string { + return $this->config->getValue('site:name', IConfig::T_STR, 'Hanyuu'); + } + public function connectDb(?IDbConnection $dbConn = null): void { $dbConn ??= DbTools::create($this->config->getValue('database:dsn', IConfig::T_STR, 'null')); $dbConn->execute(self::DB_INIT); @@ -45,6 +50,23 @@ class HanyuuContext { return new FsDbMigrationRepo(HAU_DIR_MIGRATIONS); } + public function getTemplating(): TemplateContext { + if($this->tpl === null) { + $this->tpl = new TemplateContext(HAU_DIR_TEMPLATES); + $this->tpl->setGlobal('hau', $this); + } + + return $this->tpl; + } + + public function renderTemplate(...$args): string { + return $this->getTemplating()->render(...$args); + } + + public function getRouter(): IRouter { + return $this->router->getRouter(); + } + public function setUpHttp(): void { $this->router = new HttpFx; $this->router->use('/', function($response) { @@ -60,33 +82,29 @@ class HanyuuContext { } private function registerErrorPages(): void { - /*$this->router->addErrorHandler(400, function($response) { - $response->setContent(Template::renderRaw('errors.400')); + $this->router->addErrorHandler(400, function($response) { + $response->setContent($this->renderTemplate('errors/400')); + }); + $this->router->addErrorHandler(401, function($response) { + $response->setContent($this->renderTemplate('errors/401')); }); $this->router->addErrorHandler(403, function($response) { - $response->setContent(Template::renderRaw('errors.403')); + $response->setContent($this->renderTemplate('errors/403')); }); $this->router->addErrorHandler(404, function($response) { - $response->setContent(Template::renderRaw('errors.404')); + $response->setContent($this->renderTemplate('errors/404')); }); $this->router->addErrorHandler(500, function($response) { - $response->setContent(file_get_contents(MSZ_TEMPLATES . '/500.html')); + $response->setContent(file_get_contents(HAU_DIR_TEMPLATES . '/errors/500.html')); }); $this->router->addErrorHandler(503, function($response) { - $response->setContent(file_get_contents(MSZ_TEMPLATES . '/503.html')); - });*/ + $response->setContent(file_get_contents(HAU_DIR_TEMPLATES . '/errors/503.html')); + }); } private function registerHttpRoutes(): void { $this->router->get('/', function($response, $request) { - $siteName = $this->config->getValue('site:name', IConfig::T_STR, 'Hanyuu'); - - return "\r\n" - . "{$siteName}\r\n" - . "
\r\n" - . "

Under Construction

\r\n" - . " \"\"/\r\n" - . "
\r\n"; + return 503; }); } } diff --git a/src/Templating/Template.php b/src/Templating/Template.php new file mode 100644 index 0000000..a487c4b --- /dev/null +++ b/src/Templating/Template.php @@ -0,0 +1,50 @@ +vars->setVar($name, $value); + } + public function removeVar(string $name): void { + $this->vars->removeVar($name); + } + + public function render(array $vars = [], ?TemplateSelf $self = null): string { + if(!is_file($this->path)) + throw new RuntimeException('Template file does not exist: ' . $this->path); + + $self = new TemplateSelf($this->context->getFunctions() + $vars + $this->vars->getVars(), $self); + $self->tplPath = $this->path; + + $self->var = fn(string $name, mixed $default = null) => $this->vars->getVar($name, $default); + $self->setVar = fn(string $name, mixed $value) => $this->vars->setVar($name, $value); + $self->remVar = fn(string $name) => $this->vars->removeVar($name); + + $self->block = fn(string $name, mixed $contents) => $this->context->setBlock($name, new TemplateBlock($self, $contents)); + $self->include = fn(string $name) => $this->context->render($name, [], $self); + + $self->ctx = $this->context; + $self->extends = fn(string $name) => $self->extends = $name; + + ob_start(); + (static function() use ($self) { include $self->tplPath; })(); + $buffer = ob_get_contents(); + ob_end_clean(); + + if(is_string($self->extends)) { + if(!empty($buffer)) + throw new RuntimeException('You cannot output from templates that extend another.'); + $buffer = $this->context->render($self->extends, [], $self); + } + + return $buffer; + } +} diff --git a/src/Templating/TemplateBlock.php b/src/Templating/TemplateBlock.php new file mode 100644 index 0000000..664fc93 --- /dev/null +++ b/src/Templating/TemplateBlock.php @@ -0,0 +1,22 @@ +body)) { + ob_start(); + ($this->body)($this->self); + $body = ob_get_contents(); + ob_end_clean(); + } else $body = strval($this->body); + + return $body; + } +} diff --git a/src/Templating/TemplateContext.php b/src/Templating/TemplateContext.php new file mode 100644 index 0000000..b25909b --- /dev/null +++ b/src/Templating/TemplateContext.php @@ -0,0 +1,56 @@ +pathFormat = rtrim($path, '\\/') . DIRECTORY_SEPARATOR . '%s' . self::EXT; + $this->vars = new TemplateVars; + $this->defineFunction('global', $this->vars->getVar(...)); + $this->defineFunction('getBlock', $this->getBlock(...)); + $this->defineFunction('x', fn(string $string) => htmlspecialchars($string, ENT_QUOTES, 'utf-8', true)); + } + + public function setGlobal(string $name, mixed $value): void { + $this->vars->setVar($name, $value); + } + public function removeGlobal(string $name): void { + $this->vars->removeVar($name); + } + + public function defineFunction(string $name, Closure|callable $function): void { + $this->functions[$name] = $function; + } + public function getFunctions(): array { + return $this->functions; + } + + public function create(string $path): Template { + return new Template($this, new TemplateVars($this->vars), sprintf($this->pathFormat, $path)); + } + + public function exists(string $path): bool { + return is_file(sprintf($this->pathFormat, $path)); + } + + public function render(string $path, array $vars = [], ?TemplateSelf $self = null): string { + return $this->create($path)->render($vars, $self); + } + + public function getBlock(string $name, mixed $default = ''): string { + return ($this->blocks[$name] ?? new TemplateBlock(new \stdClass, $default))->render(); + } + + public function setBlock(string $name, TemplateBlock $block): void { + $this->blocks[$name] = $block; + } +} diff --git a/src/Templating/TemplateSelf.php b/src/Templating/TemplateSelf.php new file mode 100644 index 0000000..4ce9c31 --- /dev/null +++ b/src/Templating/TemplateSelf.php @@ -0,0 +1,32 @@ +members += $inherit->members; + } + + public function __get(string $name): mixed { + return $this->members[$name]; + } + + public function __set(string $name, mixed $value): void { + $this->members[$name] = $value; + } + + public function __isset(string $name): bool { + return isset($this->members[$name]); + } + + public function __unset(string $name): void { + unset($this->members[$name]); + } + + public function __call(string $name, array $args): mixed { + return ($this->members[$name])(...$args); + } +} diff --git a/src/Templating/TemplateVars.php b/src/Templating/TemplateVars.php new file mode 100644 index 0000000..0b4fcf8 --- /dev/null +++ b/src/Templating/TemplateVars.php @@ -0,0 +1,32 @@ +vars; + if($this->parent !== null) + $vars += $this->parent->getVars(); + return $vars; + } + + public function getVar(string $name, mixed $default = null): mixed { + if(isset($this->vars[$name])) + return $this->vars[$name]; + if($this->parent !== null) + return $this->parent->getVar($name, $default); + return $default; + } + + public function setVar(string $name, mixed $value): void { + $this->vars[$name] = $value; + } + + public function removeVar(string $name): void { + unset($this->vars[$name]); + } +} diff --git a/templates/errors/400.php b/templates/errors/400.php new file mode 100644 index 0000000..c761851 --- /dev/null +++ b/templates/errors/400.php @@ -0,0 +1,5 @@ +extends('errors/master'); + +$self->http_error_title = 'Error 400'; +$self->http_error_desc = 'Request is malformed.'; diff --git a/templates/errors/401.php b/templates/errors/401.php new file mode 100644 index 0000000..91bfdb4 --- /dev/null +++ b/templates/errors/401.php @@ -0,0 +1,5 @@ +extends('errors/master'); + +$self->http_error_title = 'Error 401'; +$self->http_error_desc = 'You must be authorised to be here.'; diff --git a/templates/errors/403.php b/templates/errors/403.php new file mode 100644 index 0000000..f77a0a5 --- /dev/null +++ b/templates/errors/403.php @@ -0,0 +1,5 @@ +extends('errors/master'); + +$self->http_error_title = 'Error 403'; +$self->http_error_desc = 'You are not authorised to be here.'; diff --git a/templates/errors/404.php b/templates/errors/404.php new file mode 100644 index 0000000..aca80b9 --- /dev/null +++ b/templates/errors/404.php @@ -0,0 +1,5 @@ +extends('errors/master'); + +$self->http_error_title = 'Error 404'; +$self->http_error_desc = 'Could not find what you were looking for.'; diff --git a/templates/errors/500.html b/templates/errors/500.html new file mode 100644 index 0000000..3ffe87c --- /dev/null +++ b/templates/errors/500.html @@ -0,0 +1,18 @@ + + + + + Error 500 + + + +
+

Error 500

+

Something went horrendously wrong! Please report this if the error persists.

+
+ + diff --git a/templates/errors/503.html b/templates/errors/503.html new file mode 100644 index 0000000..7116aa9 --- /dev/null +++ b/templates/errors/503.html @@ -0,0 +1,18 @@ + + + + + Error 503 + + + +
+

Under Construction

+ +
+ + diff --git a/templates/errors/master.php b/templates/errors/master.php new file mode 100644 index 0000000..8b0f7d5 --- /dev/null +++ b/templates/errors/master.php @@ -0,0 +1,9 @@ +extends('master'); + +$self->block('body', function($self) { +?> +

http_error_title ?? 'Unknown Error');?>

+

http_error_desc ?? 'No additional information is available.');?>

+extends('master'); + +$self->block('body', function($self) { +?> +
+

Under Construction

+ +
+ + + + + + <?=$self->hau->getSiteName();?> + + + getBlock('body');?> + +