PHP全局样式匹配
简而言之,我写了一个访问控制系统 该系统的要求之一是通过将规范化/规范化路径与模式匹配来检查是否可以访问该路径 首先想到的是PREG,问题是,这些模式是基于文件的,即类似于PHP全局样式匹配,php,path,pattern-matching,glob,Php,Path,Pattern Matching,Glob,简而言之,我写了一个访问控制系统 该系统的要求之一是通过将规范化/规范化路径与模式匹配来检查是否可以访问该路径 首先想到的是PREG,问题是,这些模式是基于文件的,即类似于glob()所接受的模式。基本上,它只是包含?(匹配一个任意字符)或*(匹配任何字符)的模式 因此,简单地说,我需要在PHP上重新创建glob()的匹配功能 示例代码: function path_matches($path, $pattern){ // ... ? } path_matches('path/inde
glob()
所接受的模式。基本上,它只是包含?
(匹配一个任意字符)或*
(匹配任何字符)的模式
因此,简单地说,我需要在PHP上重新创建glob()
的匹配功能
示例代码:
function path_matches($path, $pattern){
// ... ?
}
path_matches('path/index.php', 'path/*'); // true
path_matches('path2/', 'path/*'); // false
path_matches('path2/test.php', 'path2/*.php'); // true
一个可能的解决方案是将$pattern
转换为正则表达式,而不是使用preg_match()
,但是还有其他方法吗
NB:我不能使用正则表达式的原因是模式将由非程序员编写。使用,这似乎起到了作用。转换为正则表达式似乎是我的最佳解决方案。您只需将
*
转换为*
,?
转换为
和preg
。然而,这并不像看上去那么简单,因为从你做事的顺序来看,这是一个鸡和蛋的问题
我不喜欢这个解决方案,但它是我能想到的最好的:使用正则表达式生成正则表达式
function path_matches($path, $pattern, $ignoreCase = FALSE) {
$expr = preg_replace_callback('/[\\\\^$.[\\]|()?*+{}\\-\\/]/', function($matches) {
switch ($matches[0]) {
case '*':
return '.*';
case '?':
return '.';
default:
return '\\'.$matches[0];
}
}, $pattern);
$expr = '/'.$expr.'/';
if ($ignoreCase) {
$expr .= 'i';
}
return (bool) preg_match($expr, $path);
}
编辑添加了区分大小写选项
来自glob()的PHP文档。我认为preg_match是最好的解决方案
PHP中已经有一个函数,从PHP4.3.0开始就包含了它 检查传递的字符串是否与给定的shell通配符模式匹配
我认为这应该可以将glob模式转换为regex模式:
function glob2regex($globPatt) {
return '/'.preg_replace_callback('/./u', function($m) {
switch($m[0]) {
case '*': return '.*';
case '?': return '.';
}
return preg_quote($m[0],'/');
}, $globPatt).'\z/AsS';
}
如果要防止
*
与目录名匹配,您可能需要将[^\\/]*
用于*
。如果在php中已经存在glob(),为什么要重新创建它?嗯,请把问题再读一遍glob()
使用实际路径,我需要重新创建其模式匹配功能。我不知道如何在我的案例中使用glob()
(在不存在的路径上)。我不好,请看我的答案:)这会起作用,但我需要一个能在所有系统上工作的,而不仅仅是POSIX+1尝试一下。此功能在Windows上可用,因为5.3是的,我考虑了str_replace()
方法,但我仍然无法确定它在所有情况下是否安全。您还需要将preg_match()
的返回值强制转换为bool,但这是一个次要考虑因素。这就是preg_引号的作用。它将使整个字符串正则表达式安全。然后它就变成了*和?又不安全了。所以,是的,它做你想做的,不能被滥用。不,我知道,我不喜欢的是\*
和\?
的后续替换,特别是因为主题字符串/模式字符串可能合法地包含反斜杠。另外,我刚刚注意到,您没有指定分隔符/
。我认为Dave的担心是合理的。如果有更明确的方法,我更喜欢这个方法,而不是更复杂的正则表达式。你总是可以用类似[a-zA-Z0-9\]的东西来改变*我认为这应该是万无一失的。谢谢Dave,“看到它工作”链接指向一个只显示PHP错误的页面。这真的很好。但是,它不能正常工作。例如,*.js被转换为“/.*\\.js/”。这看起来是对的,但可能不是。生成的正则表达式将与abc.jsx匹配,这可能不是用户想要的。更糟糕的是,生成的正则表达式将匹配abc/def.js。您是否需要在生成的正则表达式周围添加^
和$
?glob通常锚定在开头和结尾,而regex在默认情况下不是。
function glob2regex($globPatt) {
return '/'.preg_replace_callback('/./u', function($m) {
switch($m[0]) {
case '*': return '.*';
case '?': return '.';
}
return preg_quote($m[0],'/');
}, $globPatt).'\z/AsS';
}