成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Laravel核心解讀--控制器

fxp / 3326人閱讀

摘要:下面是剛才說的這些步驟對應的核心代碼收集路由和控制器里應用的中間件我們在前面的文章里已經(jīng)詳細的解釋過中間件和路由的原理了,接下來就看看當請求最終找到了路由對應的控制器方法后是如何為控制器方法注入正確的參數(shù)并調(diào)用控制器方法的。

控制器

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

protected $middleware = [
    IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,
    IlluminateFoundationHttpMiddlewareValidatePostSize::class,
    AppHttpMiddlewareTrimStrings::class,
    IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::class,
    AppHttpMiddlewareTrustProxies::class,
];

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

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

namespace IlluminateFoundationHttp;
class Kernel implements KernelContract
{
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance("request", $request);

            return $this->router->dispatch($request);
        };
    }
}


namespace IlluminateRouting;
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 EventsRouteMatched($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 IlluminateRouting;
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" => "AppHttpControllersSomeController@someAction",
    "controller" => "AppHttpControllersSomeController@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
{
    //解析路由里綁定的控制器方法字符串,返回控制器和方法名稱字符串構成的數(shù)組
    public static function parseCallback($callback, $default = null)
    {
        return static::contains($callback, "@") ? explode("@", $callback, 2) : [$callback, $default];
    }
}

所以路由通過parseCallback方法將uses配置項里的控制器字符串解析成數(shù)組返回, 數(shù)組第一項為控制器名稱、第二項為方法名稱。在拿到控制器和方法的名稱字符串后,路由對象將自身、控制器和方法名傳遞給了IlluminateRoutingControllerDispatcher類,由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ù)中沒有相同類的對象那么就會通過服務容器來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]
        );
    }

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

當路由的參數(shù)數(shù)組與服務容器構造的類對象數(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í)行完拿到結果后,按照上面runRouteWithinStack里的邏輯,結果會被轉(zhuǎn)換成響應對象。然后響應對象會依次經(jīng)過之前應用過的所有中間件的后置操作,最后返回給客戶端。

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

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/30778.html

相關文章

  • Laravel核心解讀 -- 用戶認證系統(tǒng)(基礎介紹)

    摘要:系統(tǒng)的核心是由的認證組件的看守器和提供器組成。使用的認證系統(tǒng),幾乎所有東西都已經(jīng)為你配置好了。其配置文件位于,其中包含了用于調(diào)整認證服務行為的注釋清晰的選項配置。 用戶認證系統(tǒng)(基礎介紹) 使用過Laravel的開發(fā)者都知道,Laravel自帶了一個認證系統(tǒng)來提供基本的用戶注冊、登錄、認證、找回密碼,如果Auth系統(tǒng)里提供的基礎功能不滿足需求還可以很方便的在這些基礎功能上進行擴展。這篇...

    RebeccaZhong 評論0 收藏0
  • Laravel核心解讀--Contracts契約

    摘要:的契約是一組定義框架提供的核心服務的接口,例如我們在介紹用戶認證的章節(jié)中到的用戶看守器契約和用戶提供器契約以及框架自帶的模型所實現(xiàn)的契約。接口與團隊開發(fā)當你的團隊在開發(fā)大型應用時,不同的部分有著不同的開發(fā)速度。 Contracts Laravel 的契約是一組定義框架提供的核心服務的接口, 例如我們在介紹用戶認證的章節(jié)中到的用戶看守器契約IllumninateContractsAuth...

    Prasanta 評論0 收藏0
  • Laravel核心解讀--完結篇

    摘要:過去一年時間寫了多篇文章來探討了我認為的框架最核心部分的設計思路代碼實現(xiàn)。為了大家閱讀方便,我把這些源碼學習的文章匯總到這里。數(shù)據(jù)庫算法和數(shù)據(jù)結構這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復雜問題。 過去一年時間寫了20多篇文章來探討了我認為的Larave框架最核心部分的設計思路、代碼實現(xiàn)。通過更新文章自己在軟件設計、文字表達方面都有所提高,在剛開始決定寫Laravel源碼分析地...

    laoLiueizo 評論0 收藏0
  • Laravel核心解讀 -- Response

    摘要:設置生成對象后就要執(zhí)行對象的方法了,該方法定義在類中,其主要目的是對進行微調(diào)使其能夠遵從協(xié)議。最后會把完整的響應發(fā)送給客戶端。本文已經(jīng)收錄在系列文章源碼學習里,歡迎訪問閱讀。 Response 前面兩節(jié)我們分別講了Laravel的控制器和Request對象,在講Request對象的那一節(jié)我們看了Request對象是如何被創(chuàng)建出來的以及它支持的方法都定義在哪里,講控制器時我們詳細地描述了...

    TigerChain 評論0 收藏0
  • Laravel核心解讀--中間件(Middleware)

    摘要:解析出后將進入應用的請求對象傳遞給的方法,在方法負責處理流入應用的請求對象并返回響應對象。攜帶了本次迭代的值。通過這種方式讓請求對象依次流過了要通過的中間件,達到目的地的方法。 中間件(Middleware)在Laravel中起著過濾進入應用的HTTP請求對象(Request)和完善離開應用的HTTP響應對象(Reponse)的作用, 而且可以通過應用多個中間件來層層過濾請求、逐步完善...

    enda 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<