Php 路由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]/? 这也是可选的。

分类一/页-2/类别一/猫二/页-2/类别一/类二/类三/页-2/类别一/猫-二/猫-三/猫-四/页-2/

我的问题是当我尝试匹配此链接时

/
类别一/猫二/页-2/

它会给我这些参数:

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

任何帮助,不胜感激。谢谢! 亚历克斯

我会使用令牌词法分析器/解析器方法。 我的 git 中心页面上有几个示例:

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 "nn========================================nn";
$subject = '/category-one/cat-two/cat-three/cat-four/page-2/';
parse($subject, $tokens);

你可以在这里看到它的实际应用

上述代码的输出:

//$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函数中添加一些逻辑来检查顺序。 这应该是您必须编辑内容的唯一位置,主要是在令牌切换语句中。

它创建的正则表达式是这样的

/(?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

因此,当我添加 or 时,您无法添加子捕获组,cat-(?:one|two|three|four)它是一个非捕获组。 但是你可以用substr稍后再分开它,所以没什么大不了的。

Z有点晦涩难懂,但它只是匹配字符串的末尾,而没有捕获任何内容。

处理部分也像这样称呼(在parse(:

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

因此,您可以将将通过parse函数返回的数据返回到您调用它的位置(如果您愿意(

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

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

更新

这是一个好的开始,但你的代码非常具体,

为了解决这个问题(不要误会我的意思或以错误的方式对待它(,我认为我需要进一步解释一下。

这是非常笼统的

$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'         => '.+?',
];

然后修改开关语句:

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

Bam你可以以清晰简洁的方式做任何你想做的事情。您可以添加任何复杂的逻辑,任何错误检查等...你需要,很容易。

最新更新