[英]Create group route with Closure in PHP (similar laravel)
如何使用 Closure 按中间件或路由前缀对路由进行分组? 类似于 laravel(不像社区中的其他答案那样使用“使用”,而是使用 static 调用)。
我不知道如何在闭包内对所有这些调用进行分组。 我怎样才能做到这一点? 一些例子?
web.php(文件路径):
//web.php file
Router::middleware(['auth', 'okok'])->group(function(){
Router::get('/teste', [TestController::class, 'test']);
Router::get('/teste2', [TestController::class, 'test2']);
});
Router.php(路由器类):
<?php
namespace Core;
use Core\RouterHandler;
use Closure;
class Router
{
protected static $routes = [];
protected static $middlewares = [];
protected static string $uriMethod;
protected static string $trigger;
/**
* start
* START THE ROUTER IN INDEX.PHP
* @param Router $router
* @return void
*/
public static function start()
{
$routerHandler = new RouterHandler(self::$routes);
$routerHandler->run();
}
/**
* constructByMethod
* CONSTRUCT THE ROUTE OBJECT WHEN CALLED BY THE MAINS METHODS
* @param string $uri
* @param array $trigger
* @param string $uriMethod
* @return object
*/
private static function constructByMethod(string $uri, array $trigger, string $uriMethod): object
{
self::$uriMethod = $uriMethod;
self::$trigger = $trigger[0].'::'.$trigger[1];
self::$routes[$uriMethod][self::$trigger]['uri'] = $uri;
self::$routes[$uriMethod][self::$trigger]['uriMethod'] = $uriMethod;
self::$routes[$uriMethod][self::$trigger]['controller'] = $trigger[0];
self::$routes[$uriMethod][self::$trigger]['action'] = $trigger[1];
if (!empty(self::$middlewares)) {
self::$routes[$uriMethod][self::$trigger]['middlewares'] = self::$middlewares;
}
self::$middlewares = [];
return new static();
}
/**
* get
* MAIN METHOD TO CONSTRUCT GET ROUTE
* @param string $uri
* @param array $trigger
* @return object
*/
public static function get(string $uri, array $trigger): object
{
self::constructByMethod($uri, $trigger, 'GET');
return new static();
}
/**
* post
* MAIN METHOD TO CONSTRUCT POST ROUTE
* @param string $uri
* @param array $trigger
* @return object
*/
public static function post(string $uri, array $trigger): object
{
self::constructByMethod($uri, $trigger, 'POST');
return new static();
}
/**
* put
* MAIN METHOD TO CONSTRUCT POST ROUTE
* @param string $uri
* @param array $trigger
* @return object
*/
public static function put(string $uri, array $trigger): object
{
self::constructByMethod($uri, $trigger, 'PUT');
return new static();
}
/**
* delete
* MAIN METHOD TO CONSTRUCT DELETE ROUTE
* @param string $uri
* @param array $trigger
* @return object
*/
public static function delete(string $uri, array $trigger): object
{
self::constructByMethod($uri, $trigger, 'DELETE');
return new static();
}
public static function group($callback)
{
$callback();
}
/**
* name
* ADD A NAME TO A ROUTE
* @param string $name
* @return object
*/
public static function name(string $name): object
{
self::$routes[self::$uriMethod][self::$trigger]['name'] = $name;
return new static();
}
/**
* middleware
* ADD A GROUP OF MIDDLEWARE FOR A ROUTES
* @param array|null $middlewares
* @return object
*/
public static function middleware(array|null $middlewares): object
{
self::$middlewares = $middlewares;
return new static();
}
/**
* where
* ADD A REGEX CONDITION FOR A VARIABLE PARAMETER IN ROUTE
* @param array $where
* @return object
*/
public static function where(array $where): object
{
self::$routes[self::$uriMethod][self::$trigger]['where'] = $where;
return new static();
}
}
之后怎么办?
public static function group($callback)
{
$callback();
}
尝试了所有方法,但我不明白如何使用闭包对路线进行分组。
我需要了解如何在这种特定情况下使用闭包。
首先,您需要使用方法Closure::call ,以便临时将组处理程序( function(){...}
) 绑定到路由器的实例。
之后,您需要执行组处理程序。 然后:
下面是我的代码的一个简短示例,关于将组添加到路由集合,并带有详细注释(请参阅RouteCollection::group
方法)。
请注意,我将组/路由添加到RouteCollection
,它进一步添加到我的Router
。 相反,您将它们直接添加到您的Router
。
在下面,我还发布了我的整个RouteCollection
代码,以便更好地理解。
注意:您正在使用static
方法/属性。 我建议根本不要使用任何东西。 相反,您应该使用依赖注入。
RouteCollectionInterface(简短版本,关于绑定和执行组):
<?php
namespace PajuranCodes\Router;
use Closure;
/**
* An interface to a collection of routes.
*
* @author pajurancodes https://github.com/pajurancodes
*/
interface RouteCollectionInterface extends \Countable, \IteratorAggregate {
/**
* Add a group.
*
* @param string $pattern A group pattern.
* @param Closure $handler A group handler.
* @return static
*/
public function group(string $pattern, Closure $handler): static;
}
RouteCollection(简短版本,关于绑定和执行组):
<?php
namespace PajuranCodes\Router;
use function array_pop;
use PajuranCodes\Router\{
RouteCollectionInterface,
};
use Closure;
/**
* A collection of routes.
*
* @author pajurancodes https://github.com/pajurancodes
*/
class RouteCollection implements RouteCollectionInterface {
[...]
/**
* A list of group patterns as an indexed array.
*
* {@internal Each time a group handler is executed, the group
* pattern is pushed to this list. When a route is added to the
* routes list (inside the scope of a group handler), the route
* pattern is prefixed with the string formed by concatenating
* all group patterns saved in this list.}
*
* @var string[]
*/
private array $groupPatterns = [];
/**
* @inheritDoc
*/
public function group(string $pattern, Closure $handler): static {
$this->addGroup($pattern, $handler);
return $this;
}
/**
* Add a group.
*
* @param string $pattern A group pattern.
* @param Closure $handler A group handler.
* @return static
*/
private function addGroup(string $pattern, Closure $handler): static {
$this->saveGroupPattern($pattern);
$this->executeGroupHandler($handler);
/*
* Remove the last group pattern from the group patterns list.
* This step is performed only after all calls for adding
* groups/routes inside the scope of the current group
* handler have finished their processing.
*/
$this->popLastGroupPattern();
return $this;
}
/**
* Save a group pattern.
*
* @param string $pattern A group pattern.
* @return static
*/
private function saveGroupPattern(string $pattern): static {
$this->groupPatterns[] = $pattern;
return $this;
}
/**
* Execute a group handler.
*
* {@internal This method temporarily binds the given group handler to
* the instance of the route collection - defined by the argument of
* Closure::call - and executes it. Inside the scope of the group handler,
* the route collection will be accessed using the keyword "$this".}
*
* @link https://www.php.net/manual/en/closure.call.php Closure::call
*
* @param Closure $handler A group handler.
* @return static The value returned by executing the group handler.
*/
private function executeGroupHandler(Closure $handler): static {
$handler->call($this);
return $this;
}
/**
* Pop and return the last group pattern in the list of group patterns.
*
* The list will be shortened by one element.
*
* @return string|null The last group pattern, or null if the list is empty.
*/
private function popLastGroupPattern(): ?string {
return array_pop($this->groupPatterns);
}
[...]
}
RouteCollectionInterface(完整版):
<?php
namespace PajuranCodes\Router;
use Closure;
use PajuranCodes\Router\RouteInterface;
/**
* An interface to a collection of routes.
*
* @author pajurancodes https://github.com/pajurancodes
*/
interface RouteCollectionInterface extends \Countable, \IteratorAggregate {
/**
* Add a group.
*
* @param string $pattern A group pattern.
* @param Closure $handler A group handler.
* @return static
*/
public function group(string $pattern, Closure $handler): static;
/**
* Add a route for one or more HTTP methods.
*
* @param string|string[] $methods One or more HTTP methods.
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function route(
string|array $methods,
string $pattern,
string|array|object $handler
): RouteInterface;
/**
* Add a route for all HTTP methods.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function routeForAllMethods(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method GET.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function get(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method HEAD.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function head(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method POST.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function post(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method PUT.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function put(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method DELETE.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function delete(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method CONNECT.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function connect(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method OPTIONS.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function options(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method TRACE.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function trace(string $pattern, string|array|object $handler): RouteInterface;
/**
* Add a route for the HTTP method PATCH.
*
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
public function patch(string $pattern, string|array|object $handler): RouteInterface;
/**
* Get a route by id.
*
* @param string $id A route id.
* @return RouteInterface The found route.
* @throws \UnexpectedValueException No route found.
*/
public function getRouteById(string $id): RouteInterface;
/**
* Get a route by name.
*
* @param string $name A route name.
* @return RouteInterface The found route.
* @throws \UnexpectedValueException No route found.
*/
public function getRouteByName(string $name): RouteInterface;
/**
* Check if a route exists in the collection.
*
* @param string $id A route id.
* @return bool True if the specified route id exists, or false otherwise.
*/
public function exists(string $id): bool;
/**
* Remove a route from the collection.
*
* @param string $id A route Id.
* @return static
*/
public function remove(string $id): static;
/**
* Get all routes from the collection.
*
* @return RouteInterface[] All routes in the collection.
*/
public function all(): array;
/**
* Remove all routes from the collection.
*
* @return static
*/
public function clear(): static;
/**
* Check if the collection is empty.
*
* @return bool True if the collection is empty, or false otherwise.
*/
public function isEmpty(): bool;
/**
* Count the routes in the collection.
*
* @return int Number of routes in the collection.
*/
public function count(): int;
/**
* Get an iterator to iterate through the collection.
*
* @return \Traversable The routes iterator.
*/
public function getIterator(): \Traversable;
}
RouteCollection(完整版):
<?php
namespace PajuranCodes\Router;
use function count;
use function implode;
use function array_pop;
use function array_key_exists;
use PajuranCodes\Router\{
Route,
RouteInterface,
RouteCollectionInterface,
};
use Closure;
use Fig\Http\Message\RequestMethodInterface as RequestMethod;
/**
* A collection of routes.
*
* @author pajurancodes https://github.com/pajurancodes
*/
class RouteCollection implements RouteCollectionInterface {
/**
* A string to be concatenated to a counter in order to form a route id.
*
* @var string
*/
private const ROUTE_ID_PREFIX = 'route';
/**
* A list of allowed HTTP methods.
*
* @link https://tools.ietf.org/html/rfc7231#section-4 4. Request Methods
* @link https://www.iana.org/assignments/http-methods/http-methods.xhtml Hypertext Transfer Protocol (HTTP) Method Registry
* @link https://tools.ietf.org/html/rfc5789 PATCH Method for HTTP
*
* @var string[]
*/
private const ROUTE_ALLOWED_METHODS = [
RequestMethod::METHOD_GET,
RequestMethod::METHOD_HEAD,
RequestMethod::METHOD_POST,
RequestMethod::METHOD_PUT,
RequestMethod::METHOD_DELETE,
RequestMethod::METHOD_CONNECT,
RequestMethod::METHOD_OPTIONS,
RequestMethod::METHOD_TRACE,
RequestMethod::METHOD_PATCH,
];
/**
* A list of routes as an associative array.
*
* The key of each list item is a route id
* built from a prefix string and a counter.
*
* @var RouteInterface[]
*/
private array $routes = [];
/**
* A list of group patterns as an indexed array.
*
* {@internal Each time a group handler is executed, the group
* pattern is pushed to this list. When a route is added to the
* routes list (inside the scope of a group handler), the route
* pattern is prefixed with the string formed by concatenating
* all group patterns saved in this list.}
*
* @var string[]
*/
private array $groupPatterns = [];
/**
* A counter used to be prefixed with a given string in order to form a route id.
*
* {@internal After a route is added to the
* routes list this counter is incremented.}
*
* @var int
*/
private int $routeIdCounter = 0;
/**
* @inheritDoc
*/
public function group(string $pattern, Closure $handler): static {
$this->addGroup($pattern, $handler);
return $this;
}
/**
* Add a group.
*
* @param string $pattern A group pattern.
* @param Closure $handler A group handler.
* @return static
*/
private function addGroup(string $pattern, Closure $handler): static {
$this->saveGroupPattern($pattern);
$this->executeGroupHandler($handler);
/*
* Remove the last group pattern from the group patterns list.
* This step is performed only after all calls for adding
* groups/routes inside the scope of the current group
* handler have finished their processing.
*/
$this->popLastGroupPattern();
return $this;
}
/**
* Save a group pattern.
*
* @param string $pattern A group pattern.
* @return static
*/
private function saveGroupPattern(string $pattern): static {
$this->groupPatterns[] = $pattern;
return $this;
}
/**
* Execute a group handler.
*
* {@internal This method temporarily binds the given group handler to
* the instance of the route collection - defined by the argument of
* Closure::call - and executes it. Inside the scope of the group handler,
* the route collection will be accessed using the keyword "$this".}
*
* @link https://www.php.net/manual/en/closure.call.php Closure::call
*
* @param Closure $handler A group handler.
* @return static The value returned by executing the group handler.
*/
private function executeGroupHandler(Closure $handler): static {
$handler->call($this);
return $this;
}
/**
* Pop and return the last group pattern in the list of group patterns.
*
* The list will be shortened by one element.
*
* @return string|null The last group pattern, or null if the list is empty.
*/
private function popLastGroupPattern(): ?string {
return array_pop($this->groupPatterns);
}
/**
* @inheritDoc
*/
public function route(
string|array $methods,
string $pattern,
string|array|object $handler
): RouteInterface {
return $this->addRoute($methods, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function routeForAllMethods(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(self::ROUTE_ALLOWED_METHODS, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function get(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_GET, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function head(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_HEAD, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function post(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_POST, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function put(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_PUT, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function delete(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_DELETE, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function connect(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_CONNECT, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function options(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_OPTIONS, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function trace(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_TRACE, $pattern, $handler);
}
/**
* @inheritDoc
*/
public function patch(string $pattern, string|array|object $handler): RouteInterface {
return $this->addRoute(RequestMethod::METHOD_PATCH, $pattern, $handler);
}
/**
* Add a route.
*
* @param string|string[] $methods One or more HTTP methods.
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
private function addRoute(
string|array $methods,
string $pattern,
string|array|object $handler
): RouteInterface {
$this
->validateRouteMethods($methods)
->validateRouteHandler($handler)
;
$prefixedPattern = $this->prefixRoutePatternWithGroupPatterns($pattern);
$route = $this->createRoute($methods, $prefixedPattern, $handler);
return $this->saveRoute($route);
}
/**
* Validate a list of route methods.
*
* @param string|string[] $methods One or more HTTP methods.
* @return static
* @throws \InvalidArgumentException The list of HTTP methods is empty.
*/
private function validateRouteMethods(string|array $methods): static {
if (empty($methods)) {
throw new \InvalidArgumentException('One or more HTTP methods must be provided.');
}
return $this;
}
/**
* Validate a route handler.
*
* @param string|array|object $handler A route handler.
* @return static
* @throws \InvalidArgumentException An empty route handler.
*/
private function validateRouteHandler(string|array|object $handler): static {
if (empty($handler)) {
throw new \InvalidArgumentException('The route handler can not be empty.');
}
return $this;
}
/**
* Prefix a route pattern with the pattern formed by
* concatenating the list of group patterns in a single string.
*
* @param string $pattern A route pattern.
* @return string The prefixed route pattern.
*/
private function prefixRoutePatternWithGroupPatterns(string $pattern): string {
return $this->getConcatenatedGroupPatterns() . $pattern;
}
/**
* Get the pattern formed by concatenating the
* list of group patterns in a single string.
*
* @return string The resulted pattern.
*/
private function getConcatenatedGroupPatterns(): string {
return $this->groupPatterns ? implode('', $this->groupPatterns) : '';
}
/**
* Create a route.
*
* @param string|string[] $methods One or more HTTP methods.
* @param string $pattern A route pattern.
* @param string|array|object $handler A route handler.
* @return RouteInterface The route.
*/
private function createRoute(
string|array $methods,
string $pattern,
string|array|object $handler
): RouteInterface {
return new Route($methods, $pattern, $handler);
}
/**
* Save a route.
*
* Before saving, an id is assigned to the route.
*
* @param RouteInterface $route A route.
* @return RouteInterface The route.
*/
private function saveRoute(RouteInterface $route): RouteInterface {
$id = $this->buildRouteId();
$route->setId($id);
$this->routes[$id] = $route;
$this->routeIdCounter++;
return $route;
}
/**
* Build a route id.
*
* The id is built by concatenating the
* route id prefix and the route id counter.
*
* @return string The route id.
*/
private function buildRouteId(): string {
return self::ROUTE_ID_PREFIX . (string) $this->routeIdCounter;
}
/**
* @inheritDoc
*/
public function getRouteById(string $id): RouteInterface {
if (!$this->exists($id)) {
throw new \UnexpectedValueException(
'A route with the id "' . $id . '" could not be found.'
);
}
return $this->routes[$id];
}
/**
* @inheritDoc
*/
public function getRouteByName(string $name): RouteInterface {
foreach ($this->routes as $route) {
if ($route->getName() === $name) {
return $route;
}
}
throw new \UnexpectedValueException(
'A route with the name "' . $name . '" could not be found.'
);
}
/**
* @inheritDoc
*/
public function exists(string $id): bool {
return array_key_exists($id, $this->routes);
}
/**
* @inheritDoc
*/
public function remove(string $id): static {
if ($this->exists($id)) {
unset($this->routes[$id]);
}
return $this;
}
/**
* @inheritDoc
*/
public function all(): array {
return $this->routes;
}
/**
* @inheritDoc
*/
public function clear(): static {
$this->routes = [];
return $this;
}
/**
* @inheritDoc
*/
public function isEmpty(): bool {
return !($this->count() > 0);
}
/**
* @inheritDoc
*/
public function count(): int {
return count($this->routes);
}
/**
* @inheritDoc
*/
public function getIterator(): \Traversable {
return new \ArrayIterator($this->routes);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.