34

Is there a good way of test if a string is a regex or normal string in PHP?

Ideally I want to write a function to run a string through, that returns true or false.

I had a look at preg_last_error():

<?php
preg_match('/[a-z]/', 'test');
var_dump(preg_last_error());
preg_match('invalid regex', 'test');
var_dump(preg_last_error());
?>

Where obviously first one is not an error, and second one is. But preg_last_error() returns int 0 both times.

Any ideas?

9
  • 2
    Maybe you could use a regex for the test ^^
    – greg0ire
    Commented May 28, 2012 at 0:52
  • 2
    'invalid regex' is a valid regex - in fact, any string that doesn't involve the regex special characters will be a valid regex. Try '[' instead. Commented May 28, 2012 at 0:52
  • 2
    To those that care: '[' is not a valid regex, therefore not all strings are valid regexs. Commented May 28, 2012 at 0:54
  • 1
    @AasmundEldhuset, 'invalid regex' is actually not a valid regex, PHP returns Warning: preg_match() [function.preg-match]: Delimiter must not be alphanumeric or backslash in D:\xampp\htdocs\overheard\test.php on line 5.
    – GManz
    Commented May 28, 2012 at 1:01
  • 1
    @ThiefMaster: Except that will make a valid regex invalid. Commented May 28, 2012 at 1:07

5 Answers 5

20

The simplest way to test if a string is a regex is:

if( preg_match("/^\/.+\/[a-z]*$/i",$regex))

This will tell you if a string has a good chance of being intended to be as a regex. However there are many string that would pass that check but fail being a regex. Unescaped slashes in the middle, unknown modifiers at the end, mismatched parentheses etc. could all cause problems.

The reason preg_last_error returned 0 is because the "invalid regex" is not:

  • PREG_INTERNAL_ERROR (an internal error)
  • PREG_BACKTRACK_LIMIT_ERROR (excessively forcing backtracking)
  • PREG_RECURSION_LIMIT_ERROR (excessively recursing)
  • PREG_BAD_UTF8_ERROR (badly formatted UTF-8)
  • PREG_BAD_UTF8_OFFSET_ERROR (offset to the middle of a UTF-8 character)
2
  • Yes this is correct! Had this kind of figured out, just didn't know how to do it properly! However, using track_errors worked!
    – GManz
    Commented May 28, 2012 at 1:09
  • 2
    Note also, this only works for regexes with / delimiters.
    – Phil Tune
    Commented Mar 4, 2016 at 18:34
17

Here is a good answer how to:

https://stackoverflow.com/a/12941133/2519073

if(@preg_match($your_pattern, '') === false){
    //pattern is broken
}else{
    //pattern is real
}
2
  • Better solutions the rest 2, as track_errors is deprecated with PHP 7.2 Commented Feb 4, 2021 at 21:39
  • 1
    Only one suggestion - the 2nd parameter should be a string (e.g. an empty string). In newer PHP versions that would cause a deprecation error (although the @ mutes it anyway, but I wouldn't rely on it): 3v4l.org/5YI6t
    – Christian
    Commented Dec 5, 2023 at 20:49
10

The only easy way to test if a regex is valid in PHP is to use it and check if a warning is thrown.

ini_set('track_errors', 'on');
$php_errormsg = '';
@preg_match('/[blah/', '');
if($php_errormsg) echo 'regex is invalid';

However, using arbitrary user input as a regex is a bad idea. There were security holes (buffer overflow => remote code execution) in the PCRE engine before and it might be possible to create specially crafted long regexes which require lots of cpu/memory to compile/execute.

3
  • It's not arbitrary user input, rather, rows taken from a database table that will only be added through direct code (for link generation). But yes, this worked :) Thanks!
    – GManz
    Commented May 28, 2012 at 1:08
  • 2
    Why is this better than just checking the output of preg_match?
    – Veda
    Commented Aug 24, 2015 at 9:55
  • track_errors is deprecated as of PHP 7.2 Commented Feb 4, 2021 at 21:38
9

Why not just use...another regex? Three lines, no @ kludges or anything:

// Test this string
$str = "/^[A-Za-z ]+$/";

// Compare it to a regex pattern that simulates any regex
$regex = "/^\/[\s\S]+\/$/";

// Will it blend?
echo (preg_match($regex, $str) ? "TRUE" : "FALSE");

Or, in function form, even more pretty:

public static function isRegex($str0) {
    $regex = "/^\/[\s\S]+\/$/";
    return preg_match($regex, $str0);
}

This doesn't test validity; but it looks like the question is Is there a good way of test if a string is a regex or normal string in PHP? and it does do that.

5
  • 1
    I presume it's because you can't detect all regexes with a regex - the best this can do is decide whether it looks kinda like a regex or not. Commented Jul 27, 2013 at 4:56
  • For the regex-lovers, the answer to this question might be interesting... Commented Sep 16, 2016 at 18:14
  • 1
    Does not work with flags? Sample '/(\n)}[\s\n\r]*$/s'
    – ajthinking
    Commented May 13, 2018 at 18:13
  • 1
    this answer does not support modifiers
    – alexwenzel
    Commented Nov 15, 2020 at 17:23
  • 1
    ..nor does it support any other delimiter than /. E.g. ``#/some/path/#` is a valid regex using hash marks, which is very common when matching paths. And since it's using regex matching anyway, might as well go with the false-return approach.
    – Christian
    Commented Dec 5, 2023 at 20:38
0

Since regex must be always surrounded only with one of three boundary characters /, @, #, and trailing characters may only be allowed modifiers, we can use quick test:

if (preg_match('/^[\/@#](.+)[\/@#imsxADSUXJun]+$/', $pattern, $match) ){
   // .. do stuff, $match[0] contains full regex
}

This does not guarantee proper REGEX syntax in any way, but helps with quick detection.

Not the answer you're looking for? Browse other questions tagged or ask your own question.