需要 Php 路由 preg_match 帮助

标签 php regex preg-match

我有一个自定义路由类,它允许我对请求进行如下匹配:

'/[*:cat1]/[*:cat2]/?[*:cat3]/?[*:cat4]/?[p:page]/?'

它将匹配以下链接:

category-one/
category-one/cat-two/
category-one/cat-two/cat-three/
category-one/cat-two/cat-three/cat-four/

正如你所看到的?/之后表示该参数是可选的。

我的问题出在 [p:page]/?这也是可选的。

category-one/page-2/
category-one/cat-two/page-2/
category-one/cat-two/cat-three/page-2/
category-one/cat-two/cat-three/cat-four/page-2/

My problem is that when i try to match this link

/category-one/cat-two/page-2/

it will give me these params:

cat1 => category-one
cat2 => cat-two
cat3 => page-2

而不是

cat1 => category-one
cat2 => cat-two
page => page-2

我正在使用这个生成的正则表达式:

`^(?:/(?P<cat1>[^/\.]+))(?:/(?P<cat2>[^/\.]+/)?)(?:(?P<cat3>[^/\.]+/)?)(?:(?P<cat4>[^/\.]+/)?)(?:(?P<page>(a^)|(?:pag-)(\d+)/)?)$`u

感谢任何帮助。谢谢! 亚历克斯

最佳答案

我会使用 token 词法分析器/解析器方法。我的 git hub 页面上有一些示例:

https://github.com/ArtisticPhoenix/MISC/tree/master/Lexers

这些是我用来回答有关 SO 的问题的其他内容,其中一个是 JSON 对象解析器,而不是 JSON 字符串。如果没有 ",这将是格式错误的 JSON围绕 json_decode 的属性无法处理。另一个是 HTML 缩小器(采用 OOP 风格,但概念相同),您可以排除类似 <textarea> 的内容。标签,因为空白在那里很重要。因此,您几乎可以使用此方法对文本进行任何类型的处理。

我修改了一个,但我真的不知道你想要如何输出或你想用它做什么,但它应该可以帮助你开始。也许你必须将它集成到你的 URL 路由类中,我不知道它是什么样的。但这是一个比简单的 preg_match 更好的方法。因为它为您提供了在比赛的每个部分执行复杂逻辑的地方。

 //don't edit this part.
function parse($subject, $tokens)
{
    $types = array_keys($tokens);
    $patterns = [];
    $lexer_stream = [];
    $result = false;
    foreach ($tokens as $k=>$v){
        $patterns[] = "(?P<$k>$v)";
    }
    $pattern = "/".implode('|', $patterns)."/i";
    if (preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE)) {
        //print_r($matches);
        foreach ($matches[0] as $key => $value) {
            $match = [];
            foreach ($types as $type) {
                $match = $matches[$type][$key];
                if (is_array($match) && $match[1] != -1) {
                    break;
                }
            }
            $tok  = [
                'content' => $match[0],
                'type' => $type,
                'offset' => $match[1]
            ];
            $lexer_stream[] = $tok;
        }
        $result = parseTokens( $lexer_stream );
    }
    return $result;
}

//make changes here to how the tokens are dealt with
function parseTokens( array &$lexer_stream ){
    $result = [];

    while($current = current($lexer_stream)){
        $content = $current['content'];
        $type = $current['type'];
        switch($type){  
            case 'T_EOF': return;

            //custom code for you tokens.
            case 'T_DELIMTER': 
            case 'T_BASE': 
                //ignore these
                next($lexer_stream); //don't forget to call next
            break;
            case 'T_CAT':
                $cat = substr($content, 4);
                echo "This is Cat ".$cat."\n";
                next($lexer_stream);
            break;
            case 'T_PAGE':
                $page = substr($content, 5);
                echo "This is Page".$page;
                next($lexer_stream);
            break;

            //catch all token
            case 'T_UNKNOWN':
            default:
                print_r($current);
                trigger_error("Unknown token $type value $content", E_USER_ERROR);
        }
    }
    if( !$current ) return;
    print_r($current);
    trigger_error("Unclosed item $mode for $type value $content", E_USER_ERROR);
}

/**
 * token should be "name" => "regx"
 * 
 * Order is important
 * 
 * @var array $tokens
 */
$tokens = [
    'T_EOF'             => '\Z',
    'T_DELIMTER'        => '\/',
    'T_BASE'            => 'category-one',
    'T_CAT'             => 'cat-(?:one|two|three|four)',
    'T_PAGE'            => 'page-\d+',
    'T_UNKNOWN'         => '.+?',
];

$subject = '/category-one/cat-two/page-2/';

parse($subject, $tokens);

echo "\n\n========================================\n\n";

$subject = '/category-one/cat-two/cat-three/cat-four/page-2/';

parse($subject, $tokens);

您可以看到它的实际效果 here

上述代码的输出:

//$subject = '/category-one/cat-two/page-2/';
This is Cat two
This is Page2

========================================

//$subject = '/category-one/cat-two/cat-three/cat-four/page-2/';
This is Cat two
This is Cat three
This is Cat four
This is Page2

它是如何工作的,这基本上使用 preg match all,但它被包装在一个说服类型的交易中,以使处理输出和构建正则表达式变得更容易。因此,您最终会得到一个更小、更容易处理的 Regx,而不是一个单一的 Regx。乍一看似乎很复杂,但实际上一旦你了解了它的作用,它就会变得容易得多。

如果需要,您甚至可以通过在 parseTokens 中添加一些逻辑来检查顺序。功能。这应该是您必须编辑内容的唯一地方,主要是在 token switch 语句中。

它创建的regx是这样的

/(?P<T_EOF>\Z)|(?P<T_DELIMTER>\/)|(?P<T_BASE>category-one)|(?P<T_CAT>cat-(?:one|two|three|four))|(?P<T_PAGE>page-\d+)|(?P<T_UNKNOWN>.+?)/i

所以当我在这个 cat-(?:one|two|three|four) 中添加 或 时,您无法添加子捕获组。这是一个非捕获组。但你可以只使用 substr稍后将其分开,所以没什么大不了的。

\Z有点晦涩,但它只是匹配字符串的结尾,而不捕获任何内容。

处理部分也这样调用(在 parse 中):

$result = parseTokens( $lexer_stream );
...
return $result;

因此您可以返回通过 parse 返回的数据函数到您调用它的地方(如果您愿意)

  $something = parse($subject,$tokens);

我现在没有时间详细解释词法分析器是什么或者它是如何工作的。所以希望这足以让您开始。

更新

It's a good start, but your code is very specific,

为了反驳这一点(不要误会我的意思或以错误的方式理解这一点),我觉得我需要进一步解释一下。

这是非常普遍的

$tokens = [
    'T_EOF'             => '\Z',
    'T_DELIMTER'        => '\/',
    'T_BASE'            => 'category-one',
    'T_CAT'             => 'cat-(?:one|two|three|four)',
    'T_PAGE'            => 'page-\d+',
    'T_UNKNOWN'         => '.+?',
];

这非常具体

`^(?:/(?P<cat1>[^/\.]+))(?:/(?P<cat2>[^/\.]+/)?)(?:(?P<cat3>[^/\.]+/)?)(?:(?P<cat4>[^/\.]+/)?)(?:(?P<page>(a^)|(?:pag-)(\d+)/)?)$`u

如果你必须编辑它,这将是一个大问题,如果你想路由到书籍或其他东西怎么办?您将如何扩展这一点?我什至不知道从哪里开始。

我给你的数组方法,你简单添加即可

$tokens = [
    'T_EOF'             => '\Z',
    'T_DELIMTER'        => '\/',
    'T_BASE'            => 'category-one',
    'T_CAT'             => 'cat-(?:one|two|three|four)',
    'T_PAGE'            => 'page-\d+',
    'T_BOOK'            => 'book-\w+',
    'T_UNKNOWN'         => '.+?',
];

然后修改 switch 语句:

  case 'T_BOOK':
       ///do something
  break;

而且,您可以以清晰简洁的方式做任何您想做的事情。您可以非常轻松地添加您需要的任何复杂逻辑、任何错误检查等。

关于需要 Php 路由 preg_match 帮助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51900441/

相关文章:

PHP PDO 登录帐户

javascript - Symfony CMF CSS 未加载 : Resource interpreted as Stylesheet but transferred with MIME type text/html:

php - 为什么 str 替换仅在第一个匹配中起作用?

php - 使用 preg_match 只接受 UTF8 字母

php - 如何搜索序列号中的模式?

javascript - jquery - 隐藏不工作

php - 我们如何在不安装的情况下从随身碟运行 Web 服务器?

用于解析 : set Properly Name Is X = value # comment 的 JavaScript 正则表达式

regex - Linux bash 脚本 - 替换文件中最后一次出现的字符串

python - re.findall 不返回完整匹配?