Skip to content

Commit

Permalink
Simplify route classes & add route builders
Browse files Browse the repository at this point in the history
  • Loading branch information
rustamwin committed Nov 9, 2023
1 parent 7848f14 commit 39f05ff
Show file tree
Hide file tree
Showing 22 changed files with 1,607 additions and 1,209 deletions.
166 changes: 166 additions & 0 deletions src/Builder/GroupBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Router\Builder;

use RuntimeException;
use Yiisoft\Router\Group;
use Yiisoft\Router\RoutableInterface;
use Yiisoft\Router\Route;

final class GroupBuilder implements RoutableInterface
{
/**
* @var Group[]|Route[]|RoutableInterface[]
*/
private array $routes = [];

/**
* @var array[]|callable[]|string[]
* @psalm-var list<array|callable|string>
*/
private array $middlewares = [];

private array $disabledMiddlewares = [];

/**
* @var string[]
*/
private array $hosts = [];
private bool $routesAdded = false;
private bool $middlewareAdded = false;

/**
* @var array|callable|string|null Middleware definition for CORS requests.
*/
private $corsMiddleware = null;

private function __construct(
private ?string $prefix = null,
private ?string $namePrefix = null,
) {
}

/**
* Create a new group instance.
*
* @param string|null $prefix URL prefix to prepend to all routes of the group.
*/
public static function create(?string $prefix = null, ?string $namePrefix = null): self
{
return new self($prefix, $namePrefix);
}

public function routes(Group|Route|RoutableInterface ...$routes): self
{
if ($this->middlewareAdded) {
throw new RuntimeException('routes() can not be used after prependMiddleware().');
}

$new = clone $this;
$new->routes = $routes;
$new->routesAdded = true;

return $new;
}

/**
* Adds a middleware definition that handles CORS requests.
* If set, routes for {@see Method::OPTIONS} request will be added automatically.
*
* @param array|callable|string|null $middlewareDefinition Middleware definition for CORS requests.
*/
public function withCors(array|callable|string|null $middlewareDefinition): self
{
$group = clone $this;
$group->corsMiddleware = $middlewareDefinition;

return $group;
}

/**
* Appends a handler middleware definition that should be invoked for a matched route.
* First added handler will be executed first.
*/
public function middleware(array|callable|string ...$definition): self
{
if ($this->routesAdded) {
throw new RuntimeException('middleware() can not be used after routes().');
}

$new = clone $this;
array_push(
$new->middlewares,
...array_values($definition)
);

return $new;
}

/**
* Prepends a handler middleware definition that should be invoked for a matched route.
* First added handler will be executed last.
*/
public function prependMiddleware(array|callable|string ...$definition): self
{
$new = clone $this;
array_unshift(
$new->middlewares,
...array_values($definition)
);

$new->middlewareAdded = true;

return $new;
}

public function namePrefix(string $namePrefix): self
{
$new = clone $this;
$new->namePrefix = $namePrefix;
return $new;
}

public function host(string $host): self
{
return $this->hosts($host);
}

public function hosts(string ...$hosts): self
{
$new = clone $this;
$new->hosts = array_values($hosts);

Check warning on line 133 in src/Builder/GroupBuilder.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "UnwrapArrayValues": --- Original +++ New @@ @@ public function hosts(string ...$hosts) : self { $new = clone $this; - $new->hosts = array_values($hosts); + $new->hosts = $hosts; return $new; } /**

return $new;
}

/**
* Excludes middleware from being invoked when action is handled.
* It is useful to avoid invoking one of the parent group middleware for
* a certain route.
*/
public function disableMiddleware(mixed ...$definition): self
{
$new = clone $this;
array_push(
$new->disabledMiddlewares,
...array_values($definition),
);

return $new;
}

public function toRoute(): Group|Route
{
return new Group(
prefix: $this->prefix,
namePrefix: $this->namePrefix,
routes: $this->routes,
middlewares: $this->middlewares,
hosts: $this->hosts,
disabledMiddlewares: $this->disabledMiddlewares,
corsMiddleware: $this->corsMiddleware
);
}
}
218 changes: 218 additions & 0 deletions src/Builder/RouteBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Router\Builder;

use Stringable;
use Yiisoft\Http\Method;
use Yiisoft\Router\Group;
use Yiisoft\Router\RoutableInterface;
use Yiisoft\Router\Route;

use function in_array;

/**
* Route defines a mapping from URL to callback / name and vice versa.
*/
final class RouteBuilder implements RoutableInterface
{
private ?string $name = null;

/**
* @var array|string|callable|null
*/
private $action = null;

/**
* @var string[]
*/
private array $hosts = [];

private bool $override = false;

private array $disabledMiddlewares = [];

/**
* @var array[]|callable[]|string[]
* @psalm-var list<array|callable|string>
*/
private array $middlewares = [];

/**
* @var array<array-key,scalar|Stringable|null>
*/
private array $defaults = [];

/**
* @param string[] $methods
*/
private function __construct(
private array $methods,
private string $pattern,
) {
}

public static function get(string $pattern): self
{
return self::methods([Method::GET], $pattern);
}

public static function post(string $pattern): self
{
return self::methods([Method::POST], $pattern);
}

public static function put(string $pattern): self
{
return self::methods([Method::PUT], $pattern);
}

public static function delete(string $pattern): self
{
return self::methods([Method::DELETE], $pattern);
}

public static function patch(string $pattern): self
{
return self::methods([Method::PATCH], $pattern);
}

public static function head(string $pattern): self
{
return self::methods([Method::HEAD], $pattern);
}

public static function options(string $pattern): self
{
return self::methods([Method::OPTIONS], $pattern);
}

/**
* @param string[] $methods
*/
public static function methods(array $methods, string $pattern): self
{
return new self(methods: $methods, pattern: $pattern);
}

public function name(string $name): self
{
$route = clone $this;
$route->name = $name;
return $route;
}

public function pattern(string $pattern): self
{
$new = clone $this;
$new->pattern = $pattern;
return $new;
}

public function host(string $host): self
{
return $this->hosts($host);
}

public function hosts(string ...$hosts): self
{
$route = clone $this;
$route->hosts = array_values($hosts);

Check warning on line 121 in src/Builder/RouteBuilder.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.1-ubuntu-latest

Escaped Mutant for Mutator "UnwrapArrayValues": --- Original +++ New @@ @@ public function hosts(string ...$hosts) : self { $route = clone $this; - $route->hosts = array_values($hosts); + $route->hosts = $hosts; return $route; } /**

return $route;
}

/**
* Marks route as override. When added it will replace existing route with the same name.
*/
public function override(): self
{
$route = clone $this;
$route->override = true;
return $route;
}

/**
* Parameter default values indexed by parameter names.
*
* @psalm-param array<array-key,null|Stringable|scalar> $defaults
*/
public function defaults(array $defaults): self
{
$route = clone $this;
$route->defaults = $defaults;
return $route;
}

/**
* Appends a handler middleware definition that should be invoked for a matched route.
* First added handler will be executed first.
*/
public function middleware(array|callable|string ...$definition): self
{
$route = clone $this;
array_push(
$route->middlewares,
...array_values($definition)
);

return $route;
}

/**
* Prepends a handler middleware definition that should be invoked for a matched route.
* Last added handler will be executed first.
*/
public function prependMiddleware(array|callable|string ...$definition): self
{
$route = clone $this;
array_unshift(
$route->middlewares,
...array_values($definition)
);

return $route;
}

/**
* Appends action handler. It is a primary middleware definition that should be invoked last for a matched route.
*/
public function action(array|callable|string $middlewareDefinition): self
{
$route = clone $this;
$route->action = $middlewareDefinition;
return $route;
}

/**
* Excludes middleware from being invoked when action is handled.
* It is useful to avoid invoking one of the parent group middleware for
* a certain route.
*/
public function disableMiddleware(mixed ...$definition): self
{
$route = clone $this;
array_push(
$route->disabledMiddlewares,
...array_values($definition)
);

return $route;
}

public function toRoute(): Group|Route
{
return new Route(
methods: $this->methods,
pattern: $this->pattern,
name: $this->name,
action: $this->action,
middlewares: $this->middlewares,
defaults: $this->defaults,
hosts: $this->hosts,
override: $this->override,
disabledMiddlewares: $this->disabledMiddlewares
);
}
}
Loading

0 comments on commit 39f05ff

Please sign in to comment.