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.
Use fnmatch()
, which seems to do the trick.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With