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
  • 3
    To those that care: '[' is not a valid regex, therefore not all strings are valid regexs. Commented May 28, 2012 at 0:54
  • 2
    @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
18

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
}

// or as a function:
function isValidRegex(string $regex): bool
{
    return @preg_match($regex, '') !== false;
}
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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.