Совместимость с 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';
}

Вместо этого вы можете использовать [^\\/]* для *, если вы хотите, чтобы * не совпадали с именами каталогов.