1

need

Recently, due to the needs of business functions, it is necessary to match the routing instance according to the request path (such as admin/auth/menu/46/edit ) and request method (such as GET ) recorded in the database, and then do some other things after getting the routing instance.

analyze

In fact, it is the core function of routing (matching a class of request mapping to a callback type variable). The function that comes with the framework itself, the source code search is implemented by the following code blocks:

 // Illuminate/Routing/RouteCollection.php public function match(Request $request) { // 1. 获取路由集合$routes = $this->get($request->getMethod()); // 2. 匹配路由$route = $this->matchAgainstRoutes($routes, $request); return $this->handleMatchedRoute($request, $route); } // Illuminate/Routing/AbstractRouteCollection.php protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true) { [$fallbacks, $routes] = collect($routes)->partition(function ($route) { return $route->isFallback; }); return $routes->merge($fallbacks)->first(function (Route $route) use ($request, $includingMethod) { // 3. 遍历匹配return $route->matches($request, $includingMethod); }); } // Illuminate/Routing/Route.php public function matches(Request $request, $includingMethod = true) { $this->compileRoute(); foreach ($this->getValidators() as $validator) { // 4. 遍历验证器验证匹配if (! $includingMethod && $validator instanceof MethodValidator) { continue; } if (! $validator->matches($this, $request)) { return false; } } return true; }

The general logic of the code is: traverse the route collection, throw the current request to the four validators UriValidator , MethodValidator , SchemeValidator , HostValidator to verify the matches one by one. The validator mainly verifies the pathInfo , method , scheme and host information of the matching request object.

So I just need to modify the information of the current request object and throw it to the matches method above to achieve the function I need.

accomplish

Since it is the same project, the scheme and host are consistent with the current request and do not need to be modified. And pathInfo and method are two private attributes and the method for which the corresponding write permission is not found. So implement a macro method that has the ability to write private properties. The final code is as follows:

 <?php namespace App\Support\Macros; use Illuminate\Routing\Route; use Illuminate\Routing\Router; use Illuminate\Support\Arr; use InvalidArgumentException; class RequestMacro { /** * 修改属性*/ public function propertyAware(): callable { return function ($property, $value){ /** @var \Illuminate\Http\Request $this */ if (! property_exists($this, $property)) { throw new InvalidArgumentException('The property not exists.'); } $this->{$property} = $value; return $this; }; } /** * 匹配路由*/ public function matchRoute(): callable { return function ($includingMethod = true){ // 1. 获取路由集合/* @var \Illuminate\Routing\RouteCollection $routeCollection */ $routeCollection = app(Router::class)->getRoutes(); /** @var \Illuminate\Http\Request $this */ $routes = is_null($this->method()) ? $routeCollection->getRoutes() : Arr::get($routeCollection->getRoutesByMethod(), $this->method(), []); [$fallbacks, $routes] = collect($routes)->partition(function ($route){ return $route->isFallback; }); return $routes->merge($fallbacks)->first(function (Route $route) use ($includingMethod){ // 2. 遍历匹配return $route->matches($this, $includingMethod); }); }; } }

Registration request macro

 // App\Providers\AppServiceProvider public function register() { Request::mixin($this->app->make(RequestMacro::class)); }

Example of use

 $route = request() ->propertyAware('pathInfo', \Illuminate\Support\Str::start('admin/auth/menu/46/edit', '/')) ->propertyAware('method', 'GET') ->matchRoute(); dump($route);

Original link


guanguans
2.4k 声望89 粉丝

No practice, no gain in one's wit.