preg_last_error

Returns the error code from the most recent PCRE regex execution.

Signature
preg_last_error(): int
  • Added in PHP 5.2.0

Return value

intOne of PREG_NO_ERROR, PREG_INTERNAL_ERROR, PREG_BACKTRACK_LIMIT_ERROR, PREG_RECURSION_LIMIT_ERROR, PREG_BAD_UTF8_ERROR, PREG_BAD_UTF8_OFFSET_ERROR or PREG_JIT_STACKLIMIT_ERROR.

Changelog

  • PHP 7.0PREG_JIT_STACKLIMIT_ERROR was added, returned when the PCRE JIT runs out of stack on a large subject.

It is also set by pattern-compilation failures, but only bluntly: a syntactically broken pattern emits an E_WARNING carrying the compile diagnostic and sets the code to PREG_INTERNAL_ERROR (1). Verified on PHP 8.3: after @preg_match('/[/', 'x'), preg_last_error() is 1, not 0. So the code tells you something went wrong but not that it was a syntax error — the detail lives in the warning.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Detect catastrophic backtracking in a validator that runs on user-submitted text
  • Distinguish 'no match' from 'invalid UTF-8 subject' when parsing scraped pages
  • Log a real reason when a preg_replace on a large document unexpectedly returns null

Frequently asked questions

Why does preg_match return false instead of 0?

false means the engine failed rather than finding no match — usually the backtrack or recursion limit, or invalid UTF-8 with the u modifier. Call preg_last_error() straight afterwards to get the reason.

What is the difference between preg_last_error and preg_last_error_msg?

They report the same failure: preg_last_error() returns an integer constant you can compare against, preg_last_error_msg() returns a human-readable string for logs. preg_last_error_msg() requires PHP 8.0 or later.

Does preg_last_error report an invalid pattern?

Only indirectly. A pattern that fails to compile emits an E_WARNING with the compile message and sets preg_last_error() to PREG_INTERNAL_ERROR (1), so you can see that something failed but not that it was a syntax error — PREG_INTERNAL_ERROR covers other internal failures too, and the readable compile diagnostic is only in the warning.

More examples

The code is reset by the next call

The first echo reports the backtrack-limit code, the second reports PREG_NO_ERROR (0). Any later preg_* call, successful or not, overwrites the stored code, which is why you must read it immediately.

Example · php
<?php
preg_match('/(?:\D+|<\d+>)*[!?]/', 'foobar foobar foobar');
echo preg_last_error(), "\n";   // non-zero: the failure above

preg_match('/foo/', 'foobar');
echo preg_last_error(), "\n";   // 0 - the good call cleared it

Catching malformed UTF-8

With the u modifier the subject must be valid UTF-8; it is not, so preg_match returns false and the error code becomes PREG_BAD_UTF8_ERROR (4). Without the u modifier the same subject would match happily as raw bytes.

Example · php
<?php
$bad = "\xB1\x31";              // not valid UTF-8

var_dump(preg_match('/./u', $bad));
var_dump(preg_last_error() === PREG_BAD_UTF8_ERROR);
var_dump(preg_last_error());

A guarded match helper

The helper turns a silent false into an exception. The first call prints bool(true); the second prints the error message with the numeric PCRE code, so the failure cannot be mistaken for 'no match'.

Example · php
<?php
function safeMatch(string $pattern, string $subject): bool {
    $r = preg_match($pattern, $subject);
    if ($r === false || preg_last_error() !== PREG_NO_ERROR) {
        throw new RuntimeException('regex failed, code ' . preg_last_error());
    }
    return (bool) $r;
}

var_dump(safeMatch('/^\d+$/', '12345'));
try {
    safeMatch('/(?:\D+|<\d+>)*[!?]/', 'foobar foobar foobar');
} catch (RuntimeException $e) {
    echo $e->getMessage(), "\n";
}

Discussion

  • Be the first to comment on this lesson.