Parse variable pattern

@param string $patern

@return array

@throws RoutingRuleParseException
518284
private static function parseVariablePattern(string $patern) : array
    {
        static $supported_types = [
            'string',
            'integer',
            'int',
            'float',
            'double',
            'boolean',
            'bool',
        ];
    
        $tmp = explode(':',$patern);
        $varname = $tmp[1] ?? null;
        $regex   = $tmp[2] ?? null;
        $type    = $tmp[3] ?? null;
        
        if (!preg_match('/^[a-zA-Z_][0-9a-zA-Z_]*(\[\])?$/',$varname)){
            throw new RoutingRuleParseException($patern, "illegal variable name: $varname");
        }
        if ($regex && !preg_match('/^[0-9a-zA-Z@#_=%\\\^\$\.\[\]\|\(\)\?\*\+\{\}\^\-]*$/',$regex)){
            throw new RoutingRuleParseException($patern, "illegal variable regex pattern: $regex");
        }
        if ($type && !in_array($type,$supported_types)){
            throw new RoutingRuleParseException($patern, "unsupported variable type: $type");
        }
        
        $res = [];
        
        if ($varname){
            $res['varname'] = $varname;
        }
        if ($regex){
            $res['regex'] = $regex;
        }
        if ($type){
            $res['type'] = $type;
        }
    
        return $res;
    }
413269
protected static function parsePattern($pattern, $remove_conditional = false, $only_pattern = false)
    {
        $url = $pattern;
        if (!preg_match_all('/\{(\w+\??)\}/is', $pattern, $matches)) {
            return $url;
        }
        
        $replaces = $matches[1];
        foreach ($replaces as $pattern) {
            $conditional = (substr($pattern, -1, 1) === '?') ? true : false;
            $pattern = rtrim($pattern, '?');

            if (array_key_exists($pattern, self::$patterns)) {

                $replace = self::$patterns[$pattern];
                if ($conditional) {
                    $replace = ($remove_conditional) ? '' : $replace.'?';
                    $url = preg_replace('#\{'.$pattern.'\?\}#', $replace, $url);
                    
                } else {
                    $url = preg_replace('#\{'.$pattern.'\}#', $replace, $url);
                }
                $pattern = $url;
                if ($only_pattern) return $pattern;
            }

            $url = self::replacePattern($pattern, self::$routes, $url, $conditional, $remove_conditional);
            $url = self::replacePattern($pattern, self::$qs, $url, $conditional, $remove_conditional);
            

        }
        return $url;
    }
283654
private function parseInterceptMap($request)
    {
        // Set up authN/authZ if an URL mapping was given.
        if (isset($this->config['interceptUrlMap']) && is_array($this->config['interceptUrlMap'])) {
            // We may need to modify the intercept map, so make a copy of it
            $interceptMap = $this->config['interceptUrlMap'];

            // Always allow HEAD requests unless the user specifically blocked them
            if (!array_key_exists('HEAD', $interceptMap)) {
                $interceptMap['HEAD'] = ['/**' => ['authN' => 'permitAll', 'authZ' => 'permitAll']];
            }

            // If the request method is not in the intercept map, throw an exception
            if (!array_key_exists($request->getMethod(), $interceptMap)) {
                throw new \Exception(
                    "Request Type: " . $request->getMethod() . " does not exist in Auth Intercept Map"
                );
            }

            $antPathMatcher = new AntPathMatcher();
            foreach ($interceptMap[$request->getMethod()] as $pattern => $auth_array) {
                // Support RegEx or Ant patterns
                if ($pattern[0] == '^') {
                    $match = preg_match('/'.$pattern.'/', $request->getUri()->getPath());
                } else {
                    $match = $antPathMatcher->match($pattern, $request->getUri()->getPath());
                }
                // Break out of the foreach loop if this URL matches the pattern.
                if ($match) {
                    $this->authN_type = $auth_array['authN'];
                    $this->authZ_roles = $auth_array['authZ'];
                    break;
                }
            }
        }
    }
1240935
public function parseRoute($routeSpec)
    {
        $path           = isset($routeSpec['path']) ? $routeSpec['path'] : null;
        $methods        = isset($routeSpec['methods']) ? $routeSpec['methods'] : array();
        $controller     = isset($routeSpec['controller']) ? $routeSpec['controller'] : null;
        $calls          = isset($routeSpec['calls']) ? $routeSpec['calls'] : null;
        $requirements   = isset($routeSpec['requirements']) ? $routeSpec['requirements'] : array();
        $regex          = isset($routeSpec['path']) ? $routeSpec['path'] : null;

        $route = new Route($path, $methods, $controller, $calls, $requirements, $regex);

        return $route;
    }
1241242
private function parsePath(string $path): array
    {
        $pathParts = [];
        foreach (explode('.', $path) as $pathPart) {
            $matches = [];
            if (1 === preg_match('/^(([^\[]+)\[(\d+|_all)\])$/', $pathPart, $matches)) {
                $pathParts[] = $matches[2];
                $pathParts[] = '_all' !== $matches[3] ? (int) $matches[3] : $matches[3];
            } else {
                $pathParts[] = $pathPart;
            }
        }

        return $pathParts;
    }
901083
public function isPatternMatches($pattern, $routePattern = false)
    {
        $uri = $this->removeIndexDotPhpAndTrillingSlash($this->getCurrentUri());
        $hasPattern = $this->hasNamedPattern($pattern);
        $pattern = ($hasPattern == false) ? $pattern : $hasPattern;
        if (preg_match_all(
            '#^' . $pattern . '$#',
            $uri,
            $matches,
            PREG_SET_ORDER
        )) {
            return $matches;
        }
    }
884834
public function parse() {
        $result = [];
        foreach ($this->routingTable as $key => $route) {
            $this->params = [];
            $route['regx'] = preg_replace_callback(
                "/%([^%]+)%/",
                [$this, 'match'],
                preg_quote(str_replace(['[', ']', ':'], ['%', '%', ' '], $route['url']))//$route['url']
            );

            /** 处理斜线 */
            $route['regx'] = rtrim($route['regx'], '/');
            $route['regx'] = '|^' . $route['regx'] . '[/]?$|';

            $route['format'] = preg_replace("/\[([^\]]+)\]/", "%s", $route['url']);//$route['url']
            $route['params'] = $this->params;

            $result[$key] = $route;
        }

        return $result;
    }
726347
private function patternToPath($pattern, $params)
    {
        // Inspired by https://github.com/nikic/FastRoute/issues/66
        // but uses named params, don't care param order and size
        $routeParser = new RouteParserStd;
        $routes = $routeParser->parse($pattern);

        // check from the longest route to the shortest route
        // first (longest) full match is the route we need
        for ($i = count($routes) - 1; $i >= 0; $i--) {
            $url = '';
            foreach ($routes[$i] as $part) {
                // replace placeholder to actual value
                if (is_array($part)) {
                    $part = @$params[$part[0]];
                }

                // if route contains part not defined in $params, abandan this route
                if (null === $part) {
                    continue 2;
                }
                $url .= $part;
            }

            // first full match, this is our hero url
            return $url;
        }

        // no full match, throw Exception
        throw new \Exception(sprintf("Can't make path for '%s' with param: %s.", $pattern, json_encode($params)));
    }
393805
protected function parsePaths()
    {
        if (is_null($this->paths) === true) {
            // Server params
            $scriptName = $this->env->get('SCRIPT_NAME'); // <-- "/foo/index.php"
            $requestUri = $this->env->get('REQUEST_URI'); // <-- "/foo/bar?test=abc" or "/foo/index.php/bar?test=abc"
            $queryString = $this->getQueryString(); // <-- "test=abc" or ""

            // Physical path
            if (strpos($requestUri, $scriptName) !== false) {
                $physicalPath = $scriptName; // <-- Without rewriting
            } else {
                $physicalPath = str_replace('\\', '', dirname($scriptName)); // <-- With rewriting
            }
            $scriptName = rtrim($physicalPath, '/'); // <-- Remove trailing slashes

            // Virtual path
            $pathInfo = substr_replace($requestUri, '', 0, strlen($physicalPath)); // <-- Remove physical path
            $pathInfo = str_replace('?' . $queryString, '', $pathInfo); // <-- Remove query string
            $pathInfo = '/' . ltrim($pathInfo, '/'); // <-- Ensure leading slash

            $this->paths = array();
            $this->paths['physical'] = $scriptName;
            $this->paths['virtual'] = $pathInfo;
        }

        return $this->paths;
    }
556985
function route($pattern,$handler,$ttl=0,$kbps=0) {
		$types=['sync','ajax','cli'];
		$alias=null;
		if (is_array($pattern)) {
			foreach ($pattern as $item)
				$this->route($item,$handler,$ttl,$kbps);
			return;
		}
		preg_match('/([\|\w]+)\h+(?:(?:@?(.+?)\h*:\h*)?(@(\w+)|[^\h]+))'.
			'(?:\h+\[('.implode('|',$types).')\])?/u',$pattern,$parts);
		if (isset($parts[2]) && $parts[2])
			$this->hive['ALIASES'][$alias=$parts[2]]=$parts[3];
		elseif (!empty($parts[4])) {
			if (empty($this->hive['ALIASES'][$parts[4]]))
				user_error(sprintf(self::E_Named,$parts[4]),E_USER_ERROR);
			$parts[3]=$this->hive['ALIASES'][$alias=$parts[4]];
		}
		if (empty($parts[3]))
			user_error(sprintf(self::E_Pattern,$pattern),E_USER_ERROR);
		$type=empty($parts[5])?0:constant('self::REQ_'.strtoupper($parts[5]));
		foreach ($this->split($parts[1]) as $verb) {
			if (!preg_match('/'.self::VERBS.'/',$verb))
				$this->error(501,$verb.' '.$this->hive['URI']);
			$this->hive['ROUTES'][$parts[3]][$type][strtoupper($verb)]=
				[$handler,$ttl,$kbps,$alias];
		}
	}
1198545
function _parseRoutes(){
		$routes = $this->getConfig('map');
		
		$uri = implode('/',$this->segments);
		// Is have route to vendor?
		if(isset($routes['root:vendor'])){
			$this->app_path = VENDOR_PATH.$routes['root:vendor'];
		}
		elseif(isset($routes[current($this->segments).':vendor'])){
			$this->app_path = VENDOR_PATH.$routes[current($this->segments).':vendor'];
			array_shift($this->segments);
		}
		
		// Is there a literal match?  If so we're done
		elseif (isset($routes[$uri])){
			explode('/', $routes[$uri]);
			return $this->_setRequest(explode('/', $routes[$uri]));
		}
		
		// Loop through the route array looking for wild-cards
		foreach ($routes as $key => $val){
			// Convert wild-cards to RegEx
			$key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));

			// Does the RegEx match?
			if (preg_match('#^'.$key.'$#', $uri))
			{
				// Do we have a back-reference?
				if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE)
				{
					$val = preg_replace('#^'.$key.'$#', $val, $uri);
				}

				return $this->_setRequest(explode('/', $val));
			}
		}

		
		// If we got this far it means we didn't encounter a
		// matching route so we'll set the site default route
		$this->_setRequest($this->segments);
		
	}
131634
protected function buildRegexp($pattern, array $matches)
    {
        $src = [];
        $trg = [];
        foreach ($matches as $match) {
            list($key, $regexp) = $this->splitSegment($match[3]);

            if (in_array(substr($regexp, -1), ['+', '*', '?'])) {
                throw new RouteException('Route must not end with quantification token');
            }

            $src[rtrim($match[0], '/')] = $key;

            $trg[$key] = '(?P<' . $key . '>' . $regexp . '+)';

            if ($match[1] == '(') {
                $trg[$key] = '(' . $trg[$key] . $this->buildConditionalSlash(preg_quote($match[4], '/')) . ')?';
            }
        }

        $pattern = strtr($pattern, $src);
        $pattern = preg_quote($pattern, '/');
        $pattern = strtr($pattern, $trg);
        $pattern = str_replace('\/((?P', '\/?((?P', $pattern);
        $pattern = $this->buildConditionalSlash($pattern);

        return '/^' . $pattern . '$/i';
    }
143197
private function _parse_routes()
	{
		$uri=implode('/', $this->uri->segments());

		if (isset($this->router[$uri])) {
			return $this->_set_request(explode('/', $this->router[$uri]));
		}

		foreach ($this->router as $key => $val) {
			$key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key));

			if (preg_match('#^'.$key.'$#', $uri)) {
				if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) {
					$val = preg_replace('#^'.$key.'$#', $val, $uri);
				}

				return $this->_set_request(explode('/', $val));
			}
		}

		$this->_set_request($this->uri->segments());
	}
400428
private static function parseHostVars(RouteInterface $route)
    {
        if (null === $host = $route->getHost()) {
            return ['expression' => null, 'tokens' => []];
        }

        return self::transpilePattern($host, true, $route->getConstraints(), $route->getDefaults());
    }
203367
static public function getRoutePatternParts( Route $route ){
		$parts	= array();
		foreach( explode( "/", $route->getPattern() ) as $part ){
			$optional	= FALSE;
			$argument	= FALSE;
			if( preg_match( "@\(:.+\)@", $part ) ){													//  optional argument found
				$part		= substr( $part, 1, -1 );												//  cut argument key
				$optional	= TRUE;																	//  note that is optional
			}
			if( substr( $part, 0, 1 ) === ":" ){													//  argument found
				$part		= substr( $part, 1 );													//  cut argument key
				$argument	= TRUE;																	//  note that is argument
			}
			$parts[]	= (object) array(															//  prepare route pattern part object
				'key'		=> $part,
				'optional'	=> $optional,
				'argument'	=> $argument,
			);
		}
		return $parts;
	}
31564
public function parse($request) {
        if (!preg_match('#'.self::PATTERN.'#', $request, $matches)) return false;
        return [
            $this->relativePath($matches[1].'.'.$matches[5]), // Path
            $matches[2] == '_' ? null : (int) $matches[2],    // Width
            $matches[3] == '_' ? null : (int) $matches[3],    // Height
            $this->options($matches[4]),                      // Options
        ];
    }
822371
protected function parsePattern($pattern)
    {
        $parts = [
            'min' => '[0-5]?\d',
            'hour' => '[01]?\d|2[0-3]',
            'day' => '0?[1-9]|[12]\d|3[01]',
            'month' => '[1-9]|1[012]',
            'dow' => '[0-7]',
            'year' => '20([0-9]{2})',
        ];

        $regex = [];
        foreach (array_slice($parts, 0, 5) as $name => $number) {
            $range = '('.$number.')(-('.$number.'))?';
            $regex[$name] = '(?P<'.$name.'>(\*(\/\d+)?|'.$range.'(,'.$range.')*))';
        }
        $range = '('.$parts['year'].')(-('.$parts['year'].'))?';
        $regexYear = '( (?P<year>(\*(\/\d+)?|'.$range.'(,'.$range.')*)))?';

        $regex = '/^'.implode('([\s\t]+)', $regex).$regexYear.'$/';
        preg_match($regex, $pattern, $matches);

        return array_intersect_key($matches, $parts);
    }