preg_last_error
Returns the error code from the most recent PCRE regex execution.
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
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.
<?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 itCatching 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.
<?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'.
<?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