日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Laravel核心解读--控制器

發(fā)布時間:2023/12/10 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Laravel核心解读--控制器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

控制器

控制器能夠?qū)⑾嚓P(guān)的請求處理邏輯組成一個單獨的類, 通過前面的路由和中間件兩個章節(jié)我們多次強調(diào)Laravel應用的請求在進入應用后首現(xiàn)會通過Http Kernel里定義的基本中間件

protected $middleware = [\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,\App\Http\Middleware\TrimStrings::class,\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,\App\Http\Middleware\TrustProxies::class, ];

然后Http Kernel會通過dispatchToRoute將請求對象移交給路由對象進行處理,路由對象會收集路由上綁定的中間件然后還是像上面Http Kernel里一樣用一個Pipeline管道對象將請求傳送通過這些路由上綁定的這些中間鍵,到達目的地后會執(zhí)行路由綁定的控制器方法然后把執(zhí)行結(jié)果封裝成響應對象,響應對象一次通過后置中間件最后返回給客戶端。

下面是剛才說的這些步驟對應的核心代碼:

namespace Illuminate\Foundation\Http; class Kernel implements KernelContract {protected function dispatchToRouter(){return function ($request) {$this->app->instance('request', $request);return $this->router->dispatch($request);};} }namespace Illuminate\Routing; class Router implements RegistrarContract, BindingRegistrar { public function dispatch(Request $request){$this->currentRequest = $request;return $this->dispatchToRoute($request);}public function dispatchToRoute(Request $request){return $this->runRoute($request, $this->findRoute($request));}protected function runRoute(Request $request, Route $route){$request->setRouteResolver(function () use ($route) {return $route;});$this->events->dispatch(new Events\RouteMatched($route, $request));return $this->prepareResponse($request,$this->runRouteWithinStack($route, $request));}protected function runRouteWithinStack(Route $route, Request $request){$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&$this->container->make('middleware.disable') === true;//收集路由和控制器里應用的中間件$middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);return (new Pipeline($this->container))->send($request)->through($middleware)->then(function ($request) use ($route) {return $this->prepareResponse($request, $route->run());});} }namespace Illuminate\Routing; class Route {public function run(){$this->container = $this->container ?: new Container;try {if ($this->isControllerAction()) {return $this->runController();}return $this->runCallable();} catch (HttpResponseException $e) {return $e->getResponse();}}}

我們在前面的文章里已經(jīng)詳細的解釋過Pipeline、中間件和路由的原理了,接下來就看看當請求最終找到了路由對應的控制器方法后Laravel是如何為控制器方法注入正確的參數(shù)并調(diào)用控制器方法的。

解析控制器和方法名

路由運行控制器方法的操作runController首現(xiàn)會解析出路由中對應的控制器名稱和方法名稱。我們在講路由那一章里說過路由對象的action屬性都是類似下面這樣的:

['uses' => 'App\Http\Controllers\SomeController@someAction','controller' => 'App\Http\Controllers\SomeController@someAction','middleware' => ... ] class Route {protected function isControllerAction(){return is_string($this->action['uses']);}protected function runController(){return (new ControllerDispatcher($this->container))->dispatch($this, $this->getController(), $this->getControllerMethod());}public function getController(){if (! $this->controller) {$class = $this->parseControllerCallback()[0];$this->controller = $this->container->make(ltrim($class, '\\'));}return $this->controller;}protected function getControllerMethod(){return $this->parseControllerCallback()[1];}protected function parseControllerCallback(){return Str::parseCallback($this->action['uses']);} }class Str {//解析路由里綁定的控制器方法字符串,返回控制器和方法名稱字符串構(gòu)成的數(shù)組public static function parseCallback($callback, $default = null){return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];} }

所以路由通過parseCallback方法將uses配置項里的控制器字符串解析成數(shù)組返回, 數(shù)組第一項為控制器名稱、第二項為方法名稱。在拿到控制器和方法的名稱字符串后,路由對象將自身、控制器和方法名傳遞給了Illuminate\Routing\ControllerDispatcher類,由ControllerDispatcher來完成最終的控制器方法的調(diào)用。下面我們詳細看看ControllerDispatcher是怎么來調(diào)用控制器方法的。

class ControllerDispatcher {use RouteDependencyResolverTrait;public function dispatch(Route $route, $controller, $method){$parameters = $this->resolveClassMethodDependencies($route->parametersWithoutNulls(), $controller, $method);if (method_exists($controller, 'callAction')) {return $controller->callAction($method, $parameters);}return $controller->{$method}(...array_values($parameters));} }

上面可以很清晰地看出,ControllerDispatcher里控制器的運行分為兩步:解決method的參數(shù)依賴resolveClassMethodDependencies、調(diào)用控制器方法。

解決method參數(shù)依賴

解決方法的參數(shù)依賴通過RouteDependencyResolverTrait這一trait負責:

trait RouteDependencyResolverTrait {protected function resolveClassMethodDependencies(array $parameters, $instance, $method){if (! method_exists($instance, $method)) {return $parameters;}return $this->resolveMethodDependencies($parameters, new ReflectionMethod($instance, $method));}//參數(shù)為路由參數(shù)數(shù)組$parameters(可為空array)和控制器方法的反射對象public function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector){$instanceCount = 0;$values = array_values($parameters);foreach ($reflector->getParameters() as $key => $parameter) {$instance = $this->transformDependency($parameter, $parameters);if (! is_null($instance)) {$instanceCount++;$this->spliceIntoParameters($parameters, $key, $instance);} elseif (! isset($values[$key - $instanceCount]) &&$parameter->isDefaultValueAvailable()) {$this->spliceIntoParameters($parameters, $key, $parameter->getDefaultValue());}}return $parameters;}}

在解決方法的參數(shù)依賴時會應用到PHP反射的ReflectionMethod類來對控制器方法進行方向工程, 通過反射對象獲取到參數(shù)后會判斷現(xiàn)有參數(shù)的類型提示(type hint)是否是一個類對象參數(shù),如果是類對象參數(shù)并且在現(xiàn)有參數(shù)中沒有相同類的對象那么就會通過服務(wù)容器來make出類對象。

protected function transformDependency(ReflectionParameter $parameter, $parameters){$class = $parameter->getClass();if ($class && ! $this->alreadyInParameters($class->name, $parameters)) {return $parameter->isDefaultValueAvailable()? $parameter->getDefaultValue(): $this->container->make($class->name);}}protected function alreadyInParameters($class, array $parameters){return ! is_null(Arr::first($parameters, function ($value) use ($class) {return $value instanceof $class;}));}

解析出類對象后需要將類對象插入到參數(shù)列表中去

protected function spliceIntoParameters(array &$parameters, $offset, $value){array_splice($parameters, $offset, 0, [$value]);}

我們之前講服務(wù)容器時,里面講的服務(wù)解析解決是類構(gòu)造方法的參數(shù)依賴,而這里resolveClassMethodDependencies里解決的是具體某個方法的參數(shù)依賴,它Laravel對method dependency injection概念的實現(xiàn)。

當路由的參數(shù)數(shù)組與服務(wù)容器構(gòu)造的類對象數(shù)量之和不足以覆蓋控制器方法參數(shù)個數(shù)時,就要去判斷該參數(shù)是否具有默認參數(shù),也就是會執(zhí)行resolveMethodDependencies方法foreach塊里的else if分支將參數(shù)的默認參數(shù)插入到方法的參數(shù)列表$parameters中去。

} elseif (! isset($values[$key - $instanceCount]) &&$parameter->isDefaultValueAvailable()) {$this->spliceIntoParameters($parameters, $key, $parameter->getDefaultValue()); }

調(diào)用控制器方法

解決完method的參數(shù)依賴后就該調(diào)用方法了,這個很簡單, 如果控制器有callAction方法就會調(diào)用callAction方法,否則的話就直接調(diào)用方法。

public function dispatch(Route $route, $controller, $method){$parameters = $this->resolveClassMethodDependencies($route->parametersWithoutNulls(), $controller, $method);if (method_exists($controller, 'callAction')) {return $controller->callAction($method, $parameters);}return $controller->{$method}(...array_values($parameters));}

執(zhí)行完拿到結(jié)果后,按照上面runRouteWithinStack里的邏輯,結(jié)果會被轉(zhuǎn)換成響應對象。然后響應對象會依次經(jīng)過之前應用過的所有中間件的后置操作,最后返回給客戶端。

本文已經(jīng)收錄在系列文章Laravel源碼學習里,歡迎訪問閱讀。

總結(jié)

以上是生活随笔為你收集整理的Laravel核心解读--控制器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。