framework/web/UrlRule.php
private function translatePattern($allowAppendSlash)
    {
        $tr = [
            '.' => '\\.',
            '*' => '\\*',
            '$' => '\\$',
            '[' => '\\[',
            ']' => '\\]',
            '(' => '\\(',
            ')' => '\\)',
        ];

        $tr2 = [];
        $requiredPatternPart = $this->pattern;
        $oldOffset = 0;
        if (preg_match_all('/<([\w._-]+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
            $appendSlash = false;
            foreach ($matches as $match) {
                $name = $match[1][0];
                $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
                $placeholder = 'a' . hash('crc32b', $name); // placeholder must begin with a letter
                $this->placeholders[$placeholder] = $name;
                if (array_key_exists($name, $this->defaults)) {
                    $length = strlen($match[0][0]);
                    $offset = $match[0][1];
                    $requiredPatternPart = str_replace("/{$match[0][0]}/", '//', $requiredPatternPart);
                    if (
                        $allowAppendSlash
                        && ($appendSlash || $offset === 1)
                        && (($offset - $oldOffset) === 1)
                        && isset($this->pattern[$offset + $length])
                        && $this->pattern[$offset + $length] === '/'
                        && isset($this->pattern[$offset + $length + 1])
                    ) {
                        // if pattern starts from optional params, put slash at the end of param pattern
                        // @see https://github.com/yiisoft/yii2/issues/13086
                        $appendSlash = true;
                        $tr["<$name>/"] = "((?P<$placeholder>$pattern)/)?";
                    } elseif (
                        $offset > 1
                        && $this->pattern[$offset - 1] === '/'
                        && (!isset($this->pattern[$offset + $length]) || $this->pattern[$offset + $length] === '/')
                    ) {
                        $appendSlash = false;
                        $tr["/<$name>"] = "(/(?P<$placeholder>$pattern))?";
                    }
                    $tr["<$name>"] = "(?P<$placeholder>$pattern)?";
                    $oldOffset = $offset + $length;
                } else {
                    $appendSlash = false;
                    $tr["<$name>"] = "(?P<$placeholder>$pattern)";
                }

                if (isset($this->_routeParams[$name])) {
                    $tr2["<$name>"] = "(?P<$placeholder>$pattern)";
                } else {
                    $this->_paramRules[$name] = $pattern === '[^\/]+' ? '' : "#^$pattern$#u";
                }
            }
        }

        // we have only optional params in route - ensure slash position on param patterns
        if ($allowAppendSlash && trim($requiredPatternPart, '/') === '') {
            $this->translatePattern(false);
            return;
        }

        $this->_template = preg_replace('/<([\w._-]+):?([^>]+)?>/', '<$1>', $this->pattern);
        $this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';

        // if host starts with relative scheme, then insert pattern to match any
        if (strncmp($this->host, '//', 2) === 0) {
            $this->pattern = substr_replace($this->pattern, '[\w]+://', 2, 0);
        }

        if (!empty($this->_routeParams)) {
            $this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u';
        }
    }
private function translatePattern($allowAppendSlash)
    {
        $tr = [
            '.' => '\\.',
            '*' => '\\*',
            '$' => '\\$',
            '[' => '\\[',
            ']' => '\\]',
            '(' => '\\(',
            ')' => '\\)',
        ];

        $tr2 = [];
        $requiredPatternPart = $this->pattern;
        $oldOffset = 0;
        if (preg_match_all('/<([\w._-]+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
            $appendSlash = false;
            foreach ($matches as $match) {
                $name = $match[1][0];
                $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
                $placeholder = 'a' . hash('crc32b', $name); // placeholder must begin with a letter
                $this->placeholders[$placeholder] = $name;
                if (array_key_exists($name, $this->defaults)) {
                    $length = strlen($match[0][0]);
                    $offset = $match[0][1];
                    $requiredPatternPart = str_replace("/{$match[0][0]}/", '//', $requiredPatternPart);
                    if (
                        $allowAppendSlash
                        && ($appendSlash || $offset === 1)
                        && (($offset - $oldOffset) === 1)
                        && isset($this->pattern[$offset + $length])
                        && $this->pattern[$offset + $length] === '/'
                        && isset($this->pattern[$offset + $length + 1])
                    ) {
                        // if pattern starts from optional params, put slash at the end of param pattern
                        // @see https://github.com/yiisoft/yii2/issues/13086
                        $appendSlash = true;
                        $tr["<$name>/"] = "((?P<$placeholder>$pattern)/)?";
                    } elseif (
                        $offset > 1
                        && $this->pattern[$offset - 1] === '/'
                        && (!isset($this->pattern[$offset + $length]) || $this->pattern[$offset + $length] === '/')
                    ) {
                        $appendSlash = false;
                        $tr["/<$name>"] = "(/(?P<$placeholder>$pattern))?";
                    }
                    $tr["<$name>"] = "(?P<$placeholder>$pattern)?";
                    $oldOffset = $offset + $length;
                } else {
                    $appendSlash = false;
                    $tr["<$name>"] = "(?P<$placeholder>$pattern)";
                }

                if (isset($this->_routeParams[$name])) {
                    $tr2["<$name>"] = "(?P<$placeholder>$pattern)";
                } else {
                    $this->_paramRules[$name] = $pattern === '[^\/]+' ? '' : "#^$pattern$#u";
                }
            }
        }

        // we have only optional params in route - ensure slash position on param patterns
        if ($allowAppendSlash && trim($requiredPatternPart, '/') === '') {
            $this->translatePattern(false);
            return;
        }

        $this->_template = preg_replace('/<([\w._-]+):?([^>]+)?>/', '<$1>', $this->pattern);
        $this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';

        // if host starts with relative scheme, then insert pattern to match any
        if (strncmp($this->host, '//', 2) === 0) {
            $this->pattern = substr_replace($this->pattern, '[\w]+://', 2, 0);
        }

        if (!empty($this->_routeParams)) {
            $this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u';
        }
    }
private function translatePattern ( $ allowAppendSlash ) { $ tr = [ '.' => '\\.' , '*' => '\\*' , '$' => '\\$' , '[' => '\\[' , ']' => '\\]' , '(' => '\\(' , ')' => '\\)' , ] ; $ tr2 = [ ] ; $ requiredPatternPart = $ this -> pattern ; $ oldOffset = 0 ; if ( preg_match_all ( '/<([\w._-]+):?([^>]+)?>/' , $ this -> pattern , $ matches , PREG_OFFSET_CAPTURE | PREG_SET_ORDER ) ) { $ appendSlash = false ; foreach ( $ matches as $ match ) { $ name = $ match [ 1 ] [ 0 ] ; $ pattern = isset ( $ match [ 2 ] [ 0 ] ) ? $ match [ 2 ] [ 0 ] : '[^\/]+' ; $ placeholder = 'a' . hash ( 'crc32b' , $ name ) ; // placeholder must begin with a letter $ this -> placeholders [ $ placeholder ] = $ name ; if ( array_key_exists ( $ name , $ this -> defaults ) ) { $ length = strlen ( $ match [ 0 ] [ 0 ] ) ; $ offset = $ match [ 0 ] [ 1 ] ; $ requiredPatternPart = str_replace ( "/{$match[0][0]}/" , '//' , $ requiredPatternPart ) ; if ( $ allowAppendSlash && ( $ appendSlash || $ offset === 1 ) && ( ( $ offset - $ oldOffset ) === 1 ) && isset ( $ this -> pattern [ $ offset + $ length ] ) && $ this -> pattern [ $ offset + $ length ] === '/' && isset ( $ this -> pattern [ $ offset + $ length + 1 ] ) ) { // if pattern starts from optional params, put slash at the end of param pattern // @see https://github.com/yiisoft/yii2/issues/13086 $ appendSlash = true ; $ tr [ "<$name>/" ] = "((?P<$placeholder>$pattern)/)?" ; } elseif ( $ offset > 1 && $ this -> pattern [ $ offset - 1 ] === '/' && ( ! isset ( $ this -> pattern [ $ offset + $ length ] ) || $ this -> pattern [ $ offset + $ length ] === '/' ) ) { $ appendSlash = false ; $ tr [ "/<$name>" ] = "(/(?P<$placeholder>$pattern))?" ; } $ tr [ "<$name>" ] = "(?P<$placeholder>$pattern)?" ; $ oldOffset = $ offset + $ length ; } else { $ appendSlash = false ; $ tr [ "<$name>" ] = "(?P<$placeholder>$pattern)" ; } if ( isset ( $ this -> _routeParams [ $ name ] ) ) { $ tr2 [ "<$name>" ] = "(?P<$placeholder>$pattern)" ; } else { $ this -> _paramRules [ $ name ] = $ pattern === '[^\/]+' ? '' : "#^$pattern$#u" ; } } } // we have only optional params in route - ensure slash position on param patterns if ( $ allowAppendSlash && trim ( $ requiredPatternPart , '/' ) === '' ) { $ this -> translatePattern ( false ) ; return ; } $ this -> _template = preg_replace ( '/<([\w._-]+):?([^>]+)?>/' , '<$1>' , $ this -> pattern ) ; $ this -> pattern = '#^' . trim ( strtr ( $ this -> _template , $ tr ) , '/' ) . '$#u' ; // if host starts with relative scheme, then insert pattern to match any if ( strncmp ( $ this -> host , '//' , 2 ) === 0 ) { $ this -> pattern = substr_replace ( $ this -> pattern , '[\w]+://' , 2 , 0 ) ; } if ( ! empty ( $ this -> _routeParams ) ) { $ this -> _routeRule = '#^' . strtr ( $ this -> route , $ tr2 ) . '$#u' ; } }
Prepares [[$pattern]] on rule initialization - replace parameter names by placeholders.

@param bool $allowAppendSlash Defines position of slash in the param pattern in [[$pattern]].
If `false` slash will be placed at the beginning of param pattern. If `true` slash position will be detected
depending on non-optional pattern part.
Prepares [[ $pattern ]] on rule initialization - replace parameter names by placeholders .