生成路由的正则规则
@access protected
@param  string $rule 路由规则
@param  array $match 匹配的变量
@param  array $pattern 路由变量规则
@param  array $option 路由参数
@param  bool $completeMatch 路由是否完全匹配
@param  string $suffix 路由正则变量后缀
@return string
178274
protected function buildRuleRegex($rule, $match, $pattern = [], $option = [], $completeMatch = false, $suffix = '')
    {
        foreach ($match as $name) {
            $replace[] = $this->buildNameRegex($name, $pattern, $suffix);
        }

        // 是否区分 / 地址访问
        if ('/' != $rule) {
            if (!empty($option['remove_slash'])) {
                $rule = rtrim($rule, '/');
            } elseif (substr($rule, -1) == '/') {
                $rule = rtrim($rule, '/');
                $hasSlash = true;
            }
        }

        $regex = str_replace($match, $replace, $rule);
        $regex = str_replace([')?/', ')/', ')?-', ')-', '\\\\/'], [')\/', ')\/', ')\-', ')\-', '\/'], $regex);

        if (isset($hasSlash)) {
            $regex .= '\/';
        }

        return $regex . ($completeMatch ? '$' : '');
    }
1204476
public function generate($pattern, array $options = array())
    {
        $options += array(
            'translit' => true,
            'language' => null,
            'placeholders' => array()
        );

        $result = null;
        $this->hook->attach('alias.generate.before', $pattern, $options, $result);

        if (isset($result)) {
            return (string) $result;
        }

        $alias = $pattern;
        if (!empty($options['placeholders'])) {
            $alias = gplcart_string_replace($pattern, $options['placeholders'], $options);
        }

        if (!empty($options['translit'])) {
            $alias = gplcart_string_slug($this->language->translit($alias, $options['language']));
        }

        $trimmed = mb_strimwidth(str_replace(' ', '-', trim($alias)), 0, 100, '');
        $result = $this->getUnique($trimmed);
        $this->hook->attach('alias.generate.after', $pattern, $options, $result);
        return $result;
    }
333121
public function setRegexp($pattern)
    {
        $regexp = new RegularExpression();
        $regexp->setPattern($pattern);
        $this->_regexps[] = $regexp;
    }
90575
private function match($url, $option, $completeMatch)
    {
        if (isset($option['complete_match'])) {
            $completeMatch = $option['complete_match'];
        }

        $pattern = array_merge($this->parent->getPattern(), $this->pattern);
        $depr = $this->router->config('app.pathinfo_depr');

        // 检查完整规则定义
        if (isset($pattern['__url__']) && !preg_match(0 === strpos($pattern['__url__'], '/') ? $pattern['__url__'] : '/^' . $pattern['__url__'] . '/', str_replace('|', $depr, $url))) {
            return false;
        }

        $var = [];
        $url = $depr . str_replace('|', $depr, $url);
        $rule = $depr . str_replace('/', $depr, $this->rule);

        if ($depr == $rule && $depr != $url) {
            return false;
        }

        if (false === strpos($rule, '<')) {
            if (0 === strcasecmp($rule, $url) || (!$completeMatch && 0 === strncasecmp($rule . $depr, $url . $depr, strlen($rule . $depr)))) {
                return $var;
            }
            return false;
        }

        $slash = preg_quote('/-' . $depr, '/');

        if ($matchRule = preg_split('/[' . $slash . ']?<\w+\??>/', $rule, 2)) {
            if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
                return false;
            }
        }

        if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
            $regex = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $completeMatch);

            try {
                if (!preg_match('/^' . $regex . ($completeMatch ? '$' : '') . '/u', $url, $match)) {
                    return false;
                }
            } catch (\Exception $e) {
                throw new Exception('route pattern error');
            }

            foreach ($match as $key => $val) {
                if (is_string($key)) {
                    $var[$key] = $val;
                }
            }
        }

        // 成功匹配后返回URL中的动态变量数组
        return $var;
    }
1147824
protected function doMatch($pattern, $path, $fullMatch)
    {

        if (($pattern[0] == '/') != ($path[0] == '/')) {
            return false;
        }
        if ('/' == substr($pattern, '-1')) {
            $pattern .= '**';
        }

        $pattDirs = $this->tokenizeToStringArray($pattern);
        $pathDirs = $this->tokenizeToStringArray($path);

        $pattIdxStart = 0;
        $pattIdxEnd = count($pattDirs) - 1;
        $pathIdxStart = 0;
        $pathIdxEnd = count($pathDirs) - 1;

        // Match all elements up to the first **
        while ($pattIdxStart <= $pattIdxEnd and $pathIdxStart <= $pathIdxEnd) {
            $patDir = $pattDirs[$pattIdxStart];
            if ('**' == $patDir) {
                break;
            }
            if (!$this->matchStrings($patDir, $pathDirs[$pathIdxStart])) {
                return false;
            }
            $pattIdxStart++;
            $pathIdxStart++;
        }

        if ($pathIdxStart > $pathIdxEnd) {
            // Path is exhausted, only match if rest of pattern is * or **'s
            if ($pattIdxStart > $pattIdxEnd) {
                return ('/' == substr($pattern, '-1') ? '/' == substr($path, '-1') :
                        !('/' == substr($path, '-1')));
            }
            if (!$fullMatch) {
                return true;
            }
            if ($pattIdxStart == $pattIdxEnd and '*' == $pattDirs[$pattIdxStart] and '/' == substr($path, -1)) {
                return true;
            }
            for ($i = $pattIdxStart; $i <= $pattIdxEnd; $i++) {
                if (!'**' == $pattDirs[$i]) {
                    return false;
                }
            }

            return false;
        } elseif ($pattIdxStart > $pattIdxEnd) {
            // String not exhausted, but pattern is. Failure.
            return false;
        } elseif (!$fullMatch and "**" == $pattDirs[$pattIdxStart]) {
            // Path start definitely matches due to "**" part in pattern.
            return true;
        }

        // up to last '**'
        while ($pattIdxStart <= $pattIdxEnd and $pathIdxStart <= $pathIdxEnd) {
            $patDir = $pattDirs[$pattIdxEnd];
            if ('**' == $patDir) {
                break;
            }
            if (!$this->matchStrings($patDir, $pathDirs[$pathIdxEnd])) {
                return false;
            }
            $pattIdxEnd--;
            $pathIdxEnd--;
        }
        if ($pathIdxStart > $pathIdxEnd) {
            // String is exhausted
            for ($i = $pattIdxStart; $i <= $pattIdxEnd; $i++) {
                if (!('**' == $pattDirs[$i])) {
                    return false;
                }
            }

            return true;
        }

        while ($pattIdxStart != $pattIdxEnd and $pathIdxStart <= $pathIdxEnd) {
            $patIdxTmp = -1;
            for ($i = $pattIdxStart + 1; $i <= $pattIdxEnd; $i++) {
                if ('**' == $pattDirs[$i]) {
                    $patIdxTmp = $i;
                    break;
                }
            }
            if ($patIdxTmp == $pattIdxStart + 1) {
                // '**/**' situation, so skip one
                $pattIdxStart++;
                continue;
            }

            // Find the pattern between padIdxStart & padIdxTmp in str between
            // strIdxStart & strIdxEnd
            $patLength = ($patIdxTmp - $pattIdxStart - 1);
            $strLength = ($pathIdxEnd - $pathIdxStart + 1);
            $foundIdx = -1;

            for ($i = 0; $i <= $strLength - $patLength; $i++) {
                for ($j = 0; $j < $patLength; $j++) {
                    $subPat = $pattDirs[$pattIdxStart + $j + 1];
                    $subStr = $pathDirs[$pathIdxStart + $i + $j];
                    if (!$this->matchStrings($subPat, $subStr)) {
                        continue 2;
                    }
                }
                $foundIdx = $pathIdxStart + $i;
                break;
            }

            if ($foundIdx == -1) {
                return false;
            }

            $pattIdxStart = $patIdxTmp;
            $pathIdxStart = $foundIdx + $patLength;

        }

        for ($i = $pattIdxStart; $i <= $pattIdxEnd; $i++) {
            if (!'**' == $pattDirs[$i]) {
                return false;
            }
        }

        return true;

    }
563863
private function toRegex(string $pattern): string
    {
        while (\preg_match(self::OPTIONAL_PLACEHOLDER_REGEX, $pattern)) {
            $pattern = \preg_replace(self::OPTIONAL_PLACEHOLDER_REGEX, '($1)?', $pattern);
        }

        $pattern = \preg_replace_callback(self::REQUIRED_PLACEHOLDER_REGEX, function (array $match = []) {
            $match = \array_pop($match);
            $pos = \mb_strpos($match, self::REGEX_PREFIX);
            if (false !== $pos) {
                $parameterName = \mb_substr($match, 0, $pos);
                $parameterRegex = \mb_substr($match, $pos + 2);

                return "(?<$parameterName>$parameterRegex)";
            }

            return "(?<$match>[^/]+)";
        }, $pattern);

        return $pattern;
    }
756808
public function map($method, $pattern, $call)
    {
        if ($this->vitex === null) {
            $this->vitex = Vitex::getInstance();
            $this->caseSensitive = $this->vitex->getConfig('router.case_sensitive');
        }
        $method = strtoupper($method);

        $matcher = $this->getPatternRegexp($pattern);
        /**
         * 请求方法
         * 路由匹配正则
         * 执行方法
         * 原始路由字符串
         * 路由应用
         * 路由别名
         * 方法
         */
        $this->_patterns[] = [$method, $matcher, $call, $pattern, $this->getRouteApp(), '', []];
        return $this;
    }
908120
function rule_match_post_format( $match, $rule, $options )
	{
		// vars
		$post_format = $options['post_format'];
		if( !$post_format )
		{	
			// validate
			if( !$options['post_id'] )
			{
				return false;
			}
			
			
			// post type
			if( !$options['post_type'] )
			{
				$options['post_type'] = get_post_type( $options['post_id'] );
			}
			
		
			// does post_type support 'post-format'
			if( post_type_supports( $options['post_type'], 'post-formats' ) )
			{
				$post_format = get_post_format( $options['post_id'] );
				
				if( $post_format === false )
				{
					$post_format = 'standard';
				}
			}
		}

       	
       	if($rule['operator'] == "==")
        {
        	$match = ( $post_format === $rule['value'] );
        	 
        }
        elseif($rule['operator'] == "!=")
        {
        	$match = ( $post_format !== $rule['value'] );
        }
        
        
        
        return $match;
        
    }
1194404
public function compile_binds($sql, $binds,$pattern)
    {
        return preg_replace_callback($pattern, function($m) use ($binds){
            if(isset($binds[$m[1]])){ // If it exists in our array
                return $binds[$m[1]]; // Then replace it from our array
            }

            return $m[0]; // Otherwise return the whole match (basically we won't change it)
        }, $sql);
    }
520047
static function compilePattern($pattern, array $options = array())
    {

        $len = strlen($pattern);
        /**
         * contains:
         *
         *   array( 'text', $text ),
         *   array( 'variable', $match[0][0][0], $regexp, $var);
         *
         */
        $tokens = array();
        $variables = array();
        $pos = 0;

        /**
         *  the path like:
         *
         *      /blog/to/:year/:month
         *
         *  will be separated like:
         *      
         *      [
         *          '/blog/to',  (text token)
         *          '/:year',    (reg exp token)
         *          '/:month',   (reg exp token)
         *      ]
         */
        $matches = self::splitTokens($pattern);


        // build tokens
        foreach ($matches as $matchIdx => $match) {
            // match[0][1] // matched position for pattern.

            /*
             * Split tokens from abstract pattern
             * to rebuild regexp pattern.
             */
            if ($text = substr($pattern, $pos, $match[0][1] - $pos)) {
                $tokens[] = array( self::TOKEN_TYPE_TEXT, $text);
            }

            // the first char from pattern (which is the seperater)
            // and the first char from the next match pattern
            $seps = array($pattern[$pos]);


            // collect the separator tokens from the next matched pattern
            if (isset($matches[$matchIdx + 1])) {
                $nextMatch = $matches[$matchIdx + 1];

                // check if the next pattern is an optional pattern
                if ($nextMatch[0][0][0] === '(') {

                    $r = self::compilePattern($nextMatch[2][0] ,array());
                    if (isset($r['tokens'][0][1])) {
                        $seps[] = $r['tokens'][0][1][0];
                    }

                } else if ($nextMatch[0][0][0] === ':') { // variable token

                } else {
                    $seps[] = $nextMatch[0][0][0];
                }
            }


            $pos = $match[0][1] + strlen($match[0][0]);


            // generate optional pattern recursively
            if ($match[0][0][0] == '(' ){
                $optional = $match[2][0];
                $subroute = self::compilePattern($optional ,array(
                    'default'   => isset($options['default']) ? $options['default'] : null,
                    'require'   => isset($options['require']) ? $options['require'] : null,
                    'variables' => isset($options['variables']) ? $options['variables'] : null,
                ));

                $tokens[] = array( 
                    self::TOKEN_TYPE_OPTIONAL,
                    $optional[0],
                    $subroute['regex'],
                );
                foreach( $subroute['variables'] as $var ) {
                    $variables[] = $var;
                }
            } else {
                // generate a variable token 
                $varName = $match[1][0];

                // if we defined a pattern for this variable, we should use the given pattern..
                if ( isset( $options['require'][$varName] ) && $req = $options['require'][$varName]) {
                    $regexp = $req;
                } else {
                    if ($pos !== $len) {
                        $seps[] = $pattern[$pos];
                    }
                    // use the default pattern (which is based on the separater charactors we got)
                    $regexp = sprintf('[^%s]+', preg_quote(implode('', array_unique($seps)), '#'));
                }

                // append token item
                $tokens[] = array(self::TOKEN_TYPE_VARIABLE, 
                    $match[0][0][0], 
                    $regexp, 
                    $varName);

                // append variable name
                $variables[] = $varName;
            }
        }

        if ($pos < $len) {
            $tokens[] = array(self::TOKEN_TYPE_TEXT, substr($pattern, $pos));
        }

        // find the first optional token
        $firstOptional = INF;
        for ($i = count($tokens) - 1; $i >= 0; $i--) {
            if ( self::TOKEN_TYPE_VARIABLE === $tokens[$i][0] 
                && isset($options['default'][ $tokens[$i][3] ]) )
            {
                $firstOptional = $i;
            } 
            else 
            {
                break;
            }
        }

        // compute the matching regexp
        $regex = '';

        // indentation level
        $indent = 1;


        // token item structure:
        //   [0] => token type,
        //   [1] => separator
        //   [2] => pattern
        //   [3] => name, 

        // first optional token and only one token.
        if (1 === count($tokens) && 0 === $firstOptional) {
            $token = $tokens[0];
            ++$indent;

            // output regexp with separator and
            $regex .= str_repeat(' ', $indent * 4) . sprintf("%s(?:\n", preg_quote($token[1], '#'));

            // regular expression with place holder name. (?P<name>pattern)
            $regex .= str_repeat(' ', $indent * 4) . sprintf("(?P<%s>%s)\n", $token[3], $token[2]);

        } else {
            foreach ($tokens as $i => $token) {

                switch ( $token[0] ) {
                case self::TOKEN_TYPE_TEXT:
                    $regex .= str_repeat(' ', $indent * 4) . preg_quote($token[1], '#')."\n";
                    break;
                case self::TOKEN_TYPE_OPTIONAL:
                    // the question mark is for optional, the optional item may contains multiple tokens and patterns
                    $regex .= str_repeat(' ', $indent * 4) . "(?:\n" . $token[2] . str_repeat(' ', $indent * 4) . ")?\n";
                    break;
                default:
                    // append new pattern group for the optional pattern
                    if ($i >= $firstOptional) {
                        $regex .= str_repeat(' ', $indent * 4) . "(?:\n";
                        ++$indent;
                    }
                    $regex .= str_repeat(' ', $indent * 4). sprintf("%s(?P<%s>%s)\n", preg_quote($token[1], '#'), $token[3], $token[2]);
                    break;
                }
            }
        }

        // close groups
        while (--$indent) {
            $regex .= str_repeat(' ', $indent * 4).")?\n";
        }

        // save variables
        $options['variables'] = $variables;
        $options['regex'] = $regex;
        $options['tokens'] = $tokens;
        return $options;
    }
526549
protected function regexifyPattern($pattern)
    {
        preg_match_all('/(\/?)\(:([^)]*)\)(\??)/', $pattern, $regExPatterns, PREG_SET_ORDER, 0);

        $pattern = preg_quote($pattern, '/');

        foreach ($regExPatterns as $regExPattern) {
            if (!empty($regExPattern[2]) && key_exists($regExPattern[2], $this->patterns)) {
                $replacement = sprintf(
                    '(%s%s)%s',
                    empty($regExPattern[1]) ? '' : '\/',
                    $this->patterns[$regExPattern[2]],
                    $regExPattern[3]
                );

                $pattern     = str_replace(preg_quote($regExPattern[0], '/'), $replacement, $pattern);
            }
        }

        return "/^$pattern$/";
    }
1046963
protected static function compilePatternAsRegex($pattern, $rules=[], $extract_params=true){

      return '#^'.preg_replace_callback('#:([a-zA-Z]\w*)#',$extract_params
        // Extract named parameters
        ? function($g) use (&$rules){
            return '(?<' . $g[1] . '>' . (isset($rules[$g[1]])?$rules[$g[1]]:'[^/]+') .')';
          }
        // Optimized for matching
        : function($g) use (&$rules){
            return isset($rules[$g[1]]) ? $rules[$g[1]] : '[^/]+';
          },
      str_replace(['.',')','*'],['\.',')?','.+'],$pattern)).'$#';
    }
732829
public function cropSuffix(string $fqn, string $suffix): string
    {
        if ($suffix === \substr($fqn, -\strlen($suffix))) {
            return \substr($fqn, 0, -\strlen($suffix));
        }

        return $fqn;
    }
1240180
public function addRule($rule, $route, $method = '*', $option = [], $pattern = [])
    {
        // 读取路由标识
        if (is_array($rule)) {
            $name = $rule[0];
            $rule = $rule[1];
        } elseif (is_string($route)) {
            $name = $route;
        } else {
            $name = null;
        }

        $method = strtolower($method);

        if ('/' === $rule || '' === $rule) {
            // 首页自动完整匹配
            $rule .= '$';
        }

        // 创建路由规则实例
        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method, $option, $pattern);

        if (!empty($option['cross_domain'])) {
            $this->router->setCrossDomainRule($ruleItem, $method);
        }

        $this->addRuleItem($ruleItem, $method);

        return $ruleItem;
    }
429628
function rule_match_page_template( $match, $rule, $options )
	{
		$page_template = $options['page_template'];
		if( ! $page_template )
		{
			$page_template = get_post_meta( $options['post_id'], '_wp_page_template', true );
		}
		
		
		if( ! $page_template )
		{
			$post_type = $options['post_type'];

			if( !$post_type )
			{
				$post_type = get_post_type( $options['post_id'] );
			}
			
			if( $post_type == 'page' )
			{
				$page_template = "default";
			}
		}
		
		
		
        if($rule['operator'] == "==")
        {
        	$match = ( $page_template === $rule['value'] );
        }
        elseif($rule['operator'] == "!=")
        {
        	$match = ( $page_template !== $rule['value'] );
        }
                
        return $match;

	}
1078702
protected function getTagPattern(string $pattern): string
    {
        // {...}标签左侧为{!}时不执行转换
        return '/(?<!{!})' . preg_quote($this->startTag) . $pattern . preg_quote($this->endTag) . '/s';
    }
1128592
public function match($query)
    {
        $ruleParts  = explode('/', $this->rule);
        $queryParts = explode('/', $query);

        $ruleCount = count($ruleParts);
        //$querCount = count($queryParts);
        
        $match = false;
        for ($i = 0; $i < $ruleCount; $i++) {
            $rp = $ruleParts[$i];
            $qp = isset($queryParts[$i]) ? $queryParts[$i] : null;
            
            $first = isset($rp[0]) ? $rp[0] : '';
            switch ($first) {
                case '*':
                    $match = true;
                    break;
                
                default:
                    $match = ($rp == $qp);
                    break;
            }
            
            if (!$match) {
                return false;
            }
        }
        
        //echo "$this->rule == $query\n";
        return true;
    }