Skip to content

Commit

Permalink
Route identifier/name fixes, init logic for route caching
Browse files Browse the repository at this point in the history
  • Loading branch information
donwilson committed Nov 28, 2023
1 parent 3820fcb commit b8c16f6
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 76 deletions.
48 changes: 28 additions & 20 deletions src/Magnetar/Helpers/Facades/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,54 @@
use Magnetar\Helpers\Facades\Facade;

/**
* @method static void setBasePath(string $base_path)
* @method static string pathBase(string $rel_path)
* @method static self setAppPath(string $path)
* @method static string pathApp(string $rel_path)
* @method static self setConfigPath(string $path)
* @method static string pathConfig(string $rel_path)
* @method static self setDataPath(string $path)
* @method static string pathData(string $rel_path)
* @method static self setPublicPath(string $path)
* @method static string pathPublic(string $rel_path)
* @method static self setAssetsPath(string $path)
* @method static string pathAssets(string $rel_path)
* @method static self setStoragePath(string $path)
* @method static string pathStorage(string $rel_path)
* @method static self setRoutingPath(string $path)
* @method static string pathRouting(string $rel_path)
* @method static self setThemesPath(string $path)
* @method static string pathThemes(string $rel_path)
* @method static string joinPath(string $base_path, string $rel_path)
* @method static bool hasBeenBootstrapped()
* @method static void bootstrapWith(array $bootstrappers)
* @method static bool hasBootedServiceProviders()
* @method static void registerConfiguredServiceProviders()
* @method static void booting(callable $callback)
* @method static void booted(callable $callback)
* @method static void bootServiceProviders()
* @method static void bootServiceProvider(\Magnetar\Helpers\ServiceProvider|string $provider)
* @method static void bootServiceProvider(\Magnetar\Helpers\ServiceProvider $provider)
* @method static \Magnetar\Helpers\ServiceProvider registerServiceProvider(\Magnetar\Helpers\ServiceProvider|string $provider)
* @method static \Magnetar\Helpers\ServiceProvider resolveServiceProvider(string $provider)
* @method static ?\Magnetar\Helpers\ServiceProvider getServiceProvider(\Magnetar\Helpers\ServiceProvider|string $provider)
* @method static array getServiceProviders(\Magnetar\Helpers\ServiceProvider|string $provider)
* @method static void loadDeferredServiceProviders()
* @method static void loadDeferredServiceProvider(string $service)
* @method static void registerDeferredServiceProvider(string $provider, ?string $service)
* @method static bool isDeferredService(string $service)
* @method static bool isDeferredServiceProviderClass(string $provider_class)
* @method static void registerCoreContainerAliases()
* @method static mixed make(callable|string $abstract, array $parameters)
* @method static bool bound(string $abstract)
* @method static void flush()
* @method static void registerTerminateCallback(callable|array|string $callback)
* @method static void terminate()
* @method static void runAppCallbacks(array $callbacks)
* @method static void setEnvironment(string $env)
* @method static string environment()
* @method static bool isDevEnv()
* @method static bool isTestEnv()
* @method static bool isLiveEnv()
* @method static void setBasePath(string $base_path)
* @method static string pathBase(string $rel_path)
* @method static self setAppPath(string $path)
* @method static string pathApp(string $rel_path)
* @method static self setConfigPath(string $path)
* @method static string pathConfig(string $rel_path)
* @method static self setDataPath(string $path)
* @method static string pathData(string $rel_path)
* @method static self setPublicPath(string $path)
* @method static string pathPublic(string $rel_path)
* @method static self setAssetsPath(string $path)
* @method static string pathAssets(string $rel_path)
* @method static self setStoragePath(string $path)
* @method static string pathStorage(string $rel_path)
* @method static self setRoutingPath(string $path)
* @method static string pathRouting(string $rel_path)
* @method static self setThemesPath(string $path)
* @method static string pathThemes(string $rel_path)
* @method static string joinPath(string $base_path, string $rel_path)
* @method static \Magnetar\Container\ContextualBindingBuilder when( $concrete)
* @method static bool has(string $id)
* @method static bool resolved(string $abstract)
Expand Down
1 change: 1 addition & 0 deletions src/Magnetar/Helpers/Facades/Cookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* @method static self set(string $name, mixed $value, ?int $expires_seconds, ?string $path, ?string $domain, ?bool $secure, ?bool $httponly)
* @method static self setCookie(\Magnetar\Http\CookieJar\Cookie $cookie)
* @method static self remove(string $name)
* @method static self unequeue(string $name)
* @method static self setDefaults(?int $expires_seconds, ?string $path, ?string $domain, ?bool $secure, ?bool $httponly)
* @method static int getDefaultExpiresSeconds()
* @method static string getDefaultPath()
Expand Down
2 changes: 2 additions & 0 deletions src/Magnetar/Helpers/Facades/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
* @method static \Magnetar\Router\Route patch(string $pattern, callable|array|string|null $callback)
* @method static \Magnetar\Router\Route delete(string $pattern, callable|array|string|null $callback)
* @method static \Magnetar\Router\Route options(string $pattern, callable|array|string|null $callback)
* @method static \Magnetar\Router\Route match(\Magnetar\Router\Enums\HTTPMethodEnum|array|string $methods, string $pattern, callable|array|string|null $callback)
* @method static \Magnetar\Router\RouteCollection group(string $pathPrefix, callable $callback)
* @method static \Magnetar\Router\Route redirect(string $pattern, string $redirect_path, int $response_code)
* @method static \Magnetar\Router\Route permanentRedirect(string $pattern, string $redirect_path)
* @method static array export()
*
* @see \Magnetar\Router\Router
*/
Expand Down
14 changes: 14 additions & 0 deletions src/Magnetar/Http/Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@

namespace Magnetar\Http\Controller;

use Magnetar\Http\Controller\HasMiddlewareTrait;

use BadMethodCallException;

class Controller {
use HasMiddlewareTrait;

/**
* Call an action registered on the controller
* @param string $method The method to call
* @param array $args The arguments to pass to the method
* @return mixed
*/
public function callAction(string $method, array $args): mixed {
return $this->{$method}(...$args);
}

/**
* Catch any undefined methods
* @param string $method
Expand Down
31 changes: 31 additions & 0 deletions src/Magnetar/Http/Controller/HasMiddlewareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);

namespace Magnetar\Http\Controller;

trait HasMiddlewareTrait {
/**
* The middleware stack
* @var array
*/
protected array $middleware = [];

/**
* Add middleware to the controller
* @param string $middleware The middleware to add
* @return self
*/
public function middleware(string $middleware): self {
$this->middleware[] = $middleware;

return $this;
}

/**
* Get the middleware attached to the controller
* @return array
*/
public function middlewares(): array {
return $this->middleware;
}
}
4 changes: 2 additions & 2 deletions src/Magnetar/Http/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public function process(Request $request): Response {
* @return void
*/
protected function recordException(Throwable $e): void {
$this->app[ExceptionHandler::class]->record($e);
$this->app[ ExceptionHandler::class ]->record($e);
}

/**
Expand All @@ -125,7 +125,7 @@ protected function recordException(Throwable $e): void {
* @return Response
*/
protected function renderException(Request $request, Throwable $e): Response {
return $this->app[ExceptionHandler::class]->render($request, $e);
return $this->app[ ExceptionHandler::class ]->render($request, $e);
}

/**
Expand Down
92 changes: 71 additions & 21 deletions src/Magnetar/Router/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
* using request methods and paths
*/
class Route {
/**
* Unique identifier for this route
* @var string
*/
protected string|null $uniqueID = null;

/**
* The route name
* @var string|null The route name
Expand Down Expand Up @@ -60,6 +66,8 @@ class Route {
* An array of parameters that were matched in the request.
* Key is the parameter name, value is a typed variable of the matched value
* @var array|null
*
* @TODO upon route match, generate a 'MatchedRoute' object that extends this route and stores the matched parameters (among other things)
*/
protected array|null $parameters = null;

Expand Down Expand Up @@ -118,6 +126,26 @@ public function setContainer(Container $container): self {
return $this;
}

/**
* Get the route's unique identifier
* @return string
*/
public function getUniqueID(): string {
return $this->uniqueID ??= $this->generateUniqueID();
}

/**
* Generate a unique identifier for this route
* @return void
*/
protected function generateUniqueID(): string {
// specific method(s) - use pipe-delimited list of methods as a prefix
return md5(implode('|', array_map(
fn(HTTPMethodEnum $method): string => HTTPMethodEnumResolver::resolveToString($method),
$this->methods
)) .':'. $this->path_basic);
}

/**
* Parse the constructor method
* @param HTTPMethodEnum|array|string|null $method
Expand Down Expand Up @@ -267,30 +295,11 @@ public function name(string|null $name=null): Route {
}

/**
* Returns the name of the path, or an md5 hash of the path if no name is set
* Returns the name of the path, or the route's unique identifier (md5 hash) if the name is not set
* @return string
*
* @todo make this more efficient (maybe have a unique id for each route)
*/
public function getName(): string {
return $this->name ??= $this->generateName();
}

/**
* Generate a name for this route
* @return string The generated name
*/
protected function generateName(): string {
if(null === $this->methods) {
// any method
return md5($this->path_basic);
}

// specific method(s) - use pipe-delimited list of methods as a prefix
return md5(implode('|', array_map(
fn(HTTPMethodEnum $method): string => HTTPMethodEnumResolver::resolveToString($method),
$this->methods
)) .':'. $this->path_basic);
return $this->name ?? $this->getUniqueID();
}

/**
Expand All @@ -317,6 +326,14 @@ public function getPathRegex(): string {
return $this->path_regex;
}

/**
* Determine if the route's path is a regex pattern
* @return bool
*/
public function isPathRegex(): bool {
return $this->path_is_regex;
}

/**
* Get the route's assoc array of variables defined in the path.
* Key is the variable name, value is the TypedEnum
Expand Down Expand Up @@ -412,4 +429,37 @@ protected function parseMatchedPathVariables(array $matches): void {
public function parameters(): array {
return $this->parameters ?? [];
}

/**
* Export the route's data
* @return array
*/
public function export(): array {
return [
'unique_id' => $this->getUniqueID(),
'name' => $this->getName(),
'methods' => $this->getMethods(),
'path_basic' => $this->getPath(),
'path_regex' => $this->getPathRegex(),
'path_is_regex' => $this->isPathRegex(),
'var_types' => $this->getPathVariableTypes(),
];
}

/**
* Import cached route data
* @param array $data The cached route data
* @return void
*
* @TODO instead of modifying this route, return a new route with the cached data (or use a Route factory)
*/
public function importCachedRoute(array $data): void {
$this->uniqueID = $data['unique_id'] ?? null;
$this->name = $data['name'] ?? null;
$this->methods = $data['methods'] ?? [];
$this->path_basic = $data['path_basic'] ?? '';
$this->path_regex = $data['path_regex'] ?? '';
$this->path_is_regex = $data['path_is_regex'] ?? false;
$this->var_types = $data['var_types'] ?? [];
}
}
2 changes: 1 addition & 1 deletion src/Magnetar/Router/RouteActionRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function register(
Route $route,
callable|array|string|null $callback
): Route {
$this->actions[ $route->getName() ] = $callback;
$this->actions[ $route->getUniqueID() ] = $callback;

return $route;
}
Expand Down
28 changes: 26 additions & 2 deletions src/Magnetar/Router/RouteCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
class RouteCollection {
/**
* An array of routes within this collection
* @var array Route[]
* @var array \Magnetar\Router\Route[]
*/
public array $routes = [];

Expand Down Expand Up @@ -172,7 +172,7 @@ public function makeRoute(
public function add(
Route $route
): Route {
return $this->routes[ $route->getName() ] = $route;
return $this->routes[ $route->getUniqueID() ] = $route;
}

/**
Expand Down Expand Up @@ -212,4 +212,28 @@ public function matchRequestToRoute(
// no route matches, return null
return null;
}

/**
* Export the collection as a serialized array of routes (for caching)
* @return array
*/
public function export(): array {
$routes = [];

foreach($this->routes as $route) {
$routes[] = $route->export();
}

//return serialize($routes);
return $routes;
}

/**
* Import a serialized array of routes (for caching)
* @param string $serializedData
* @return self
*/
public function import(string $serializedData): self {
// @TODO make use of a Route factory to import routes
}
}
Loading

0 comments on commit b8c16f6

Please sign in to comment.