DB Entity: Inheriting/Extending

This commit is contained in:
Roman Hergenreder
2022-11-27 12:33:27 +01:00
parent 3b2b5984d6
commit 26a22f5299
20 changed files with 308 additions and 157 deletions

View File

@@ -1,157 +0,0 @@
<?php
namespace Core\Objects\Router;
use Core\API\Parameter\Parameter;
abstract class AbstractRoute {
const PARAMETER_PATTERN = "/^{([^:]+)(:(.*?)(\?)?)?}$/";
private string $pattern;
private bool $exact;
public function __construct(string $pattern, bool $exact = true) {
$this->pattern = $pattern;
$this->exact = $exact;
}
private static function parseParamType(?string $type): ?int {
if ($type === null || trim($type) === "") {
return null;
}
$type = strtolower(trim($type));
if (in_array($type, ["int", "integer"])) {
return Parameter::TYPE_INT;
} else if (in_array($type, ["float", "double"])) {
return Parameter::TYPE_FLOAT;
} else if (in_array($type, ["bool", "boolean"])) {
return Parameter::TYPE_BOOLEAN;
} else {
return Parameter::TYPE_STRING;
}
}
public function getPattern(): string {
return $this->pattern;
}
public abstract function call(Router $router, array $params): string;
protected function getArgs(): array {
return [$this->pattern, $this->exact];
}
public function getClass(): \ReflectionClass {
return new \ReflectionClass($this);
}
public function generateCache(): string {
$reflection = $this->getClass();
$className = $reflection->getShortName();
$args = implode(", ", array_map(function ($arg) {
return var_export($arg, true);
}, $this->getArgs()));
return "new $className($args)";
}
public function match(string $url) {
# /test/{abc}/{param:?}/{xyz:int}/{aaa:int?}
$patternParts = explode("/", Router::cleanURL($this->pattern, false));
$countPattern = count($patternParts);
$patternOffset = 0;
# /test/param/optional/123
$urlParts = explode("/", Router::cleanURL($url));
$countUrl = count($urlParts);
$urlOffset = 0;
$params = [];
for (; $patternOffset < $countPattern; $patternOffset++) {
if (!preg_match(self::PARAMETER_PATTERN, $patternParts[$patternOffset], $match)) {
// not a parameter? check if it matches
if ($urlOffset >= $countUrl || $urlParts[$urlOffset] !== $patternParts[$patternOffset]) {
return false;
}
$urlOffset++;
} else {
// we got a parameter here
$paramName = $match[1];
if (isset($match[2])) {
$paramType = self::parseParamType($match[3]) ?? Parameter::TYPE_MIXED;
$paramOptional = !empty($match[4] ?? null);
} else {
$paramType = Parameter::TYPE_MIXED;
$paramOptional = false;
}
$parameter = new Parameter($paramName, $paramType, $paramOptional);
if ($urlOffset >= $countUrl || $urlParts[$urlOffset] === "") {
if ($parameter->optional) {
$value = $urlParts[$urlOffset] ?? null;
if ($value === null || $value === "") {
$params[$paramName] = null;
} else {
if (!$parameter->parseParam($value)) {
return false;
} else {
$params[$paramName] = $parameter->value;
}
}
if ($urlOffset < $countUrl) {
$urlOffset++;
}
} else {
return false;
}
} else {
$value = $urlParts[$urlOffset];
if (!$parameter->parseParam($value)) {
return false;
} else {
$params[$paramName] = $parameter->value;
$urlOffset++;
}
}
}
}
if ($urlOffset !== $countUrl && $this->exact) {
return false;
}
return $params;
}
public function getUrl(array $parameters = []): string {
$patternParts = explode("/", Router::cleanURL($this->pattern, false));
foreach ($patternParts as $i => $part) {
if (preg_match(self::PARAMETER_PATTERN, $part, $match)) {
$paramName = $match[1];
$patternParts[$i] = $parameters[$paramName] ?? null;
}
}
return "/" . implode("/", array_filter($patternParts));
}
public function getParameterNames(): array {
$parameterNames = [];
$patternParts = explode("/", Router::cleanURL($this->pattern, false));
foreach ($patternParts as $part) {
if (preg_match(self::PARAMETER_PATTERN, $part, $match)) {
$parameterNames[] = $match[1];
}
}
return $parameterNames;
}
}

View File

@@ -4,13 +4,14 @@ namespace Core\Objects\Router;
use Core\API\Request;
use Core\Elements\TemplateDocument;
use Core\Objects\DatabaseEntity\Route;
use ReflectionClass;
use ReflectionException;
class ApiRoute extends AbstractRoute {
class ApiRoute extends Route {
public function __construct() {
parent::__construct("/api/{endpoint:?}/{method:?}", false);
parent::__construct("API", "/api/{endpoint:?}/{method:?}", false);
}
private static function checkClass(string $className): bool {

View File

@@ -2,34 +2,44 @@
namespace Core\Objects\Router;
use Core\Driver\SQL\SQL;
use Core\Elements\Document;
use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Route;
use Core\Objects\Search\Searchable;
use Core\Objects\Search\SearchQuery;
use JetBrains\PhpStorm\Pure;
use ReflectionException;
class DocumentRoute extends AbstractRoute {
class DocumentRoute extends Route {
use Searchable;
private string $className;
private array $args;
private ?\ReflectionClass $reflectionClass;
private ?\ReflectionClass $reflectionClass = null;
public function __construct(string $pattern, bool $exact, string $className, ...$args) {
parent::__construct($pattern, $exact);
$this->className = $className;
parent::__construct("dynamic", $pattern, $className, $exact);
$this->args = $args;
$this->reflectionClass = null;
$this->extra = json_encode($args);
}
public function postFetch(SQL $sql, array $row) {
parent::postFetch($sql, $row);
$this->args = json_decode($this->extra);
}
#[Pure] private function getClassName(): string {
return $this->getTarget();
}
private function loadClass(): bool {
if ($this->reflectionClass === null) {
try {
$file = getClassPath($this->className);
$file = getClassPath($this->getClassName());
if (file_exists($file)) {
$this->reflectionClass = new \ReflectionClass($this->className);
$this->reflectionClass = new \ReflectionClass($this->getClassName());
if ($this->reflectionClass->isSubclassOf(Document::class)) {
return true;
}
@@ -56,20 +66,22 @@ class DocumentRoute extends AbstractRoute {
}
protected function getArgs(): array {
return array_merge(parent::getArgs(), [$this->className], $this->args);
return array_merge(parent::getArgs(), [$this->getClassName()], $this->args);
}
public function call(Router $router, array $params): string {
$className = $this->getClassName();
try {
if (!$this->loadClass()) {
return $router->returnStatusCode(500, [ "message" => "Error loading class: $this->className"]);
return $router->returnStatusCode(500, [ "message" => "Error loading class: $className"]);
}
$args = array_merge([$router], $this->args, $params);
$document = $this->reflectionClass->newInstanceArgs($args);
return $document->load($params);
} catch (\ReflectionException $e) {
return $router->returnStatusCode(500, [ "message" => "Error loading class $this->className: " . $e->getMessage()]);
return $router->returnStatusCode(500, [ "message" => "Error loading class $className: " . $e->getMessage()]);
}
}

View File

@@ -2,10 +2,12 @@
namespace Core\Objects\Router;
class EmptyRoute extends AbstractRoute {
use Core\Objects\DatabaseEntity\Route;
class EmptyRoute extends Route {
public function __construct(string $pattern, bool $exact = true) {
parent::__construct($pattern, $exact);
parent::__construct("empty", $pattern, $exact);
}
public function call(Router $router, array $params): string {

View File

@@ -0,0 +1,9 @@
<?php
namespace Core\Objects\Router;
class RedirectPermanentlyRoute extends RedirectRoute {
public function __construct(string $pattern, bool $exact, string $destination) {
parent::__construct("redirect_permanently", $pattern, $exact, $destination, 308);
}
}

View File

@@ -2,24 +2,29 @@
namespace Core\Objects\Router;
class RedirectRoute extends AbstractRoute {
use Core\Objects\DatabaseEntity\Route;
use JetBrains\PhpStorm\Pure;
class RedirectRoute extends Route {
private string $destination;
private int $code;
public function __construct(string $pattern, bool $exact, string $destination, int $code = 307) {
parent::__construct($pattern, $exact);
$this->destination = $destination;
public function __construct(string $type, string $pattern, bool $exact, string $destination, int $code = 307) {
parent::__construct($type, $pattern, $destination, $exact);
$this->code = $code;
}
#[Pure] private function getDestination(): string {
return $this->getTarget();
}
public function call(Router $router, array $params): string {
header("Location: $this->destination");
header("Location: " . $this->getDestination());
http_response_code($this->code);
return "";
}
protected function getArgs(): array {
return array_merge(parent::getArgs(), [$this->destination, $this->code]);
return array_merge(parent::getArgs(), [$this->getDestination(), $this->code]);
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Core\Objects\Router;
class RedirectTemporaryRoute extends RedirectRoute {
public function __construct(string $pattern, bool $exact, string $destination) {
parent::__construct("redirect_temporary", $pattern, $exact, $destination, 307);
}
}

View File

@@ -4,12 +4,13 @@ namespace Core\Objects\Router;
use Core\Driver\Logger\Logger;
use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Route;
class Router {
private Context $context;
private Logger $logger;
private ?AbstractRoute $activeRoute;
private ?Route $activeRoute;
private ?string $requestedUri;
protected array $routes;
protected array $statusCodeRoutes;
@@ -31,7 +32,7 @@ class Router {
}
}
public function getActiveRoute(): ?AbstractRoute {
public function getActiveRoute(): ?Route {
return $this->activeRoute;
}
@@ -75,7 +76,7 @@ class Router {
}
}
public function addRoute(AbstractRoute $route) {
public function addRoute(Route $route) {
if (preg_match("/^\/(\d+)$/", $route->getPattern(), $re)) {
$this->statusCodeRoutes[$re[1]] = $route;
}

View File

@@ -2,22 +2,29 @@
namespace Core\Objects\Router;
use Core\Driver\SQL\SQL;
use Core\Objects\Context;
use Core\Objects\DatabaseEntity\Route;
use Core\Objects\Search\Searchable;
use Core\Objects\Search\SearchQuery;
use Core\Objects\Search\SearchResult;
use JetBrains\PhpStorm\Pure;
class StaticFileRoute extends AbstractRoute {
class StaticFileRoute extends Route {
use Searchable;
private string $path;
private int $code;
public function __construct(string $pattern, bool $exact, string $path, int $code = 200) {
parent::__construct($pattern, $exact);
$this->path = $path;
parent::__construct("static", $pattern, $path, $exact);
$this->code = $code;
$this->extra = json_encode($this->code);
}
public function postFetch(SQL $sql, array $row) {
parent::postFetch($sql, $row);
$this->code = json_decode($this->extra);
}
public function call(Router $router, array $params): string {
@@ -26,12 +33,16 @@ class StaticFileRoute extends AbstractRoute {
return "";
}
#[Pure] private function getPath(): string {
return $this->getTarget();
}
protected function getArgs(): array {
return array_merge(parent::getArgs(), [$this->path, $this->code]);
return array_merge(parent::getArgs(), [$this->getPath(), $this->code]);
}
public function getAbsolutePath(): string {
return WEBROOT . DIRECTORY_SEPARATOR . $this->path;
return WEBROOT . DIRECTORY_SEPARATOR . $this->getPath();
}
public static function serveStatic(string $path, ?Router $router = null) {

View File

@@ -2,13 +2,15 @@
namespace Core\Objects\Router;
class StaticRoute extends AbstractRoute {
use Core\Objects\DatabaseEntity\Route;
class StaticRoute extends Route {
private string $data;
private int $code;
public function __construct(string $pattern, bool $exact, string $data, int $code = 200) {
parent::__construct($pattern, $exact);
parent::__construct("static", $pattern, $exact);
$this->data = $data;
$this->code = $code;
}