Автор оригинала: David Wong.
Laravel имеет множество конфигураций маршрутизации. Вы можете задать доменное имя, протокол запроса, режим запроса и путь запроса. Итак, что сделал laravel, чтобы соответствовать маршруту после получения запроса? В этой статье объясняется исходный код larave5.8, и вы шаг за шагом ознакомитесь с исходным кодом.
Существует четыре валидатора для маршрута laravel по умолчанию. Url-идентификатор , Валидатор метода , |/Schemavalidator , HostValidator Соответственно обрабатывают сопоставление URI, сопоставление методов запроса, сопоставление протоколов и сопоставление доменных имен.
Например:
Средство проверки хостаУбедитесь, что доменное имя соответствует конфигурации домена
Route::domain('{account}.blog.dev')->function({
return 'Hello';
});Валидатор UriУбедитесь, что запрошенный URL-адрес соответствует конфигурации маршрутизации.Валидатор методовУбедитесь, что текущий метод запросаgetМетод
Route::get('/home/posts/{id?}',function($id=null){
return 'get post '.$id;
})Проверка схемыПротокол доступа для аутентификации в основном используется для проверки безопасных маршрутов. Можно проверить только HTTP или HTTPS
Route::get('foo', array('https', function(){}));Только после прохождения всех четырех валидаторов текущий запрос может успешно соответствовать маршруту.
Как эти четыре верификатора проверяют?
Проверка метода запроса
class MethodValidator implements ValidatorInterface
{
public function matches(Route $route, Request $request)
{
return in_array($request->getMethod(), $route->methods());
}
SchemeValidator
}Самый простой способ проверить режим запроса-проверить, является ли текущий режим запроса разрешенным текущим маршрутом. Разрешенный режим запроса маршрута создается при создании экземпляра маршрута.
Проверка протокола запроса
class SchemeValidator implements ValidatorInterface
{
public function matches(Route $route, Request $request)
{
if ($route->httpOnly()) {
return ! $request->secure();
} elseif ($route->secure()) {
return $request->secure();
}
return true;
}
}Получив текущий запрос Запросите , определите, является ли он HTTPS, и сравните его с текущей конфигурацией маршрутизации
Проверка доменного имени и URI-кода
Эти два вида проверки, по сути, одинаковы. Путем компиляции и декомпозиции конфигурации маршрута получается URI для получения регулярного выражения сопоставления доменных имен, а затем используется регулярное выражение для сопоставления. Если совпадение проходит успешно, проверка проходит.
Здесь, чтобы Urlvalidator В качестве примера
class UriValidator implements ValidatorInterface
{
/**
* Validate a given rule against a route and request.
*
* @param \Illuminate\Routing\Route $route
* @param \Illuminate\Http\Request $request
* @return bool
*/
public function matches(Route $route, Request $request)
{
$path = $request->path() === '/' ? '/' : '/'.$request->path();
return preg_match($route->getCompiled()->getRegex(), rawurldecode($path));
}
}Ключом здесь является получить компиляцию Объект вернулся. получить компиляцию Возвращаемое значение Symfony\Компонент\Маршрутизация\CompiledRoute Этот объект содержит регулярное выражение, соответствующее URI, регулярное выражение, соответствующее доменному имени, и другую информацию после компиляции текущего маршрута.
Составленный маршрут, кто вернулся?
Перед тем, как каждый маршрут будет проверен верификатором, компиляция маршрута Создание метода Составленный маршрут Объект.
//Illuminate\Routing\Route
public function matches(Request $request, $includingMethod = true)
{
$this->compileRoute();
foreach ($this->getValidators() as $validator) {
if (! $includingMethod && $validator instanceof MethodValidator) {
continue;
}
if (! $validator->matches($this, $request)) {
return false;
}
}
return true;
}
protected function compileRoute()
{
if (! $this->compiled) {
$this->compiled = (new RouteCompiler($this))->compile();
}
return $this->compiled;
}Осветить\Маршрутизацию\RouteCompiler в компиляции Метод заключается в следующем:
//use Symfony\Component\Routing\Route as SymfonyRoute;
public function compile()
{
$optionals = $this->getOptionalParameters();
$uri = preg_replace('/\{(\w+?)\?\}/', '{$1}', $this->route->uri());
return (
new SymfonyRoute($uri, $optionals, $this->route->wheres, ['utf8' => true], $this->route->getDomain() ?: '')
)->compile();
}//Symfony \ component \ routing \ route code
//compiler_class Symfony\Component\Routing\RouteCompiler
public function compile()
{
if (null !== $this->compiled) {
return $this->compiled;
}
$class = $this->getOption('compiler_class');
return $this->compiled = $class::compile($this);
}Видно, что окончательный Symfony\Component\Routing\RouteCompiler Из компиляции Вернуться к окончательному составленный маршрут Объект.
Что делает компиляция маршрута?
//Symfony \ component \ routing \ routecompiler source code
public static function compile(Route $route)
{
...
if ('' !== $host = $route->getHost()) {
$result = self::compilePattern($route, $host, true);
$hostVariables = $result['variables'];
$variables = $hostVariables;
$hostTokens = $result['tokens'];
$hostRegex = $result['regex'];
}
...
}Компилятор маршрутов::компиляция Входным параметром является текущий маршрут, который необходимо сопоставить. Во-первых, определите, имеет ли маршрут конфигурацию доменного имени. Если существует конфигурация доменного имени, скомпилируйте регулярное выражение конфигурации доменного имени, чтобы получить соответствующее регулярное выражение доменного имени и информацию о переменной в выражении.
//Symfony \ component \ routing \ routecompiler source code
public static function compile(Route $route)
{
...
$path = $route->getPath();
$result = self::compilePattern($route, $path, false);
$staticPrefix = $result['staticPrefix'];
$pathVariables = $result['variables'];
...
$variables = array_merge($variables, $pathVariables);
$tokens = $result['tokens'];
$regex = $result['regex'];
...
}Затем получите конфигурацию URI маршрута, проанализируйте конфигурацию и получите соответствующее регулярное выражение, массив переменных и информацию о префиксе.
После разрешения правила сопоставления доменного имени и пути создайте Составленный маршрут Объект и возврат
Поэтому в процессе компиляции маршрута соответствующие регулярное выражение, массив переменных и информация о префиксе анализируются в основном в соответствии с конфигурацией маршрута. И создайте Составленный маршрут Объект возвращается вызывающему объекту. Таким образом, вызывающий абонент может напрямую передать Составленный маршрут Атрибут получает правило соответствия непосредственно после разрешения маршрута.
Как решается правило сопоставления?
//Symfony \ component \ routing \ routecompiler source code
private static function compilePattern(Route $route, $pattern, $isHost)
{
...
preg_match_all('#\{(!)?(\w+)\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
foreach ($matches as $match) {
...
if ($isSeparator && $precedingText !== $precedingChar) {
$tokens[] = ['text', substr($precedingText, 0, -\strlen($precedingChar))];
} elseif (!$isSeparator && \strlen($precedingText) > 0) {
$tokens[] = ['text', $precedingText];
}
...
if ($important) {
$token = ['variable', $isSeparator ? $precedingChar : '', $regexp, $varName, false, true];
} else {
$token = ['variable', $isSeparator ? $precedingChar : '', $regexp, $varName];
}
...
}
...
}Во-первых, настроена ли переменная путем сопоставления регулярных выражений, например Маршрут::get('/сообщения/{идентификатор}') , Маршрут::домен('{учетная запись}.blog.dev') 。 Если есть переменные, правило конфигурации будет перехвачено, и часть правила конфигурации, которая не содержит переменных, будет $tokens[] = ['['текст', $предшествующий текст]; , для всех переменных $токен = ["переменная", $isSeparator ? $$предшествующий символ: ", $регулярное выражение, $varName, false, true] Сохраните проанализированную информацию.
//Symfony \ component \ routing \ routecompiler source code
private static function compilePattern(Route $route, $pattern, $isHost)
{
...
if ($pos < \strlen($pattern)) {
$tokens[] = ['text', substr($pattern, $pos)];
}
// find the first optional token
$firstOptional = PHP_INT_MAX;
if (!$isHost) {
for ($i = \count($tokens) - 1; $i >= 0; --$i) {
$token = $tokens[$i];
// variable is optional when it is not important and has a default value
if ('variable' === $token[0] && !($token[5] ?? false) && $route->hasDefault($token[3])) {
$firstOptional = $i;
} else {
break;
}
}
}
...Если информация о конфигурации не содержит никаких переменных, введите первое решение if в этом коде и сохраните правила сопоставления в токене Массив.
Различите, соответствует ли текущее разрешение доменному имени или URI. Если URI совпадает, узнайте расположение первого необязательного параметра в переменной.
Этот шаг заключается в преобразовании конфигурации маршрутизации в маркер правила соответствия. Удобно передавать каждый токен Генерирует соответствующее регулярное выражение.
//Symfony \ component \ routing \ routecompiler source code
private static function computeRegexp(array $tokens, int $index, int $firstOptional): string
{
$token = $tokens[$index];
if ('text' === $token[0]) {
return preg_quote($token[1], self::REGEX_DELIMITER);
} else {
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]);
if ($index >= $firstOptional) {
$regexp = "(?:$regexp";
$nbTokens = \count($tokens);
if ($nbTokens - 1 == $index) {
// Close the optional subpatterns
$regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0));
}
}
return $regexp;
}
}
}В результате анализа полученного массива токенов сохраняются все массивы правил соответствия. Если текущий маркер правила соответствия текст Введите, строка экранируется и возвращается в виде соответствующего регулярного выражения.
Если это переменная, необязательный идентификатор добавляется к регулярному выражению в зависимости от того, является ли он необязательным (местоположение первого необязательного параметра было найдено на предыдущем шаге).
//Symfony \ component \ routing \ routecompiler source code
private static function compilePattern(Route $route, $pattern, $isHost)
{
...
$regexp = '';
for ($i = 0, $nbToken = \count($tokens); $i < $nbToken; ++$i) {
$regexp .= self::computeRegexp($tokens, $i, $firstOptional);
}
$regexp = self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'sD'.($isHost ? 'i' : '');
...
return [
'staticPrefix' => self::determineStaticPrefix($route, $tokens),
'regex' => $regexp,
'tokens' => array_reverse($tokens),
'variables' => $variables,
];В соответствии с каждым токеном получается регулярное выражение каждого правила сопоставления. Все регулярные выражения объединяются в регулярное выражение, и добавляются префикс и суффикс регулярного выражения. Это приводит к полному совпадению регулярного выражения.
Затем префикс, сопоставьте регулярное выражение, сопоставьте массив правил токены , вызывающему объекту возвращается массив переменных. Генерируется вызывающим абонентом Составленный маршрут Объект.
Прикрепите блок – схему вызова процедуры сопоставления маршрутов laravel
Оригинал: “https://developpaper.com/laowang-takes-you-step-by-step-to-see-what-has-been-done-in-the-routing-matching-process-of-source-codelaravel/”