This commit is contained in:
2022-08-20 22:17:17 +02:00
parent 58c905acf5
commit 35d7e4000a
29 changed files with 619 additions and 131 deletions

View File

@@ -6,6 +6,8 @@ use Api\Parameter\Parameter;
abstract class AbstractRoute {
const PARAMETER_PATTERN = "/^{([^:]+)(:(.*?)(\?)?)?}$/";
private string $pattern;
private bool $exact;
@@ -64,7 +66,7 @@ abstract class AbstractRoute {
$params = [];
for (; $patternOffset < $countPattern; $patternOffset++) {
if (!preg_match("/^{([^:]+)(:(.*?)(\?)?)?}$/", $patternParts[$patternOffset], $match)) {
if (!preg_match(self::PARAMETER_PATTERN, $patternParts[$patternOffset], $match)) {
// not a parameter? check if it matches
if ($urlOffset >= $countUrl || $urlParts[$urlOffset] !== $patternParts[$patternOffset]) {
@@ -122,4 +124,30 @@ abstract class AbstractRoute {
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

@@ -21,7 +21,7 @@ class ApiRoute extends AbstractRoute {
if (empty($params["endpoint"])) {
header("Content-Type: text/html");
$document = new \Elements\TemplateDocument($router, "swagger.twig");
return $document->getCode();
return $document->load();
} else if (!preg_match("/[a-zA-Z]+/", $params["endpoint"])) {
http_response_code(400);
$response = createError("Invalid Method");

View File

@@ -3,10 +3,15 @@
namespace Objects\Router;
use Elements\Document;
use Objects\Context;
use Objects\Search\Searchable;
use Objects\Search\SearchQuery;
use ReflectionException;
class DocumentRoute extends AbstractRoute {
use Searchable;
private string $className;
private array $args;
private ?\ReflectionClass $reflectionClass;
@@ -31,7 +36,7 @@ class DocumentRoute extends AbstractRoute {
}
} catch (ReflectionException $exception) {
$this->reflectionClass = null;
return false;
throw $exception;
}
$this->reflectionClass = null;
@@ -55,16 +60,32 @@ class DocumentRoute extends AbstractRoute {
}
public function call(Router $router, array $params): string {
if (!$this->loadClass()) {
return $router->returnStatusCode(500, [ "message" => "Error loading class: $this->className"]);
}
try {
$args = array_merge([$router], $this->args);
if (!$this->loadClass()) {
return $router->returnStatusCode(500, [ "message" => "Error loading class: $this->className"]);
}
$args = array_merge([$router], $this->args, $params);
$document = $this->reflectionClass->newInstanceArgs($args);
return $document->getCode($params);
return $document->load($params);
} catch (\ReflectionException $e) {
return $router->returnStatusCode(500, [ "message" => "Error loading class $this->className: " . $e->getMessage()]);
}
}
public function doSearch(Context $context, SearchQuery $query): array {
try {
if ($this->loadClass()) {
$args = array_merge([$context->router], $this->args);
$document = $this->reflectionClass->newInstanceArgs($args);
if ($document->isSearchable()) {
return $document->doSearch($query, $this);
}
}
return [];
} catch (\ReflectionException) {
return [];
}
}
}

View File

@@ -9,6 +9,8 @@ class Router {
private Context $context;
private Logger $logger;
private ?AbstractRoute $activeRoute;
private ?string $requestedUri;
protected array $routes;
protected array $statusCodeRoutes;
@@ -16,6 +18,9 @@ class Router {
$this->context = $context;
$this->routes = [];
$this->statusCodeRoutes = [];
$this->activeRoute = null;
$this->requestedUri = null;
$this->context->router = $this;
$sql = $context->getSQL();
if ($sql) {
@@ -26,14 +31,24 @@ class Router {
}
}
public function getActiveRoute(): ?AbstractRoute {
return $this->activeRoute;
}
public function getRequestedUri(): ?string {
return $this->requestedUri;
}
public function run(string $url): string {
// TODO: do we want a global try cache and return status page 500 on any error?
$this->requestedUri = $url;
$url = strtok($url, "?");
foreach ($this->routes as $route) {
$pathParams = $route->match($url);
if ($pathParams !== false) {
$this->activeRoute = $route;
return $route->call($this, $pathParams);
}
}
@@ -54,7 +69,6 @@ class Router {
if ($res) {
return $req->getResult()["html"];
} else {
var_dump($req->getLastError());
$description = htmlspecialchars($params["status_description"]);
return "<b>$code - $description</b>";
}
@@ -62,11 +76,11 @@ class Router {
}
public function addRoute(AbstractRoute $route) {
if (preg_match("/^\d+$/", $route->getPattern())) {
$this->statusCodeRoutes[$route->getPattern()] = $route;
} else {
$this->routes[] = $route;
if (preg_match("/^\/(\d+)$/", $route->getPattern(), $re)) {
$this->statusCodeRoutes[$re[1]] = $route;
}
$this->routes[] = $route;
}
public function writeCache(string $file): bool {
@@ -134,4 +148,15 @@ class RouterCache extends Router {
// strip leading slash
return preg_replace("/^\/+/", "", $url);
}
public function getRoutes(bool $includeStatusRoutes = false): array {
if (!$includeStatusRoutes && !empty($this->statusCodeRoutes)) {
return array_filter($this->routes, function ($route) {
return !in_array($route, $this->statusCodeRoutes);
});
}
return $this->routes;
}
}

View File

@@ -2,8 +2,15 @@
namespace Objects\Router;
use Objects\Context;
use Objects\Search\Searchable;
use Objects\Search\SearchQuery;
use Objects\Search\SearchResult;
class StaticFileRoute extends AbstractRoute {
use Searchable;
private string $path;
private int $code;
@@ -15,7 +22,7 @@ class StaticFileRoute extends AbstractRoute {
public function call(Router $router, array $params): string {
http_response_code($this->code);
$this->serveStatic($this->path, $router);
$this->serveStatic($this->getAbsolutePath(), $router);
return "";
}
@@ -23,9 +30,11 @@ class StaticFileRoute extends AbstractRoute {
return array_merge(parent::getArgs(), [$this->path, $this->code]);
}
public static function serveStatic(string $path, ?Router $router = null) {
public function getAbsolutePath(): string {
return WEBROOT . DIRECTORY_SEPARATOR . $this->path;
}
$path = realpath(WEBROOT . DIRECTORY_SEPARATOR . $path);
public static function serveStatic(string $path, ?Router $router = null) {
if (!startsWith($path, WEBROOT . DIRECTORY_SEPARATOR)) {
http_response_code(406);
echo "<b>Access restricted, requested file outside web root:</b> " . htmlspecialchars($path);
@@ -72,4 +81,34 @@ class StaticFileRoute extends AbstractRoute {
downloadFile($handle, $offset, $length);
}
}
public function doSearch(Context $context, SearchQuery $query): array {
$results = [];
$path = $this->getAbsolutePath();
if (is_file($path) && is_readable($path)) {
$pathInfo = pathinfo($path);
$extension = $pathInfo["extension"] ?? "";
$fileName = $pathInfo["filename"] ?? "";
if ($context->getSettings()->isExtensionAllowed($extension)) {
$mimeType = mime_content_type($path);
if (startsWith($mimeType, "text/")) {
$document = @file_get_contents($path);
if ($document) {
if ($mimeType === "text/html") {
$results = Searchable::searchHtml($document, $query);
} else {
$results = Searchable::searchText($document, $query);
}
}
}
}
$results = array_map(function ($res) use ($fileName) {
return new SearchResult($this->getPattern(), $fileName, $res["text"]);
}, $results);
}
return $results;
}
}