Split the route pattern in a path + inclusive and exclusive methods

@param string $pattern
@return array  [path, inc, excl]
500021
protected function splitRoutePattern($pattern)
    {
        if (strpos($pattern, ' ') !== false && preg_match_all('/\s+\+(\w+)\b|\s+\-(\w+)\b/', $pattern, $matches)) {
            list($path) = preg_split('/\s+/', $pattern, 2);
            $inc = isset($matches[1]) ? array_filter($matches[1]) : [];
            $excl = isset($matches[2]) ? array_filter($matches[2]) : [];
        } else {
            $path = $pattern;
            $inc = [];
            $excl = [];
        }
        
        return [$path, $inc, $excl];
    }
54984
protected function filterPath($path, $pattern, array $extensions=[])
    {
        $usePattern = $this->isFilterPattern($pattern);

        if (!$usePattern && !$extensions) {
            return true;
        }

        if ($usePattern && !fnmatch($pattern, $this->basename($path))) {
            return false;
        }

        if ($extensions && !in_array(strtolower($this->extension($path)), $extensions)) {
            return false;
        }

        return true;
    }
624482
private function matchPattern($pattern) {
        $regex = $this->patternToRegex($pattern);
        $result = preg_match($regex, $this->pathInfo);
        if ($result === false) {
            throw new Exception("拦截器匹配发生错误，错误的pathinfo模式：{$pattern}");
        }

        return $result == 1;
    }
8932
public function map(array $methods, string $pattern, $callable) : RouteInterface
    {
        $route = $this->getRouter()->map($methods, $pattern, $callable);

        return $route;
    }
1239473
public function filterMatch($patterns)
    {
        $patterns = is_string($patterns) ? [$patterns] : $patterns;

        // String pattern is assumed to be path.
        foreach ($patterns as $key => $pattern) {
            if (is_string($pattern)) {
                $patterns[$key] = ['path' => $pattern];
            }
        }

        return $this->filter(function ($route) use ($patterns) {
            // If any of patterns matches - route passes.
            foreach ($patterns as $pattern) {
                if ($this->isRouteMatchesPattern($route, $pattern)) {
                    return true;
                }
            }

            // If all patterns don't match - route is filtered out.
            return false;
        });
    }
1071427
private function parseRule($pattern)
    {
        $matcher = $pattern;
        $matcher = trim($matcher, '/');
        if (!$matcher) {
            return '|^/$|';
        }
        if ($matcher === '*') {
            return '|^.*$|i';
        } else {
            //替换 *为匹配除了 /分组之外的所有内容
            $matcher = str_replace('*', '[^\/]*', $matcher);
        }

        $slices = $this->route->getSlice($matcher);
        foreach ($slices as list($slice, $reg)) {
            $matcher = str_replace(':' . $slice, $reg, $matcher);
        }
        $matcher = '|^' . $matcher . '$|i';
        return $matcher;
    }
62103
protected function parsePattern($pattern)
    {
        $patterns = explode(';', $pattern);
        if (!isset($patterns[1])) {
            // No explicit negative pattern was provided, construct it.
            $patterns[1] = '-' . $patterns[0];
        }
        return $patterns;
    }
61213
public static function split(string $subject, string $pattern, int $flags = 0): array
    {
        return static::pcre('\preg_split', [$pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE]);
    }
603177
protected function splitPath($path)
    {
        $chunks = array_map(
            [$this, 'unescape'],
            preg_split('#(?<!\\\)\/#', ltrim($path, '/'))
        );

        if (empty($chunks) || (array_filter($chunks) !== $chunks)) {
            throw new \InvalidArgumentException('Invalid path provided:' . $path);
        }

        return $chunks;
    }
869152
public function splitWithContentIdKey($pattern, $string)
    {
        $array    = [];
        $exploded = explode($pattern, $string);
        foreach ($exploded as $key => $value) {
            $response  = new HttpResponse($value);
            $contentId = $response->getHeader('Content-Id');

            if (null !== $contentId) {
                $array[$contentId] = $value;
            } else {
                $array[$key] = $value;
            }
        }

        return $array;
    }
615614
protected function getFilesByPattern($pattern)
    {
        $iterator = Injector::inst()->create('GreenFinder')
            ->in($this->getPath())
            ->name($pattern);
        $files = [];

        foreach ($iterator as $file) {
            $files[] = $this->getRelativePath() . '/' . $file->getRelativePathname();
        }

        return $files;
    }
1345872
private function getOptionByRegExpr(string $pattern): array
    {
        $purgedArgs = $this->getRawArgs(true);
        return preg_grep("/" . $pattern . "/", $purgedArgs);
    }
809979
protected function getNestedPrefix($pattern)
    {
        $segments = explode("/", $pattern);
        $pattern = "";

        foreach ($segments as $index => $segment) {
            if (strpos($segment, "{") === 0) {
                   $pattern .= "/{" . $segments[$index - 1] . "_" . ltrim($segment, "{");
            } else $pattern .= $segment;
        }

        return $pattern;
    }
223467
private static function _normalize($pattern)
    {
        return self::$_delimiter . trim($pattern, self::$_delimiter)
            . self::$_delimiter;
    }
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)));
    }
1255882
protected function parsePattern($pattern)
    {
        $currentPos      = 0;
        $length          = strlen($pattern);
        $tokens          = [];
        $level           = 0;
        $quotedDelimiter = preg_quote($this->delimiter);

        while ($currentPos < $length) {
            preg_match('(\G(?P<literal>[^:{\[\]]*)(?P<token>[:\[\]]|$))', $pattern, $matches, 0, $currentPos);

            $currentPos += strlen($matches[0]);

            if (!empty($matches['literal'])) {
                $tokens[] = ['literal', $matches['literal']];
            }

            if ($matches['token'] === ':') {
                if (!preg_match(
                    '(\G(?P<name>[^:' . $quotedDelimiter . '{\[\]]+)(?:{(?P<delimiters>[^}]+)})?:?)',
                    $pattern,
                    $matches,
                    0,
                    $currentPos
                )) {
                    throw new Exception\RuntimeException('Found empty parameter name');
                }

                $tokens[] = [
                    'parameter',
                    $matches['name'],
                    isset($matches['delimiters']) ? $matches['delimiters'] : null
                ];

                $currentPos += strlen($matches[0]);
            } elseif ($matches['token'] === '[') {
                $tokens[] = ['optional-start'];
                $level++;
            } elseif ($matches['token'] === ']') {
                $tokens[] = ['optional-end'];
                $level--;

                if ($level < 0) {
                    throw new Exception\RuntimeException('Found closing bracket without matching opening bracket');
                }
            } else {
                break;
            }
        }

        if ($level > 0) {
            throw new Exception\RuntimeException('Found unbalanced brackets');
        }

        return $tokens;
    }
1239749
private function setBaseRoute($pattern)
    {
        $pattern = $this->routeBasePath . '/' . trim($pattern, '/');

        return $this->routeBasePath ? rtrim($pattern, '/') : $pattern;
    }