Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP glob-style matching

To keep things short, I wrote an access-control system.

One of the requirements of this system is to check whether a canonical/normalized path can be accessed or not by matching it against a pattern.

First thoughts would fall on PREG, problem is, the patterns are file-based, ie, similar to those accepted by glob(). Basically, it's just patterns containing ? (match one arbitrary character) or * (match any character).

So in simple terms, I need to recreate glob()'s matching functionality on PHP.

Sample code:

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

A possible solution would be to convert $pattern into a regular expression than use preg_match(), is there any other way though?

NB: The reason why I can't use regex is that patterns will be written by non-programmers.

like image 905
Christian Avatar asked Dec 17 '12 12:12

Christian


2 Answers

Use fnmatch(), which seems to do the trick.

like image 102
Intrepidd Avatar answered Oct 27 '22 06:10

Intrepidd


Converting to a regex seems like the best solution to me. All you need to do is convert * to .*, ? to . and preg_quote. However it's not as simple as it may seem because it's a bit of a chicken-and-egg problem in terms of the order in which you do things.

I don't like this solution but it's the best I can come up with: use a regex to generate the regex.

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 Added case-sensitivity option.

See it working

like image 40
DaveRandom Avatar answered Oct 27 '22 07:10

DaveRandom