摘要:請(qǐng)求綁定取得有效的對(duì)應(yīng)的參數(shù)鍵值對(duì)取得有效的對(duì)應(yīng)的主機(jī)鍵值對(duì),并合并到參數(shù)鍵值對(duì)從中取出正則匹配的相應(yīng)的值類(lèi)似從上取出參數(shù)數(shù)組,并去掉小結(jié)本質(zhì)就是對(duì)象參數(shù)屬性的處理。
Laravel 路由處理 代碼展示
protected function sendRequestThroughRouter($request) { # $this->app->instance("request", $request); # Facade::clearResolvedInstance("request"); # $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }路由解析 請(qǐng)求分發(fā)
protected function dispatchToRouter() { return function ($request) { $this->app->instance("request", $request); return $this->router->dispatch($request); }; } public function dispatch(Request $request) { $this->currentRequest = $request; return $this->dispatchToRoute($request); } public function dispatchToRoute(Request $request) { $route = $this->findRoute($request); $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new EventsRouteMatched($route, $request)); // 至此,系統(tǒng)的流程走到了控制器相關(guān)的處理,后面待續(xù) $response = $this->runRouteWithinStack($route, $request); return $this->prepareResponse($request, $response); }路由查找
protected function findRoute($request) { $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route; } public function match(Request $request) { $routes = $this->get($request->getMethod()); // 根據(jù)請(qǐng)求匹配到第一個(gè)相應(yīng)的路由 $route = $this->matchAgainstRoutes($routes, $request); if (! is_null($route)) { return $route->bind($request); } // 若沒(méi)有找到,則按照["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]順序再找一遍 $others = $this->checkForAlternateVerbs($request); if (count($others) > 0) { return $this->getRouteForMethods($request, $others); } throw new NotFoundHttpException; } public function get($method = null) { return is_null($method) ? $this->getRoutes() : Arr::get($this->routes, $method, []); } protected function getRouteForMethods($request, array $methods) { if ($request->method() == "OPTIONS") { return (new Route("OPTIONS", $request->path(), function () use ($methods) { return new Response("", 200, ["Allow" => implode(",", $methods)]); }))->bind($request); } $this->methodNotAllowed($methods); }
protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true) { return Arr::first($routes, function ($value) use ($request, $includingMethod) { return $value->matches($request, $includingMethod); }); } public function matches(Request $request, $includingMethod = true) { // 解析路由 $this->compileRoute(); // 檢驗(yàn)(uri正則匹配、方法是否存在、http(s)請(qǐng)求、主機(jī)正則匹配)均通過(guò),則返回真 foreach ($this->getValidators() as $validator) { if (! $includingMethod && $validator instanceof MethodValidator) { continue; } if (! $validator->matches($this, $request)) { return false; } } return true; } public static function getValidators() { if (isset(static::$validators)) { return static::$validators; } return static::$validators = [ new UriValidator, new MethodValidator, new SchemeValidator, new HostValidator, ]; } protected function compileRoute() { if (! $this->compiled) { $this->compiled = (new RouteCompiler($this))->compile(); } return $this->compiled; } // 記錄可選參數(shù)并統(tǒng)一 uri 形式,后面進(jìn)行統(tǒng)一的處理 public function compile() { $optionals = $this->getOptionalParameters(); $uri = preg_replace("/{(w+?)?}/", "{$1}", $this->route->uri()); // 構(gòu)造成 symfony 的路由形式,再委托 SymfonyComponentRoutingRouteCompiler 處理 return ( new SymfonyRoute($uri, $optionals, $this->route->wheres, [], $this->route->domain() ?: "") )->compile(); } protected function getOptionalParameters() { preg_match_all("/{(w+?)?}/", $this->route->uri(), $matches); return isset($matches[1]) ? array_fill_keys($matches[1], null) : []; } // 初始化處理(路徑和參數(shù)的規(guī)格化,合并 options,設(shè)置主機(jī)等) public function __construct($path, array $defaults = array(), array $requirements = array(), array $options = array(), $host = "", $schemes = array(), $methods = array(), $condition = "") { $this->setPath($path); $this->setDefaults($defaults); $this->setRequirements($requirements); $this->setOptions($options); // 合并["compiler_class" => "SymfonyComponentRoutingRouteCompiler",] 和 $options $this->setHost($host); $this->setSchemes($schemes); $this->setMethods($methods); $this->setCondition($condition); } public function compile() { if (null !== $this->compiled) { return $this->compiled; } $class = $this->getOption("compiler_class"); // 委托 SymfonyComponentRoutingRouteCompiler 來(lái)處理,返回 SymfonyComponentRoutingCompiledRoute 對(duì)象 return $this->compiled = $class::compile($this); } public static function compile(Route $route) { $hostVariables = array(); $variables = array(); $hostRegex = null; $hostTokens = array(); if ("" !== $host = $route->getHost()) { $result = self::compilePattern($route, $host, true); $hostVariables = $result["variables"]; $variables = $hostVariables; $hostTokens = $result["tokens"]; $hostRegex = $result["regex"]; } $path = $route->getPath(); $result = self::compilePattern($route, $path, false); $staticPrefix = $result["staticPrefix"]; $pathVariables = $result["variables"]; foreach ($pathVariables as $pathParam) { if ("_fragment" === $pathParam) { throw new InvalidArgumentException(sprintf("Route pattern "%s" cannot contain "_fragment" as a path parameter.", $route->getPath())); } } $variables = array_merge($variables, $pathVariables); $tokens = $result["tokens"]; $regex = $result["regex"]; // 委托 CompiledRoute 返回?cái)?shù)據(jù),數(shù)據(jù)沒(méi)做什么處理,只是多提供了序列化和反序列化方法 return new CompiledRoute( $staticPrefix, $regex, $tokens, $pathVariables, $hostRegex, $hostTokens, $hostVariables, array_unique($variables) ); } // 核心方法 private static function compilePattern(Route $route, $pattern, $isHost) { $tokens = array(); $variables = array(); $matches = array(); $pos = 0; $defaultSeparator = $isHost ? "." : "/"; // 主機(jī)或路徑默認(rèn)分割符 $useUtf8 = preg_match("http://u", $pattern); $needsUtf8 = $route->getOption("utf8"); if (!$needsUtf8 && $useUtf8 && preg_match("/[x80-xFF]/", $pattern)) { $needsUtf8 = true; @trigger_error(sprintf("Using UTF-8 route patterns without setting the "utf8" option is deprecated since Symfony 3.2 and will throw a LogicException in 4.0. Turn on the "utf8" route option for pattern "%s".", $pattern), E_USER_DEPRECATED); } if (!$useUtf8 && $needsUtf8) { throw new LogicException(sprintf("Cannot mix UTF-8 requirements with non-UTF-8 pattern "%s".", $pattern)); } // 解析類(lèi)似 $pattern("/posts/{post}/comments/{comment}") 里面的 {w+} 到 $matches ($matches 類(lèi)似 [[["{post}",7]],[["{comment}",23]]],值和位置) preg_match_all("#{w+}#", $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); foreach ($matches as $match) { $varName = substr($match[0][0], 1, -1); // 取方括號(hào)中間的值,即變量名稱(chēng) $precedingText = substr($pattern, $pos, $match[0][1] - $pos); // 取前一個(gè)變量末尾位置后一位到({)的前一段文本 $pos = $match[0][1] + strlen($match[0][0]); // 取(})后一段的(/)位置 // 嘗試取前一段的最后一個(gè)字符 if (!strlen($precedingText)) { $precedingChar = ""; } elseif ($useUtf8) { preg_match("/.$/u", $precedingText, $precedingChar); $precedingChar = $precedingChar[0]; } else { $precedingChar = substr($precedingText, -1); } $isSeparator = "" !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar); // 是否分割符 if (preg_match("/^d/", $varName)) { throw new DomainException(sprintf("Variable name "%s" cannot start with a digit in route pattern "%s". Please use a different name.", $varName, $pattern)); } if (in_array($varName, $variables)) { throw new LogicException(sprintf("Route pattern "%s" cannot reference variable name "%s" more than once.", $pattern, $varName)); } if (strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) { throw new DomainException(sprintf("Variable name "%s" cannot be longer than %s characters in route pattern "%s". Please use a shorter name.", $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern)); } // 若前一段有值,即文本形式。則入 $tokens 數(shù)組 類(lèi)似: $tokens[] = ["text", "/posts",] if ($isSeparator && $precedingText !== $precedingChar) { $tokens[] = array("text", substr($precedingText, 0, -strlen($precedingChar))); } elseif (!$isSeparator && strlen($precedingText) > 0) { $tokens[] = array("text", $precedingText); } // 嘗試獲取 where 條件設(shè)置的正則 $regexp = $route->getRequirement($varName); // 沒(méi)有則用最寬松的方式處理 if (null === $regexp) { // 取分割符后面的內(nèi)容 類(lèi)似: "/comments/{comment}" $followingPattern = (string) substr($pattern, $pos); // 找出下一個(gè)分割符 類(lèi)似: "/" $nextSeparator = self::findNextSeparator($followingPattern, $useUtf8); // 構(gòu)造 regexp 類(lèi)似: "[^/]" $regexp = sprintf( "[^%s%s]+", preg_quote($defaultSeparator, self::REGEX_DELIMITER), $defaultSeparator !== $nextSeparator && "" !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : "" ); // "[^/]+" if (("" !== $nextSeparator && !preg_match("#^{w+}#", $followingPattern)) || "" === $followingPattern) { $regexp .= "+"; } } else { if (!preg_match("http://u", $regexp)) { $useUtf8 = false; } elseif (!$needsUtf8 && preg_match("/[x80-xFF]|(?= 0; --$i) { $token = $tokens[$i]; if ("variable" === $token[0] && $route->hasDefault($token[3])) { $firstOptional = $i; } else { break; } } } // 構(gòu)造正則 $regexp = ""; for ($i = 0, $nbToken = count($tokens); $i < $nbToken; ++$i) { $regexp .= self::computeRegexp($tokens, $i, $firstOptional); } // 類(lèi)似: "#^/posts/(?P[^/]+)/comments/(?P [^/]+)$#si" $regexp = self::REGEX_DELIMITER."^".$regexp."$".self::REGEX_DELIMITER."s".($isHost ? "i" : ""); if ($needsUtf8) { $regexp .= "u"; for ($i = 0, $nbToken = count($tokens); $i < $nbToken; ++$i) { if ("variable" === $tokens[$i][0]) { $tokens[$i][] = true; } } } return array( "staticPrefix" => "text" === $tokens[0][0] ? $tokens[0][1] : "", // 設(shè)置靜態(tài)的前綴 "regex" => $regexp, "tokens" => array_reverse($tokens), "variables" => $variables, ); } private static function computeRegexp(array $tokens, $index, $firstOptional) { $token = $tokens[$index]; // 文本形式,直接轉(zhuǎn)義返回 if ("text" === $token[0]) { return preg_quote($token[1], self::REGEX_DELIMITER); } // 參數(shù)形式 else { // 第一個(gè)就是可選項(xiàng)的處理 if (0 === $index && 0 === $firstOptional) { return sprintf("%s(?P<%s>%s)?", preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); } else { $regexp = sprintf("%s(?P<%s>%s)", preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); // 可選項(xiàng)后面的正則處理,均變?yōu)榭蛇x項(xiàng)。 if ($index >= $firstOptional) { $regexp = "(?:$regexp"; $nbTokens = count($tokens); if ($nbTokens - 1 == $index) { $regexp .= str_repeat(")?", $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0)); } } return $regexp; } } }
小結(jié)(路徑 /posts/{post}/comments/{comment}):
路由解析主要就是解析路由的主機(jī)和路徑部分(帶參數(shù)部分),差別在于分割符不一樣。并將解析結(jié)果放到 route 對(duì)象的 $compiled 屬性供后續(xù)使用。
重點(diǎn)是先將其分割成對(duì)應(yīng)的文本和變量部分( $tokens = [["text", "/posts",],["variable", "/", "1+", "post"],["text", "/comments",], $variables = ["post", "comment",])。
再根據(jù)上面分割的數(shù)組構(gòu)造相應(yīng)的正則表達(dá)式(會(huì)使用到 where 條件設(shè)置的正則)。
最后返回一個(gè)數(shù)組,表明此路由對(duì)應(yīng)的靜態(tài)前綴、正則表達(dá)式、tokens、variables。
$route->bind($request): public function bind(Request $request) { $this->compileRoute(); $this->parameters = (new RouteParameterBinder($this)) ->parameters($request); return $this; } new RouteParameterBinder($this): public function __construct($route) { $this->route = $route; } public function parameters($request) { // 取得有效的對(duì)應(yīng)的參數(shù)鍵值對(duì) $parameters = $this->bindPathParameters($request); if (! is_null($this->route->compiled->getHostRegex())) { // 取得有效的對(duì)應(yīng)的主機(jī)鍵值對(duì),并合并到參數(shù)鍵值對(duì) $parameters = $this->bindHostParameters( $request, $parameters ); } return $this->replaceDefaults($parameters); } protected function bindPathParameters($request) { preg_match($this->route->compiled->getRegex(), "/".$request->decodedPath(), $matches); // 從 pathinfo 中取出正則匹配的相應(yīng)的值 類(lèi)似["post"=>1,"comment"=>2] return $this->matchToKeys(array_slice($matches, 1)); } public function decodedPath() { return rawurldecode($this->path()); } protected function matchToKeys(array $matches) { if (empty($parameterNames = $this->route->parameterNames())) { return []; } $parameters = array_intersect_key($matches, array_flip($parameterNames)); return array_filter($parameters, function ($value) { return is_string($value) && strlen($value) > 0; }); } public function parameterNames() { if (isset($this->parameterNames)) { return $this->parameterNames; } return $this->parameterNames = $this->compileParameterNames(); } protected function compileParameterNames() { // 從 uri 上取出參數(shù)數(shù)組,并去掉 "?" preg_match_all("/{(.*?)}/", $this->domain().$this->uri, $matches); return array_map(function ($m) { return trim($m, "?"); }, $matches[1]); } protected function bindHostParameters($request, $parameters) { preg_match($this->route->compiled->getHostRegex(), $request->getHost(), $matches); return array_merge($this->matchToKeys(array_slice($matches, 1)), $parameters); } protected function replaceDefaults(array $parameters) { foreach ($parameters as $key => $value) { $parameters[$key] = isset($value) ? $value : Arr::get($this->route->defaults, $key); } foreach ($this->route->defaults as $key => $value) { if (! isset($parameters[$key])) { $parameters[$key] = $value; } } return $parameters; }
小結(jié):
本質(zhì)就是 route 對(duì)象參數(shù)屬性 $parameters 的處理。根據(jù)路由解析得到的正則從實(shí)際請(qǐng)求的PATHINFO和HOST里面提取出相應(yīng)參數(shù)對(duì)應(yīng)的值,再進(jìn)行合并處理(包括默認(rèn)值的設(shè)置、追加默認(rèn)參數(shù)及對(duì)值得過(guò)濾)。
路由分離器設(shè)置public function setRouteResolver(Closure $callback) { $this->routeResolver = $callback; return $this; }事件分發(fā) 參考 Kernel實(shí)例化后的處理 路由執(zhí)行
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() ); }); }
獲取經(jīng)過(guò)優(yōu)先級(jí)處理過(guò)的所有的非全局中間件
public function gatherRouteMiddleware(Route $route) { // 返回 object(IlluminateSupportCollection),$items 屬性類(lèi)似 ["類(lèi)名","類(lèi)名:參數(shù)","匿名函數(shù)"] $middleware = collect($route->gatherMiddleware())->map(function ($name) { return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups); })->flatten(); return $this->sortMiddleware($middleware); } // 獲取本次請(qǐng)求設(shè)置的所有的非全局的 middleware (包括前置棧、middleware 方法、控制器 getMiddleware 方法) public function gatherMiddleware() { if (! is_null($this->computedMiddleware)) { return $this->computedMiddleware; } $this->computedMiddleware = []; return $this->computedMiddleware = array_unique(array_merge( $this->middleware(), $this->controllerMiddleware() ), SORT_REGULAR); } // 解析 middleware ,返回簡(jiǎn)單的形式(匿名函數(shù)或 middleware:parameters ),優(yōu)先從 $middleware 和 $middlewareGroups 取,沒(méi)有則直接返回 $name public static function resolve($name, $map, $middlewareGroups) { if ($name instanceof Closure) { return $name; } elseif (isset($map[$name]) && $map[$name] instanceof Closure) { return $map[$name]; } elseif (isset($middlewareGroups[$name])) { return static::parseMiddlewareGroup( $name, $map, $middlewareGroups ); } else { list($name, $parameters) = array_pad(explode(":", $name, 2), 2, null); return (isset($map[$name]) ? $map[$name] : $name). (! is_null($parameters) ? ":".$parameters : ""); } } protected static function parseMiddlewareGroup($name, $map, $middlewareGroups) { $results = []; foreach ($middlewareGroups[$name] as $middleware) { // 組內(nèi)部還是個(gè)組,遞歸執(zhí)行 if (isset($middlewareGroups[$middleware])) { $results = array_merge($results, static::parseMiddlewareGroup( $middleware, $map, $middlewareGroups )); continue; } list($middleware, $parameters) = array_pad( explode(":", $middleware, 2), 2, null ); if (isset($map[$middleware])) { $middleware = $map[$middleware]; } $results[] = $middleware.($parameters ? ":".$parameters : ""); } return $results; } ##################################### 排序塊 BEGIN ####################################### protected function sortMiddleware(Collection $middlewares) { return (new SortedMiddleware($this->middlewarePriority, $middlewares))->all(); } public function __construct(array $priorityMap, $middlewares) { if ($middlewares instanceof Collection) { $middlewares = $middlewares->all(); } $this->items = $this->sortMiddleware($priorityMap, $middlewares); } // 類(lèi)似插入排序 protected function sortMiddleware($priorityMap, $middlewares) { $lastIndex = 0; foreach ($middlewares as $index => $middleware) { // 匿名函數(shù)直接 continue if (! is_string($middleware)) { continue; } $stripped = head(explode(":", $middleware)); if (in_array($stripped, $priorityMap)) { $priorityIndex = array_search($stripped, $priorityMap); if (isset($lastPriorityIndex) && $priorityIndex < $lastPriorityIndex) { return $this->sortMiddleware( $priorityMap, array_values( $this->moveMiddleware($middlewares, $index, $lastIndex) ) ); } else { $lastIndex = $index; $lastPriorityIndex = $priorityIndex; } } } return array_values(array_unique($middlewares, SORT_REGULAR)); } protected function moveMiddleware($middlewares, $from, $to) { array_splice($middlewares, $to, 0, $middlewares[$from]); unset($middlewares[$from + 1]); return $middlewares; } ##################################### 排序塊 END #######################################
后續(xù)分解
public function run() { $this->container = $this->container ?: new Container; try { // 控制器形式的處理(Controller@Method) if ($this->isControllerAction()) { return $this->runController(); } // 匿名函數(shù)形式的處理 return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); } } public function prepareResponse($request, $response) { if ($response instanceof PsrResponseInterface) { $response = (new HttpFoundationFactory)->createResponse($response); } elseif (! $response instanceof SymfonyResponse) { $response = new Response($response); } return $response->prepare($request); }
/ ?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/22618.html
摘要:又限于層的內(nèi)容太多,我在這篇中將整理路由中間件控制器部分內(nèi)容。前者定義頁(yè)面路由,默認(rèn)應(yīng)用中間件組后者定義無(wú)狀態(tài)路由,會(huì)應(yīng)用中間件組。命名路由可以為指定路由或者控制器方法命名,也可以為已命名的路由生成。 showImg(https://segmentfault.com/img/remote/1460000010882838); 上圖列出了 Laravel HTTP 層的相關(guān)知識(shí)大綱。由于...
摘要:合適和夠用是最完美的追求。比如從頁(yè)面去請(qǐng)求的資源。它允許瀏覽器向跨源服務(wù)器,發(fā)出請(qǐng)求,從而克服了只能同源使用的限制。定義在中的路由都是無(wú)狀態(tài)的,并且會(huì)應(yīng)用中間件組。 關(guān)于作者 程序開(kāi)發(fā)人員,不拘泥于語(yǔ)言與技術(shù),目前主要從事PHP和前端開(kāi)發(fā),使用Laravel和VueJs,App端使用Apicloud混合式開(kāi)發(fā)。合適和夠用是最完美的追求。 個(gè)人網(wǎng)站:http://www.linganm...
摘要:合適和夠用是最完美的追求。比如從頁(yè)面去請(qǐng)求的資源。它允許瀏覽器向跨源服務(wù)器,發(fā)出請(qǐng)求,從而克服了只能同源使用的限制。定義在中的路由都是無(wú)狀態(tài)的,并且會(huì)應(yīng)用中間件組。 關(guān)于作者 程序開(kāi)發(fā)人員,不拘泥于語(yǔ)言與技術(shù),目前主要從事PHP和前端開(kāi)發(fā),使用Laravel和VueJs,App端使用Apicloud混合式開(kāi)發(fā)。合適和夠用是最完美的追求。 個(gè)人網(wǎng)站:http://www.linganm...
摘要:合適和夠用是最完美的追求。比如從頁(yè)面去請(qǐng)求的資源。它允許瀏覽器向跨源服務(wù)器,發(fā)出請(qǐng)求,從而克服了只能同源使用的限制。定義在中的路由都是無(wú)狀態(tài)的,并且會(huì)應(yīng)用中間件組。 關(guān)于作者 程序開(kāi)發(fā)人員,不拘泥于語(yǔ)言與技術(shù),目前主要從事PHP和前端開(kāi)發(fā),使用Laravel和VueJs,App端使用Apicloud混合式開(kāi)發(fā)。合適和夠用是最完美的追求。 個(gè)人網(wǎng)站:http://www.linganm...
摘要:而我的新輪子也并不是專(zhuān)門(mén)解決它的問(wèn)題的,而是順便解決而已。概述這個(gè)包,支持在所有的項(xiàng)目中使用。一旦出現(xiàn)成員,代表允許全部。列出允許跨域請(qǐng)求的方法列表,默認(rèn)是代表所有方法。信息地址嗯,新輪子,求一波。 showImg(https://segmentfault.com/img/bV5VxN?w=844&h=656); 是的,可能了解 Laravel 的都知道,在 Laravel 中簡(jiǎn)單的設(shè)...
閱讀 3493·2021-11-23 10:13
閱讀 863·2021-09-22 16:01
閱讀 909·2021-09-09 09:33
閱讀 630·2021-08-05 09:58
閱讀 1717·2019-08-30 11:14
閱讀 1935·2019-08-30 11:02
閱讀 3265·2019-08-29 16:28
閱讀 1478·2019-08-29 16:09