Совместимость с PHP-глобусом
Чтобы все было в порядке, я написал систему управления доступом.
Одно из требований этой системы - проверить, доступен ли доступ к каноническому/нормализованному пути или нет, сопоставляя его с шаблоном.
Первые мысли упадут на PREG, проблема в том, что шаблоны основаны на файлах, то есть аналогичны тем, которые были приняты glob()
. В принципе, это просто шаблоны, содержащие ?
(соответствуют одному произвольному символу) или *
(соответствуют любому символу).
Итак, в простых терминах мне нужно воссоздать функциональность соответствия glob()
на PHP.
Пример кода:
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: Причина, по которой я не могу использовать регулярное выражение, заключается в том, что шаблоны будут записаны не-программистами.
Ответы
Ответ 1
Преобразование в регулярное выражение кажется лучшим решением для меня. Все, что вам нужно сделать, это преобразовать *
в .*
, ?
в .
и preg_quote
. Однако это не так просто, как может показаться, потому что это немного проблема с курицей и яйцом с точки зрения порядка, в котором вы делаете.
Мне не нравится это решение, но это лучшее, что я могу придумать: используйте регулярное выражение для генерации регулярного выражения.
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);
}
EDIT Добавлена опция чувствительности к регистру.
Посмотрите, как работает
Ответ 2
Используйте fnmatch()
, который, кажется, делает трюк.
Ответ 3
В PHP уже есть функция, включенная с PHP 4.3.0.
fnmatch()
проверяет, соответствует ли переданная строка данному шаблону шаблона оболочки.
Ответ 4
Из документации PHP для glob(). Я думаю, что preg_match - наилучшее решение.
http://php.net/manual/en/function.glob.php
<?php
function match_wildcard( $wildcard_pattern, $haystack ) {
$regex = str_replace(
array("\*", "\?"), // wildcard chars
array('.*','.'), // regexp chars
preg_quote($wildcard_pattern)
);
return preg_match('/^'.$regex.'$/is', $haystack);
}
$test = "foobar and blob\netc.";
var_dump(
match_wildcard('foo*', $test), // TRUE
match_wildcard('bar*', $test), // FALSE
match_wildcard('*bar*', $test), // TRUE
match_wildcard('**blob**', $test), // TRUE
match_wildcard('*a?d*', $test), // TRUE
match_wildcard('*etc**', $test) // TRUE
);
?>
Ответ 5
Я думаю, что это должно работать для превращения шаблонов glob в шаблоны регулярных выражений:
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';
}
Вместо этого вы можете использовать [^\\/]*
для *
, если вы хотите, чтобы *
не совпадали с именами каталогов.