Tom Wright Tom Wright - 5 months ago 8
PHP Question

PHP Regex - Matching one of a character group, when ONE of that character group is not in a string

I feel like I've been hitting my head against a brick wall.

I have a string that looks like this:

$record['filenameGood'] = '49161_Comma_Dataphoria-Clickwork7Export{DATE:dmY}';


And I want to block filenames that contain any restricted characters.

However... I am using a placeholder for the current date, which looks like
{DATE:Y-m-d}
where
Y-m-d
would get inserted into
php
s
date
function.
This part I am fine with, it's just ensuring that the rest of the string doesn't contain a restricted character.

The script I am testing with looks like this:

// Matches one of " * : % $ / \ ' ?
$patternOne = '#["*:%$/\\\'?]#';

// Desired: matches one of " * : % $ / \ ' ?, but ALLOWS {DATE:.*?}
$patternTwo = '#["*:%$/\\\'?]#';
$record = [];
$record['filenameGood'] = '49161_Comma_Dataphoria-Clickwork7Export{DATE:dmY}';
$record['filenameBad'] = '49161_Comma_Dataphoria-Clickwork7:Export{DATE:dmY}';

var_dump(preg_match($patternTwo, $record['filenameGood']));
var_dump(preg_match($patternTwo, $record['filenameBad']));


The current output is:

int(1)
int(1)


Whereas my desired output is:

int(0) // Good string, contains : within {DATE:}
int(1) // Bad string, contains a : NOT within {DATE:}


I also need a string like the following to get matched:

'49161_Comma_Dataphoria-Clickwork7Export{DATE:d:m:Y}'


I hope I've explained this well enough for you to understand!

Answer

You can use a negative lookbehind after the character class:

$patternTwo = '#["*:%$/\\\\\'?](?<!\b{DATE:)#';
                               ^^^^^^^^^^^^^

See the IDEONE demo.

Here, one of the characters in the character class is matched first, but then the negative lookbehind will check if the : is not preceded with {DATE. If it is, the match will be failed.

Comments