PHP PREG_REPLACE根据检查顺序返回错误结果
我在PHP的preg_place函数和一些正则表达式模式中偶然发现了一个非常奇怪的错误。我要做的是替换由括号分隔的自定义标记,并将它们转换为HTML。正则表达式必须考虑自定义的"填充"标记,这些标记将保留在输出的HTML语言中,以便在页面加载时可以在飞翔上替换(例如,替换为站点名称)。
每个正则表达式模式都会自行工作,但是由于某些原因,如果先检查其他模式之一,则其中一些模式会提前退出函数。当我偶然发现这一点时,我使用了preg_match和foreach循环来检查模式,然后再继续,如果找到就返回结果-所以假设它对每个模式都是新的。这也不起作用。
校验码:
function replaceLTags($originalString){
$patterns = array(
'#^[l]([^s]+)[/l]$#i' => '<a href="$1">$1</a>',
'#^[l=([^s]+)]([^[]+)[/l]$#i'=> '<a href="$1">$2</a>',
'#^[l=([^s]+) title=([^[]+)]([^[]+)[/l]$#i' => '<a href="$1" title="$2">$3</a>',
'#^[l=([^s]+) rel=([^[]+)]([^[]+)[/l]$#i' => '<a href="$1" rel="$2">$3</a>',
'#^[l=([^s]+) onClick=([^[]+)]([^[]+)[/l]$#i' => '<a href="$1" onClick="$2">$3</a>',
'#^[l=([^s]+) style=([^[]+)]([^[]+)[/l]$#i' => '<a href="$1" style="$2">$3</a>',
'#^[l=([^s]+) onClick=([^[]+) style=([^[]+)]([^[]+)[/l]$#i' => '<a href="$1" onClick="$2" style="$3">$4</a>',
'#^[l=([^s]+) class=([^[]+) style=([^[]+)]([^[]+)[/l]$#i' => '<a href="$1" class="$2" style="$3">$4</a>',
'#^[l=([^s]+) class=([^[]+) rel=([^[]+)] target=([^[]+)]([^[]+)[/l]$#i' => '<a href="$1" class="$2" rel="$3" target="$4">$5</a>'
);
foreach ($patterns as $pattern => $replace){
if (preg_match($pattern, $originalString)){
return preg_replace($pattern, $replace, $originalString);
}
}
}
$string = '[l=[site_url]/site-category/ class=hello rel=nofollow target=_blank]Hello there[/l]';
echo $alteredString = $format->replaceLTags($string);
上述"字符串"将显示为:
<a href="[site_url">/site-category/ class=hello rel=nofollow target=_blank]Hello there</a>
何时应显示为:
<a href="[site_url]/site-category/" class="hello" rel="nofollow" target="_blank">Hello there</a>
但是,如果将该模式在列表中进一步向上移动以更快地进行检查,它的格式将正确。
我被难住了,因为每次检查字符串时似乎都会以某种方式覆盖它,尽管这没有任何意义。
解决方案
在我看来,您做的工作比您需要做的多得多。与其对每个可能的属性列表使用单独的正则表达式/替换,为什么不使用preg_replace_callback
在单独的步骤中处理属性呢?例如:
function replaceLTags($originalString){
return preg_replace_callback('#[l=((?>[^s[]]+|[site_url])+)([^]]*)](.*?)[/l]#',
replaceWithinTags, $originalString);
}
function replaceWithinTags($groups){
return '<a href="' . $groups[1] . '"' .
preg_replace('#(s+w+)=(S+)#', '$1="$2"', $groups[2]) .
'>' . $groups[3] . '</a>';
}
查看完整演示here(已更新;请参阅评论)。
以下是基于注释中提供的新信息的代码的更新版本:
function replaceLTags($originalString){
return preg_replace_callback('#[l=((?>[^s[]]+|[w+])+)([^]]*)](.*?)[/l]#',
replaceWithinTags, $originalString);
}
function replaceWithinTags($groups){
return '<a href="' . $groups[1] . '"' .
preg_replace(
'#(s+[^s=]+)s*=s*([^s=]+(?>s+[^s=]+)*(?!s*=))#',
'$1="$2"', $groups[2]) .
'>' . $groups[3] . '</a>';
}
demo
在第一个正则表达式中,我将[site_url]
更改为[w+]
,以便它可以匹配任何自定义填充标记。
以下是第二个正则表达式的细目:
(s+[^s=]+) # the attribute name and its leading whitespace
s*=s*
(
[^s=]+ # the first word of the attribute value
(?>s+[^s=]+)* # the second and subsequent words, if any
(?!s*=) # prevents the group above from consuming tag names
)
最棘手的部分是匹配多词属性值。(?>s+[^s=]+)*
将始终使用下一个标记名(如果有),但前视会强制它回溯。正常情况下,它一次只能后退一个字符,但原子组有效地迫使它通过整个单词或根本不后退。
相关文章