17

I am writing a form validation class and wish to include regular expressions in the validation. Therefore, the regex provided isn't guaranteed to be valid.

How can I (efficiently) check that the regex is valid?

CrazeD
  • 525
  • 2
  • 6
  • 12

6 Answers6

25

Use the pattern in your preg_* calls. If the function returns false there is likely a problem with your pattern. As far as I know this is the easiest way to check if a regex pattern is valid in PHP.


Here's an example specifying the right kind of boolean check:

$invalidPattern = 'i am not valid regex';
$subject = 'This is some text I am searching in';
if (@preg_match($invalidPattern, $subject) === false) {
    // the regex failed and is likely invalid
}
Charles Sprayberry
  • 7,741
  • 3
  • 41
  • 50
  • 4
    Just to emphasize, that's strictly (`===`) Boolean `false`, not a falsy (`==`) value such as `0`. – Wiseguy Jan 11 '12 at 19:02
  • That seems to work. I figured the preg_* would return false if it didn't match the expression, thereby giving false positives. – CrazeD Jan 11 '12 at 19:11
  • 2
    @CrazeD Depending on the function called and the option passed it might be a variety of values. For the above if `$subject` did not match `$pattern`, and was valid, it will return `0`. However, `preg_replace` will return `NULL` on failure instead of false. You'll just have to look at the docs for the particular function you are using. – Charles Sprayberry Jan 11 '12 at 19:14
  • 3
    @cspray I'd advise preceding your preg_* calls with the @ in order to suppress PHP Warnings. I.e. `if (@preg_match($possiblyInvalidPattern, '') === false)` This may or may not be a problem depending on your [error_reporting](http://www.php.net/manual/en/function.error-reporting.php) level. – Ben Dec 01 '12 at 02:13
  • 2
    Never use the @ operator. Ever – twicejr Jul 30 '14 at 21:30
  • 2
    While I certainly agree that you should not use the `@` operator in nearly all situations there are edge cases where its use is warranted. This certainly appears to be one of those edge cases. – Charles Sprayberry Jul 30 '14 at 23:03
2

When you have error reporting on, you can't get away with simply testing the boolean result. If the regex fails warnings are thrown (i.e. 'Warning: No ending delimiter xxx found'.)

What I find odd, is that the PHP documentation tells nothing about these thrown warnings.

Below is my solution for this problem, using try, catch.

//Enable all errors to be reported. E_WARNING is what we must catch, but I like to have all errors reported, always.
error_reporting(E_ALL);
ini_set('display_errors', 1);

//My error handler for handling exceptions.
set_error_handler(function($severity, $message, $file, $line)
{
    if(!(error_reporting() & $severity))
    {
        return;
    }
    throw new ErrorException($message, $severity, $severity, $file, $line);
});

//Very long function name for example purpose.
function checkRegexOkWithoutNoticesOrExceptions($test)
{
    try
    {
        preg_match($test, '');
        return true;
    }
    catch(Exception $e)
    {
        return false;
    }
}
twicejr
  • 1,319
  • 3
  • 13
  • 21
2

You shouldn't be using @ to silence all errors because it also silences fatal errors.

function isRegularExpression($string) {
  set_error_handler(function() {}, E_WARNING);
  $isRegularExpression = preg_match($string, "") !== FALSE;
  restore_error_handler();
  return isRegularExpression;
}

This only silences warnings for the preg_match call.

smichaelsen
  • 158
  • 8
1

Anyone still looking at this question anno 2018, and is using php 7, should be using try/catch.

try { 
    preg_match($mypattern, '');
} catch (\Throwable $exception) {
    // regex was invalid and more info is in $exception->getMessage()
}
Rein Baarsma
  • 1,466
  • 13
  • 22
  • This still won't work when error reporting is on. The error handler will execute instead of catch block. – Czar Pino May 01 '20 at 10:13
  • This won't work, but you _can_ get the error text by using a `@` and `error_get_last()`. See my answer for details. – dearsina Aug 28 '20 at 09:09
1

PHP has progressed quite a bit since this question was first asked (and answered). You can now (PHP 5.2+) simply write the following to, not only test if the regular expression is valid, but to get the detailed error message if it's not:

if(@preg_match($pattern, '') === false){
   echo error_get_last()["message"];
}

Placed in a function

/**
 * Return an error message if the given pattern argument or its underlying regular expression
 * are not syntactically valid. Otherwise (if they are valid), NULL is returned.
 *
 * @param $pattern
 *
 * @return string|null
 */
function regexHasErrors($pattern): ?string
{
    if(@preg_match($pattern, '') === false){
        //Silence the error by using a @

        return str_replace("preg_match(): ", "", error_get_last()["message"]);
        //Make it prettier by removing the function name prefix
    }
    return NULL;
}

Demo

dearsina
  • 4,774
  • 2
  • 28
  • 34
-7

This is my solution using the upcoming warning if something is wrong with the expression:

function isRegEx($test)
{
    $notThisLine = error_get_last();
    $notThisLine = isset($notThisLine['line']) ? $notThisLine['line'] + 0 : 0;
    while (($lines = rand(1, 100)) == $notThisLine);
    eval(
        str_repeat("\n", $lines) . 
        '@preg_match(\'' . addslashes($test) . '\', \'\');'
    );
    $check = error_get_last();
    $check = isset($check['line']) ? $check['line'] + 0 : 0;
    return $check == $notThisLine;
}
ortreum
  • 53
  • 6
  • 1
    Seriously, I've managed to find my way back here and I still have absolutely no idea about this snippet; what on earth is it doing? – Dan Lugg Jan 24 '14 at 20:33
  • 2
    This is a custom-coded try-catch. Very horrible to read but it probably works. I would simply use try-catch. – twicejr Oct 15 '14 at 07:47