From e9519be71745cfa5fe713db0a00a8825320ceae6 Mon Sep 17 00:00:00 2001 From: Matthew Baggett Date: Fri, 31 Jul 2020 23:10:39 +0200 Subject: [PATCH] Revise loading of routes to support @domains tag that only loads these routes for specific domains. To enable multisite behaviour. --- src/App.php | 106 +++++---- src/Router/Route.php | 499 +++++++++++++++++++++--------------------- src/Router/Router.php | 9 +- 3 files changed, 318 insertions(+), 296 deletions(-) diff --git a/src/App.php b/src/App.php index 4d220b7..00b1455 100644 --- a/src/App.php +++ b/src/App.php @@ -147,7 +147,11 @@ class App } $container = $container->build(); - $container->set(Slim\Views\Twig::class, function (EnvironmentService $environmentService, SessionService $sessionService, Translation\Translator $translator) { + $container->set(Slim\Views\Twig::class, function ( + EnvironmentService $environmentService, + SessionService $sessionService, + Translation\Translator $translator + ) { foreach ($this->viewPaths as $i => $viewLocation) { if (!file_exists($viewLocation) || !is_dir($viewLocation)) { unset($this->viewPaths[$i]); @@ -321,10 +325,9 @@ class App ); }); - /** @var Services\EnvironmentService $environmentService */ - $environmentService = $container->get(Services\EnvironmentService::class); - if ($environmentService->has('TIMEZONE')) { - date_default_timezone_set($environmentService->get('TIMEZONE')); + $this->environmentService = $container->get(Services\EnvironmentService::class); + if ($this->environmentService->has('TIMEZONE')) { + date_default_timezone_set($this->environmentService->get('TIMEZONE')); } elseif (file_exists('/etc/timezone')) { date_default_timezone_set(trim(file_get_contents('/etc/timezone'))); } else { @@ -516,7 +519,10 @@ class App } $this->interrogateControllersComplete = true; - if ($this->router->loadCache()) { + if ($this->environmentService->has('USE_ROUTE_CACHE') + && 'yes' == strtolower($this->environmentService->get('USE_ROUTE_CACHE')) + && $this->router->loadCache() + ) { return; } @@ -531,7 +537,7 @@ class App $appClass = new \ReflectionClass(get_called_class()); $expectedClasses = [ $appClass->getNamespaceName().'\\Controllers\\'.str_replace('.php', '', $controllerFile->getFilename()), - '⌬\\Controllers\\'.str_replace('.php', '', $controllerFile->getFilename()), + 'Benzine\\Controllers\\'.str_replace('.php', '', $controllerFile->getFilename()), ]; foreach ($expectedClasses as $expectedClass) { if (class_exists($expectedClass)) { @@ -541,51 +547,59 @@ class App /** @var \ReflectionMethod $method */ if (true || ResponseInterface::class == ($method->getReturnType() instanceof \ReflectionType ? $method->getReturnType()->getName() : null)) { $docBlock = $method->getDocComment(); + $newRoute = new Route($this->logger); foreach (explode("\n", $docBlock) as $docBlockRow) { - if (false === stripos($docBlockRow, '@route')) { - continue; - } + if (false !== stripos($docBlockRow, '@route')) { + $route = trim(substr( + $docBlockRow, + (stripos($docBlockRow, '@route') + strlen('@route')) + )); - $route = trim(substr( - $docBlockRow, - (stripos($docBlockRow, '@route') + strlen('@route')) - )); + @list($httpMethods, $path, $extra) = explode(' ', $route, 3); + $httpMethods = explode(',', strtoupper($httpMethods)); - @list($httpMethods, $path, $extra) = explode(' ', $route, 3); - $httpMethods = explode(',', strtoupper($httpMethods)); - - $options = []; - $defaultOptions = [ - 'access' => Route::ACCESS_PUBLIC, - 'weight' => 100, - ]; - if (isset($extra)) { - foreach (explode(' ', $extra) as $item) { - @list($extraK, $extraV) = explode('=', $item, 2); - if (!isset($extraV)) { - $extraV = true; - } - $options[$extraK] = $extraV; - } - } - $options = array_merge($defaultOptions, $options); - foreach ($httpMethods as $httpMethod) { - $newRoute = Route::Factory() - ->setHttpMethod($httpMethod) - ->setRouterPattern('/'.ltrim($path, '/')) - ->setCallback($method->class.':'.$method->name) - ; - - foreach ($options as $key => $value) { - $keyMethod = 'set'.ucfirst($key); - if (method_exists($newRoute, $keyMethod)) { - $newRoute->{$keyMethod}($value); - } else { - $newRoute->setArgument($key, $value); + $options = []; + $defaultOptions = [ + 'access' => Route::ACCESS_PUBLIC, + 'weight' => 100, + ]; + if (isset($extra)) { + foreach (explode(' ', $extra) as $item) { + @list($extraK, $extraV) = explode('=', $item, 2); + if (!isset($extraV)) { + $extraV = true; + } + $options[$extraK] = $extraV; } } + $options = array_merge($defaultOptions, $options); + foreach ($httpMethods as $httpMethod) { + $newRoute + ->setHttpMethod($httpMethod) + ->setRouterPattern('/'.ltrim($path, '/')) + ->setCallback($method->class.':'.$method->name) + ; - $this->router->addRoute($newRoute); + foreach ($options as $key => $value) { + $keyMethod = 'set'.ucfirst($key); + if (method_exists($newRoute, $keyMethod)) { + $newRoute->{$keyMethod}($value); + } else { + $newRoute->setArgument($key, $value); + } + } + + $this->router->addRoute($newRoute); + } + } elseif (false !== stripos($docBlockRow, '@domains')) { + $domains = explode(' ', trim(substr( + $docBlockRow, + (stripos($docBlockRow, '@domains') + strlen('@domains')) + ))); + foreach ($domains as $domain) { + $newRoute->addValidDomain($domain); + } + $newRoute->setWeight($newRoute->getWeight() - 10); } } } diff --git a/src/Router/Route.php b/src/Router/Route.php index f863292..1648bb4 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -2,6 +2,7 @@ namespace Benzine\Router; +use Monolog\Logger; use Slim\App; class Route @@ -27,14 +28,17 @@ class Route protected $propertyOptions; protected $exampleEntity; protected $exampleEntityFinderFunction; - protected $callbackProperties = []; - protected $arguments = [ + protected array $callbackProperties = []; + protected array $arguments = [ self::ARGUMENT_ACCESS => self::ACCESS_PUBLIC, ]; + protected array $validDomains = []; - public static function Factory(): Route + private Logger $logger; + + public function __construct(Logger $logger) { - return new Route(); + $this->logger = $logger; } public function getCallbackProperties(): array @@ -84,18 +88,6 @@ class Route return $this; } - public function getSDKTemplate(): string - { - return $this->SDKTemplate; - } - - public function setSDKTemplate(string $SDKTemplate): Route - { - $this->SDKTemplate = $SDKTemplate; - - return $this; - } - public function getUniqueIdentifier() { return implode( @@ -104,194 +96,11 @@ class Route $this->getRouterPattern(), $this->getHttpMethod(), "Weight={$this->getWeight()}", + $this->callback, ] ); } - /** - * @return mixed - */ - public function getHttpMethod() - { - return $this->httpMethod; - } - - /** - * @param mixed $httpMethod - */ - public function setHttpMethod($httpMethod): Route - { - $this->httpMethod = $httpMethod; - - return $this; - } - - public function getWeight(): int - { - return $this->weight; - } - - public function setWeight(int $weight): Route - { - $this->weight = $weight; - - return $this; - } - - /** - * @return mixed - */ - public function getRouterPattern() - { - return $this->routerPattern; - } - - /** - * @param mixed $routerPattern - */ - public function setRouterPattern($routerPattern): Route - { - $this->routerPattern = $routerPattern; - - return $this; - } - - public function setExampleEntityFindFunction(callable $finderFunction): Route - { - $this->exampleEntityFinderFunction = $finderFunction; - - return $this; - } - - /** - * @return mixed - */ - public function getExampleEntity() - { - if (!$this->exampleEntity && $this->exampleEntityFinderFunction) { - $function = $this->exampleEntityFinderFunction; - $this->exampleEntity = $function(); - } - - return $this->exampleEntity; - } - - /** - * @param mixed $exampleEntity - */ - public function setExampleEntity($exampleEntity): Route - { - $this->exampleEntity = $exampleEntity; - - return $this; - } - - /** - * @return mixed - */ - public function getName() - { - return $this->name; - } - - /** - * @param mixed $name - */ - public function setName($name): Route - { - $this->name = $name; - - return $this; - } - - /** - * @return mixed - */ - public function getSDKClass() - { - return $this->SDKClass; - } - - /** - * @param mixed $SDKClass - */ - public function setSDKClass($SDKClass): Route - { - $this->SDKClass = $SDKClass; - - return $this; - } - - /** - * @return mixed - */ - public function getSDKFunction() - { - return $this->SDKFunction; - } - - /** - * @param mixed $function - */ - public function setSDKFunction($function): Route - { - $this->SDKFunction = $function; - - return $this; - } - - /** - * @return mixed - */ - public function getSingular() - { - return $this->singular; - } - - /** - * @param mixed $singular - */ - public function setSingular($singular): Route - { - $this->singular = $singular; - - return $this; - } - - /** - * @return mixed - */ - public function getPlural() - { - return $this->plural; - } - - /** - * @param mixed $plural - */ - public function setPlural($plural): Route - { - $this->plural = $plural; - - return $this; - } - - public function getPropertyData() - { - return $this->propertyData; - } - - /** - * @return mixed - */ - public function getProperties() - { - return $this->properties; - } - - /** - * @param mixed $properties - */ public function setProperties($properties): Route { $this->properties = []; @@ -307,14 +116,6 @@ class Route return $this; } - /** - * @return mixed - */ - public function getPropertyOptions() - { - return $this->propertyOptions; - } - /** * @param mixed $propertyOptions * @@ -333,7 +134,16 @@ class Route public function populateRoute(App $app): App { - //echo "Populating: {$this->getHttpMethod()} {$this->getRouterPattern()}\n"; + $this->logger->debug(sprintf( + 'Router Populating: %s %s', + $this->getHttpMethod(), + $this->getRouterPattern() + )); + + if ($this->hasValidDomains() && !$this->isInContainedInValidDomains()) { + return $app; + } + $mapping = $app->map( [$this->getHttpMethod()], $this->getRouterPattern(), @@ -349,42 +159,6 @@ class Route return $app; } - /** - * @return mixed - */ - public function getCallback() - { - return $this->callback; - } - - /** - * @param mixed $callback - */ - public function setCallback($callback): Route - { - $this->callback = $callback; - - return $this; - } - - /** - * @return mixed - */ - public function getHttpEndpoint() - { - return $this->httpEndpoint; - } - - /** - * @param mixed $httpEndpoint - */ - public function setHttpEndpoint($httpEndpoint): Route - { - $this->httpEndpoint = $httpEndpoint; - - return $this; - } - /** * @return string */ @@ -416,6 +190,241 @@ class Route return $this; } + public function getName(): string + { + return $this->name ?? 'Unnamed Route'; + } + + public function setName(string $name): Route + { + $this->name = $name; + + return $this; + } + + public function getCallback(): string + { + return $this->callback; + } + + public function setCallback(string $callback): Route + { + $this->callback = $callback; + + return $this; + } + + public function getSDKClass(): string + { + return $this->SDKClass; + } + + public function setSDKClass(string $SDKClass): Route + { + $this->SDKClass = $SDKClass; + + return $this; + } + + public function getSDKFunction(): string + { + return $this->SDKFunction; + } + + public function setSDKFunction(string $SDKFunction): Route + { + $this->SDKFunction = $SDKFunction; + + return $this; + } + + public function getSDKTemplate(): string + { + return $this->SDKTemplate; + } + + public function setSDKTemplate(string $SDKTemplate): Route + { + $this->SDKTemplate = $SDKTemplate; + + return $this; + } + + public function getRouterPattern(): string + { + return $this->routerPattern; + } + + public function setRouterPattern(string $routerPattern): Route + { + $this->routerPattern = $routerPattern; + + return $this; + } + + public function getHttpEndpoint(): string + { + return $this->httpEndpoint; + } + + public function setHttpEndpoint(string $httpEndpoint): Route + { + $this->httpEndpoint = $httpEndpoint; + + return $this; + } + + public function getHttpMethod(): string + { + return $this->httpMethod; + } + + public function setHttpMethod(string $httpMethod): Route + { + $this->httpMethod = $httpMethod; + + return $this; + } + + public function getWeight(): int + { + return $this->weight; + } + + public function setWeight(int $weight): Route + { + $this->weight = $weight; + + return $this; + } + + public function getSingular(): string + { + return $this->singular; + } + + public function setSingular(string $singular): Route + { + $this->singular = $singular; + + return $this; + } + + public function getPlural(): string + { + return $this->plural; + } + + public function setPlural(string $plural): Route + { + $this->plural = $plural; + + return $this; + } + + public function getPropertyData(): array + { + return $this->propertyData; + } + + public function setPropertyData(array $propertyData): Route + { + $this->propertyData = $propertyData; + + return $this; + } + + public function getExampleEntity() + { + return $this->exampleEntity; + } + + /** + * @param mixed $exampleEntity + * + * @return Route + */ + public function setExampleEntity($exampleEntity) + { + $this->exampleEntity = $exampleEntity; + + return $this; + } + + public function getExampleEntityFinderFunction() + { + return $this->exampleEntityFinderFunction; + } + + /** + * @param mixed $exampleEntityFinderFunction + * + * @return Route + */ + public function setExampleEntityFinderFunction($exampleEntityFinderFunction) + { + $this->exampleEntityFinderFunction = $exampleEntityFinderFunction; + + return $this; + } + + /** + * @return array|string[] + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * @param array|string[] $arguments + * + * @return Route + */ + public function setArguments($arguments) + { + $this->arguments = $arguments; + + return $this; + } + + public function addValidDomain(string $validDomain): Route + { + $this->validDomains[] = $validDomain; + + return $this; + } + + public function getValidDomains(): array + { + $this->validDomains = array_unique($this->validDomains); + + return $this->validDomains; + } + + public function hasValidDomains(): bool + { + return count($this->validDomains) > 0; + } + + public function isInContainedInValidDomains(): bool + { + foreach ($this->validDomains as $validDomain) { + if (fnmatch($validDomain, $_SERVER['HTTP_HOST'])) { + return true; + } + } + + return false; + } + + public function setValidDomains(array $validDomains): Route + { + $this->validDomains = $validDomains; + + return $this; + } + private function prefixArgumentKey(string $key) { if (0 !== strpos($key, '_')) { diff --git a/src/Router/Router.php b/src/Router/Router.php index f50a807..688f8c1 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -9,7 +9,7 @@ use Slim\App; class Router { /** @var Route[] */ - private $routes = []; + private array $routes = []; private Logger $logger; private CachePoolChain $cachePoolChain; private int $cacheTTL = 60; @@ -29,7 +29,8 @@ class Router }); foreach ($this->routes as $index => $route) { - if (!isset($allocatedRoutes[$route->getHttpMethod().$route->getRouterPattern()])) { + if (($route->isInContainedInValidDomains() || !$route->hasValidDomains()) + && !isset($allocatedRoutes[$route->getHttpMethod().$route->getRouterPattern()])) { $allocatedRoutes[$route->getHttpMethod().$route->getRouterPattern()] = true; } else { unset($this->routes[$index]); @@ -44,7 +45,7 @@ class Router { $this->weighRoutes(); if (count($this->routes) > 0) { - foreach ($this->routes as $route) { + foreach ($this->getRoutes() as $route) { $app = $route->populateRoute($app); } } @@ -64,8 +65,6 @@ class Router */ public function getRoutes() { - ksort($this->routes); - return $this->routes; }