2

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 phps 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!

Tom Wright
  • 2,841
  • 4
  • 22
  • 30
  • Have you tried resolving the placeholder first, and then validating it? – Niet the Dark Absol Jun 20 '16 at 15:14
  • 1
    Some obscure magic using `(*SKIP)(*FAIL)`: [`{DATE:[^}]*}(*SKIP)(*FAIL)|["*:%$/\\\'?]`](https://regex101.com/r/pW4pO5/1). [Explanation here](http://stackoverflow.com/questions/24534782/how-do-skip-or-f-work-on-regex) – HamZa Jun 20 '16 at 15:15
  • 1
    @HamZa Thanks for your reply. It's incredibly close to what I needed. All I've had to do to get it working was add my list of rejected characters to the negated character class at the start: `{DATE:[^}"*:%$/\\\' ?]*}(*SKIP)(*FAIL)|["*:%$/\\\' ?]`. Add this as an answer and I'll happily accept it – Tom Wright Jun 20 '16 at 15:22

1 Answers1

2

You can use a negative lookbehind after the character class:

$patternTwo = '#["*:%$/\\\\\'?](?<!{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.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • Thanks for your answer! I ended up marking this as correct over the comment given by @HamZa due to the fact I only had to include my restricted characters once. Thanks for the help! – Tom Wright Jun 20 '16 at 15:27
  • 2
    Please note that you need 4 backslashes to match a literal backslash with a PHP single-quoted regex, I have fixed that just now. And I agree the `(*SKIP)(*F)` technique is quite handy in these scenarios. I also removed the unnecessary `\b` since it requred a word letter before the `{`. – Wiktor Stribiżew Jun 20 '16 at 15:34