mirror of
https://github.com/openvk/chandler.git
synced 2025-03-31 21:43:59 +03:00
Reformatting the Router class.
This commit is contained in:
parent
c89cd500a1
commit
5d7b131240
1 changed files with 165 additions and 197 deletions
|
@ -1,298 +1,266 @@
|
||||||
<?php declare(strict_types=1);
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
namespace Chandler\MVC\Routing;
|
namespace Chandler\MVC\Routing;
|
||||||
use Chandler\Patterns\TSimpleSingleton;
|
|
||||||
use Chandler\Eventing\EventDispatcher;
|
use Chandler\Eventing\EventDispatcher;
|
||||||
use Chandler\Session\Session;
|
|
||||||
use Chandler\MVC\Exceptions\InterruptedException;
|
use Chandler\MVC\Exceptions\InterruptedException;
|
||||||
use Chandler\MVC\IPresenter;
|
use Chandler\MVC\IPresenter;
|
||||||
|
use Chandler\Patterns\TSimpleSingleton;
|
||||||
|
use Chandler\Session\Session;
|
||||||
use Nette\DI;
|
use Nette\DI;
|
||||||
|
use Nette\DI\Config\Adapters\NeonAdapter;
|
||||||
|
use Nette\DI\Config\Loader;
|
||||||
|
use SodiumException;
|
||||||
|
|
||||||
class Router
|
class Router
|
||||||
{
|
{
|
||||||
|
const ALIAS_REGEX = "%{(\??\!?([A-z]++))}%";
|
||||||
|
|
||||||
const HANDLER_DELIMITER = "%([#@❤]|\->)%";
|
const HANDLER_DELIMITER = "%([#@❤]|\->)%";
|
||||||
const ALIAS_REGEX = "%{(\??\!?([A-z]++))}%";
|
|
||||||
|
|
||||||
private $url = NULL;
|
|
||||||
private $routes = [];
|
|
||||||
private $statics = [];
|
|
||||||
private $scope = [];
|
|
||||||
|
|
||||||
private $events;
|
private $events;
|
||||||
|
|
||||||
|
private $routes = [];
|
||||||
|
|
||||||
|
private $scope = [];
|
||||||
|
|
||||||
|
private $statics = [];
|
||||||
|
|
||||||
|
private $url = null;
|
||||||
|
|
||||||
protected $types = [
|
protected $types = [
|
||||||
"num" => "(-?\d++)",
|
"num" => "(-?\d++)",
|
||||||
"text" => "([A-z0-9]++)",
|
"text" => "([A-z0-9]++)",
|
||||||
"slug" => "([A-z0-9А-я\-_ ]++)",
|
"slug" => "([A-z0-9А-я\-_ ]++)",
|
||||||
];
|
];
|
||||||
|
|
||||||
private function __construct()
|
private function computeRegExp(string $route, array $customAliases = [], ?string $prefix = null): string
|
||||||
{
|
{
|
||||||
$this->events = EventDispatcher::i();
|
$regexp = preg_replace_callback(Router::ALIAS_REGEX, function ($matches) use ($customAliases) {
|
||||||
}
|
if ($matches[1][0] === "?") {
|
||||||
|
|
||||||
private function computeRegExp(string $route, array $customAliases = [], ?string $prefix = NULL): string
|
|
||||||
{
|
|
||||||
$regexp = preg_replace_callback(Router::ALIAS_REGEX, function($matches) use ($customAliases) {
|
|
||||||
if($matches[1][0] === "?") {
|
|
||||||
$replacement = !isset($customAliases[$matches[2]])
|
$replacement = !isset($customAliases[$matches[2]])
|
||||||
? NULL
|
? null
|
||||||
: ($matches[1][1] !== "!" ? "(" : "(?:") . $customAliases[$matches[2]] . ")";
|
: ($matches[1][1] !== "!" ? "(" : "(?:") . $customAliases[$matches[2]] . ")";
|
||||||
} else {
|
} else {
|
||||||
$replacement = $this->types[$matches[1]];
|
$replacement = $this->types[$matches[1]];
|
||||||
}
|
}
|
||||||
|
if (!$replacement) {
|
||||||
if(!$replacement) {
|
$exMessage = "Unknown type alias: $matches[1].";
|
||||||
$exMessage = "Unknown type alias: $matches[1].";
|
|
||||||
$exMessage .= " (Available options are: " . implode(", ", array_keys($this->types));
|
$exMessage .= " (Available options are: " . implode(", ", array_keys($this->types));
|
||||||
if(sizeof($customAliases) > 0)
|
if (sizeof($customAliases) > 0)
|
||||||
$exMessage .= " or any of these user-defined aliases: " . implode(", ", array_keys($customAliases)) . ")";
|
$exMessage .= " or any of these user-defined aliases: " . implode(", ", array_keys($customAliases)) . ")";
|
||||||
else
|
else
|
||||||
$exMessage .= ")";
|
$exMessage .= ")";
|
||||||
|
|
||||||
throw new Exceptions\UnknownTypeAliasException($exMessage);
|
throw new Exceptions\UnknownTypeAliasException($exMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $replacement;
|
return $replacement;
|
||||||
}, addslashes($route));
|
}, addslashes($route));
|
||||||
|
if (!is_null($prefix)) {
|
||||||
if(!is_null($prefix)) {
|
|
||||||
$regexp = "\\/$prefix\\" . ($route === "/" ? "/" : "/$regexp");
|
$regexp = "\\/$prefix\\" . ($route === "/" ? "/" : "/$regexp");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "%^$regexp$%";
|
return "%^$regexp$%";
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeCSRFToken(Route $route, string $nonce): string
|
|
||||||
{
|
|
||||||
$key = hash("snefru", CHANDLER_ROOT_CONF["security"]["secret"] . bin2hex($nonce));
|
|
||||||
|
|
||||||
$data = $route->namespace;
|
|
||||||
$data .= Session::i()->get("tok", -1);
|
|
||||||
|
|
||||||
return hash_hmac("snefru", $data, $key) . "#" . bin2hex($nonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setCSRFStatus(Route $route): void
|
|
||||||
{
|
|
||||||
if(CHANDLER_ROOT_CONF["security"]["csrfProtection"] === "disabled") {
|
|
||||||
$GLOBALS["csrfCheck"] = true;
|
|
||||||
} else {
|
|
||||||
$GLOBALS["csrfCheck"] = false;
|
|
||||||
|
|
||||||
$hash = ($_GET["hash"] ?? ($_POST["hash"] ?? false));
|
|
||||||
if($hash !== false) {
|
|
||||||
$data = explode("#", $hash);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if(!isset($data[0]) || !isset($data[1])) throw new \SodiumException;
|
|
||||||
[$hash, $nonce] = $data;
|
|
||||||
|
|
||||||
if(sodium_memcmp($this->makeCSRFToken($route, hex2bin($nonce)), "$hash#$nonce") === 0) {
|
|
||||||
if(CHANDLER_ROOT_CONF["security"]["csrfProtection"] === "permissive")
|
|
||||||
$GLOBALS["csrfCheck"] = true;
|
|
||||||
else if(CHANDLER_ROOT_CONF["security"]["csrfProtection"] === "strict")
|
|
||||||
$GLOBALS["csrfCheck"] = parse_url($_SERVER["HTTP_REFERER"], PHP_URL_HOST) === $_SERVER["HTTP_HOST"];
|
|
||||||
else
|
|
||||||
trigger_error("Bad value for chandler.security.csrfProtection: disabled, permissive or strict expected.", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
} catch(\SodiumException $ex) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$GLOBALS["csrfToken"] = $this->makeCSRFToken($route, openssl_random_pseudo_bytes(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getDI(string $namespace): DI\Container
|
|
||||||
{
|
|
||||||
$loader = new DI\ContainerLoader(CHANDLER_ROOT . "/tmp/cache/di_$namespace", true);
|
|
||||||
$class = $loader->load(function($compiler) use ($namespace) {
|
|
||||||
$fileLoader = new \Nette\DI\Config\Loader;
|
|
||||||
$fileLoader->addAdapter("yml", \Nette\DI\Config\Adapters\NeonAdapter::class);
|
|
||||||
|
|
||||||
$compiler->loadConfig(CHANDLER_EXTENSIONS_ENABLED . "/$namespace/Web/di.yml", $fileLoader);
|
|
||||||
});
|
|
||||||
|
|
||||||
return new $class;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getPresenter(string $namespace, string $presenterName): ?IPresenter
|
|
||||||
{
|
|
||||||
$di = $this->getDI($namespace);
|
|
||||||
|
|
||||||
$services = $di->findByType("\\$namespace\\Web\\Presenters\\$presenterName" . "Presenter", false);
|
|
||||||
return $di->getService($services[0], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function delegateView(string $filename, IPresenter $presenter): string
|
|
||||||
{
|
|
||||||
return $presenter->getTemplatingEngine()->renderToString($filename, $this->scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function delegateController(string $namespace, string $presenterName, string $action, array $parameters = []): string
|
private function delegateController(string $namespace, string $presenterName, string $action, array $parameters = []): string
|
||||||
{
|
{
|
||||||
$presenter = $this->getPresenter($namespace, $presenterName);
|
$presenter = $this->getPresenter($namespace, $presenterName);
|
||||||
$action = ucfirst($action);
|
$action = ucfirst($action);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$presenter->onStartup();
|
$presenter->onStartup();
|
||||||
$presenter->{"render$action"}(...$parameters);
|
$presenter->{"render$action"}(...$parameters);
|
||||||
$presenter->onBeforeRender();
|
$presenter->onBeforeRender();
|
||||||
|
|
||||||
$this->scope += array_merge_recursive($presenter->getTemplateScope(), []); #TODO: add default parameters
|
$this->scope += array_merge_recursive($presenter->getTemplateScope(), []); #TODO: add default parameters
|
||||||
#TODO: move this to delegateView
|
#TODO: move this to delegateView
|
||||||
|
|
||||||
$tpl = $this->scope["_template"] ?? "$presenterName/$action.xml";
|
$tpl = $this->scope["_template"] ?? "$presenterName/$action.xml";
|
||||||
if($tpl[0] !== "/") {
|
if ($tpl[0] !== "/") {
|
||||||
$dir = CHANDLER_EXTENSIONS_ENABLED . "/$namespace/Web/Presenters/templates";
|
$dir = CHANDLER_EXTENSIONS_ENABLED . "/$namespace/Web/Presenters/templates";
|
||||||
$tpl = "$dir/$tpl";
|
$tpl = "$dir/$tpl";
|
||||||
if(isset($this->scope["_templatePath"]))
|
if (isset($this->scope["_templatePath"]))
|
||||||
$tpl = str_replace($dir, $this->scope["_templatePath"], $tpl);
|
$tpl = str_replace($dir, $this->scope["_templatePath"], $tpl);
|
||||||
}
|
}
|
||||||
|
if (!file_exists($tpl)) {
|
||||||
if(!file_exists($tpl)) {
|
|
||||||
trigger_error("Could not open $tpl as template, falling back.", E_USER_NOTICE);
|
trigger_error("Could not open $tpl as template, falling back.", E_USER_NOTICE);
|
||||||
$tpl = "$presenterName/$action.xml";
|
$tpl = "$presenterName/$action.xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
$output = $this->delegateView($tpl, $presenter);
|
$output = $this->delegateView($tpl, $presenter);
|
||||||
|
|
||||||
$presenter->onAfterRender();
|
$presenter->onAfterRender();
|
||||||
} catch(InterruptedException $ex) {}
|
} catch (InterruptedException $ex) {
|
||||||
|
}
|
||||||
$presenter->onStop();
|
$presenter->onStop();
|
||||||
$presenter->onDestruction();
|
$presenter->onDestruction();
|
||||||
$presenter = NULL;
|
$presenter = null;
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function delegateRoute(Route $route, array $matches): string
|
private function delegateRoute(Route $route, array $matches): string
|
||||||
{
|
{
|
||||||
$parameters = [];
|
$parameters = [];
|
||||||
|
foreach ($matches as $param)
|
||||||
foreach($matches as $param)
|
$parameters[] = is_numeric($param) ? (int)$param : $param;
|
||||||
$parameters[] = is_numeric($param) ? (int) $param : $param;
|
|
||||||
|
|
||||||
$this->setCSRFStatus($route);
|
$this->setCSRFStatus($route);
|
||||||
return $this->delegateController($route->namespace, $route->presenter, $route->action, $parameters);
|
return $this->delegateController($route->namespace, $route->presenter, $route->action, $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function delegateView(string $filename, IPresenter $presenter): string
|
||||||
|
{
|
||||||
|
return $presenter->getTemplatingEngine()->renderToString($filename, $this->scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDI(string $namespace): DI\Container
|
||||||
|
{
|
||||||
|
$loader = new DI\ContainerLoader(CHANDLER_ROOT . "/tmp/cache/di_$namespace", true);
|
||||||
|
$class = $loader->load(function ($compiler) use ($namespace) {
|
||||||
|
$fileLoader = new Loader;
|
||||||
|
$fileLoader->addAdapter("yml", NeonAdapter::class);
|
||||||
|
$compiler->loadConfig(CHANDLER_EXTENSIONS_ENABLED . "/$namespace/Web/di.yml", $fileLoader);
|
||||||
|
});
|
||||||
|
return new $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPresenter(string $namespace, string $presenterName): ?IPresenter
|
||||||
|
{
|
||||||
|
$di = $this->getDI($namespace);
|
||||||
|
$services = $di->findByType("\\$namespace\\Web\\Presenters\\$presenterName" . "Presenter", false);
|
||||||
|
return $di->getService($services[0], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setCSRFStatus(Route $route): void
|
||||||
|
{
|
||||||
|
if (CHANDLER_ROOT_CONF["security"]["csrfProtection"] === "disabled") {
|
||||||
|
$GLOBALS["csrfCheck"] = true;
|
||||||
|
} else {
|
||||||
|
$GLOBALS["csrfCheck"] = false;
|
||||||
|
$hash = ($_GET["hash"] ?? ($_POST["hash"] ?? false));
|
||||||
|
if ($hash !== false) {
|
||||||
|
$data = explode("#", $hash);
|
||||||
|
try {
|
||||||
|
if (!isset($data[0]) || !isset($data[1])) throw new SodiumException;
|
||||||
|
[$hash, $nonce] = $data;
|
||||||
|
if (sodium_memcmp($this->makeCSRFToken($route, hex2bin($nonce)), "$hash#$nonce") === 0) {
|
||||||
|
if (CHANDLER_ROOT_CONF["security"]["csrfProtection"] === "permissive")
|
||||||
|
$GLOBALS["csrfCheck"] = true;
|
||||||
|
else if (CHANDLER_ROOT_CONF["security"]["csrfProtection"] === "strict")
|
||||||
|
$GLOBALS["csrfCheck"] = parse_url($_SERVER["HTTP_REFERER"], PHP_URL_HOST) === $_SERVER["HTTP_HOST"];
|
||||||
|
else
|
||||||
|
trigger_error("Bad value for chandler.security.csrfProtection: disabled, permissive or strict expected.", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
} catch (SodiumException $ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$GLOBALS["csrfToken"] = $this->makeCSRFToken($route, openssl_random_pseudo_bytes(4));
|
||||||
|
}
|
||||||
|
|
||||||
function delegateStatic(string $namespace, string $path): string
|
function delegateStatic(string $namespace, string $path): string
|
||||||
{
|
{
|
||||||
$static = $static = $this->statics[$namespace];
|
$static = $static = $this->statics[$namespace];
|
||||||
if(!isset($static)) return "Fatal error: no route";
|
if (!isset($static)) return "Fatal error: no route";
|
||||||
|
if (!file_exists($file = "$static/$path"))
|
||||||
if(!file_exists($file = "$static/$path"))
|
|
||||||
return "Fatal error: no resource";
|
return "Fatal error: no resource";
|
||||||
|
|
||||||
$hash = "W/\"" . hash_file("snefru", $file) . "\"";
|
$hash = "W/\"" . hash_file("snefru", $file) . "\"";
|
||||||
if(isset($_SERVER["HTTP_IF_NONE_MATCH"]))
|
if (isset($_SERVER["HTTP_IF_NONE_MATCH"]))
|
||||||
if($_SERVER["HTTP_IF_NONE_MATCH"] === $hash)
|
if ($_SERVER["HTTP_IF_NONE_MATCH"] === $hash)
|
||||||
exit(header("HTTP/1.1 304"));
|
exit(header("HTTP/1.1 304"));
|
||||||
|
|
||||||
header("Content-Type: " . system_extension_mime_type($file) ?? "text/plain; charset=unknown-8bit");
|
header("Content-Type: " . system_extension_mime_type($file) ?? "text/plain; charset=unknown-8bit");
|
||||||
header("Content-Size: " . filesize($file));
|
header("Content-Size: " . filesize($file));
|
||||||
header("ETag: $hash");
|
header("ETag: $hash");
|
||||||
|
|
||||||
readfile($file);
|
readfile($file);
|
||||||
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reverse(string $hotlink, ...$parameters): ?string
|
function execute(string $url, ?string $parentModule = null): ?string
|
||||||
{
|
{
|
||||||
if(sizeof($j = explode("!", $hotlink)) === 2)
|
$this->url = chandler_escape_url(parse_url($url, PHP_URL_PATH));
|
||||||
[$namespace, $hotlink] = $j;
|
if (!is_null($parentModule)) {
|
||||||
else
|
$GLOBALS["parentModule"] = $parentModule;
|
||||||
$namespace = explode("\\", $this->scope["parentModule"])[0];
|
$this->scope["parentModule"] = $GLOBALS["parentModule"];
|
||||||
|
|
||||||
[$presenter, $action] = preg_split(Router::HANDLER_DELIMITER, $hotlink);
|
|
||||||
|
|
||||||
foreach($this->routes as $route) {
|
|
||||||
if($route->namespace !== $namespace || $route->presenter !== $presenter) continue;
|
|
||||||
if(!is_null($action) && $route->action != $action) continue;
|
|
||||||
|
|
||||||
$count = preg_match_all(Router::ALIAS_REGEX, $route->raw);
|
|
||||||
if($count != sizeof($parameters)) continue;
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
return preg_replace_callback(Router::ALIAS_REGEX, function() use ($parameters, &$i) {
|
|
||||||
return $parameters[$i++];
|
|
||||||
}, $route->raw);
|
|
||||||
}
|
}
|
||||||
|
if (preg_match("%^\/assets\/packages\/static\/([A-z_\\-]++)\/(.++)$%", $this->url, $matches)) {
|
||||||
return NULL;
|
[$j, $namespace, $file] = $matches;
|
||||||
|
return $this->delegateStatic($namespace, $file);
|
||||||
|
}
|
||||||
|
$match = $this->getMatchingRoute($this->url);
|
||||||
|
if (!$match)
|
||||||
|
return null;
|
||||||
|
return $this->delegateRoute(...$match);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMatchingRoute(string $url): ?array
|
||||||
|
{
|
||||||
|
foreach ($this->routes as $route)
|
||||||
|
if (preg_match($route->regex, $url, $matches))
|
||||||
|
return [$route, array_slice($matches, 1)];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCSRFToken(Route $route, string $nonce): string
|
||||||
|
{
|
||||||
|
$key = hash("snefru", CHANDLER_ROOT_CONF["security"]["secret"] . bin2hex($nonce));
|
||||||
|
$data = $route->namespace;
|
||||||
|
$data .= Session::i()->get("tok", -1);
|
||||||
|
return hash_hmac("snefru", $data, $key) . "#" . bin2hex($nonce);
|
||||||
|
}
|
||||||
|
|
||||||
function push(?string $prefix, string $url, string $namespace, string $presenter, string $action, array $ph): void
|
function push(?string $prefix, string $url, string $namespace, string $presenter, string $action, array $ph): void
|
||||||
{
|
{
|
||||||
$route = new Route;
|
$route = new Route;
|
||||||
$route->raw = $url;
|
$route->raw = $url;
|
||||||
if(!is_null($prefix))
|
if (!is_null($prefix))
|
||||||
$route->raw = "/$prefix" . $route->raw;
|
$route->raw = "/$prefix" . $route->raw;
|
||||||
|
$route->regex = $this->computeRegExp($url, $ph, $prefix);
|
||||||
$route->regex = $this->computeRegExp($url, $ph, $prefix);
|
|
||||||
$route->namespace = $namespace;
|
$route->namespace = $namespace;
|
||||||
$route->presenter = $presenter;
|
$route->presenter = $presenter;
|
||||||
$route->action = $action;
|
$route->action = $action;
|
||||||
|
$this->routes[] = $route;
|
||||||
$this->routes[] = $route;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushStatic(string $namespace, string $path): void
|
function pushStatic(string $namespace, string $path): void
|
||||||
{
|
{
|
||||||
$this->statics[$namespace] = $path;
|
$this->statics[$namespace] = $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readRoutes(string $filename, string $namespace, bool $autoprefix = true): void
|
function readRoutes(string $filename, string $namespace, bool $autoprefix = true): void
|
||||||
{
|
{
|
||||||
$config = chandler_parse_yaml($filename);
|
$config = chandler_parse_yaml($filename);
|
||||||
|
if (isset($config["static"]))
|
||||||
if(isset($config["static"]))
|
|
||||||
$this->pushStatic($namespace, CHANDLER_EXTENSIONS_ENABLED . "/$namespace/Web/$config[static]");
|
$this->pushStatic($namespace, CHANDLER_EXTENSIONS_ENABLED . "/$namespace/Web/$config[static]");
|
||||||
|
if (isset($config["include"]))
|
||||||
if(isset($config["include"]))
|
foreach ($config["include"] as $include)
|
||||||
foreach($config["include"] as $include)
|
|
||||||
$this->readRoutes(dirname($filename) . "/$include", $namespace, $autoprefix);
|
$this->readRoutes(dirname($filename) . "/$include", $namespace, $autoprefix);
|
||||||
|
foreach ($config["routes"] as $route) {
|
||||||
foreach($config["routes"] as $route) {
|
$route = (object)$route;
|
||||||
$route = (object) $route;
|
|
||||||
$placeholders = $route->placeholders ?? [];
|
$placeholders = $route->placeholders ?? [];
|
||||||
[$presenter, $action] = preg_split(Router::HANDLER_DELIMITER, $route->handler);
|
[$presenter, $action] = preg_split(Router::HANDLER_DELIMITER, $route->handler);
|
||||||
|
$this->push($autoprefix ? $namespace : null, $route->url, $namespace, $presenter, $action, $placeholders);
|
||||||
$this->push($autoprefix ? $namespace : NULL, $route->url, $namespace, $presenter, $action, $placeholders);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMatchingRoute(string $url): ?array
|
function reverse(string $hotlink, ...$parameters): ?string
|
||||||
{
|
{
|
||||||
foreach($this->routes as $route)
|
if (sizeof($j = explode("!", $hotlink)) === 2)
|
||||||
if(preg_match($route->regex, $url, $matches))
|
[$namespace, $hotlink] = $j;
|
||||||
return [$route, array_slice($matches, 1)];
|
else
|
||||||
|
$namespace = explode("\\", $this->scope["parentModule"])[0];
|
||||||
return NULL;
|
[$presenter, $action] = preg_split(Router::HANDLER_DELIMITER, $hotlink);
|
||||||
|
foreach ($this->routes as $route) {
|
||||||
|
if ($route->namespace !== $namespace || $route->presenter !== $presenter) continue;
|
||||||
|
if (!is_null($action) && $route->action != $action) continue;
|
||||||
|
$count = preg_match_all(Router::ALIAS_REGEX, $route->raw);
|
||||||
|
if ($count != sizeof($parameters)) continue;
|
||||||
|
$i = 0;
|
||||||
|
return preg_replace_callback(Router::ALIAS_REGEX, function () use ($parameters, &$i) {
|
||||||
|
return $parameters[$i++];
|
||||||
|
}, $route->raw);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute(string $url, ?string $parentModule = null): ?string
|
private function __construct()
|
||||||
{
|
{
|
||||||
$this->url = chandler_escape_url(parse_url($url, PHP_URL_PATH));
|
$this->events = EventDispatcher::i();
|
||||||
|
|
||||||
if(!is_null($parentModule)) {
|
|
||||||
$GLOBALS["parentModule"] = $parentModule;
|
|
||||||
$this->scope["parentModule"] = $GLOBALS["parentModule"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(preg_match("%^\/assets\/packages\/static\/([A-z_\\-]++)\/(.++)$%", $this->url, $matches)) {
|
|
||||||
[$j, $namespace, $file] = $matches;
|
|
||||||
return $this->delegateStatic($namespace, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
$match = $this->getMatchingRoute($this->url);
|
|
||||||
if(!$match)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return $this->delegateRoute(...$match);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use TSimpleSingleton;
|
use TSimpleSingleton;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue