Рубрики
Uncategorized

Лао ван шаг за шагом расскажет вам, что было сделано в процессе сопоставления маршрутизации исходного кода|laravel?

Автор оригинала: 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/”