preg_last_error()
Learn how PHP's preg_last_error() reports the error code from the last PCRE call, with error constants, preg_last_error_msg(), and examples.
Introduction
Regular expressions are a powerful tool for manipulating and searching strings in PHP. Occasionally, regex operations fail due to invalid patterns or engine limits. The preg_last_error() function helps identify these failures by returning the error code from the last PCRE function call.
Syntax
preg_last_error(): intThe function takes no arguments. It returns an integer constant describing what went wrong during the last PCRE function call — such as preg_match(), preg_match_all(), preg_replace(), or preg_split(). If the last call succeeded, it returns PREG_NO_ERROR (0).
Why you need it
PCRE functions are unusual: when they fail, they do not throw an exception. Instead, preg_match() returns false (not 0 — that means "no match"), and preg_replace() returns null. These return values tell you that something failed, but not why. preg_last_error() fills the gap by reporting the underlying reason.
That is why you must compare with === rather than a loose ==: 0 (no match) and false (error) both look "falsy", but they mean very different things.
Error constants
preg_last_error() returns one of these integer constants. The numeric values are shown for reference, but you should always compare against the named constant.
| Constant | Value | Meaning |
|---|---|---|
PREG_NO_ERROR | 0 | No error — the last operation succeeded. |
PREG_INTERNAL_ERROR | 1 | An internal PCRE error (often a malformed pattern). |
PREG_BACKTRACK_LIMIT_ERROR | 2 | The pattern hit the pcre.backtrack_limit. |
PREG_RECURSION_LIMIT_ERROR | 3 | The pattern hit the pcre.recursion_limit. |
PREG_BAD_UTF8_ERROR | 4 | The subject is not valid UTF-8 (with the u modifier). |
PREG_BAD_UTF8_OFFSET_ERROR | 5 | The offset did not match the start of a valid UTF-8 code point. |
PREG_JIT_STACKLIMIT_ERROR | 6 | The pattern exhausted the JIT stack limit. |
Since PHP 8.0, preg_last_error_msg() returns the same information as a human-readable string, which is handy for logging.
Triggering and reading an error
A common real-world failure is catastrophic backtracking, where a poorly written pattern forces the engine to try an enormous number of paths and trips the backtrack limit. The example below lowers pcre.backtrack_limit so the failure is reproducible, then inspects the result.
<?php
// Lower the limit so the failure is easy to reproduce.
ini_set('pcre.backtrack_limit', '100');
$pattern = '/(\w+)*$/'; // nested quantifier — backtracks heavily
$string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaa!';
$result = preg_match($pattern, $string, $matches);
if ($result === false) {
echo 'preg_match() failed!' . PHP_EOL;
echo 'Error code: ' . preg_last_error() . PHP_EOL;
echo 'Error message: ' . preg_last_error_msg() . PHP_EOL;
if (preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR) {
echo 'The pattern was too expensive to evaluate.' . PHP_EOL;
}
} elseif ($result === 1) {
echo 'Match found.' . PHP_EOL;
} else {
echo 'No match.' . PHP_EOL;
}Output:
preg_match() failed!
Error code: 2
Error message: Backtrack limit exhausted
The pattern was too expensive to evaluate.The error code 2 is PREG_BACKTRACK_LIMIT_ERROR. Crucially, the if ($result === false) check is what tells us a real error occurred — a 0 here would simply mean the pattern did not match.
Detecting invalid UTF-8 input
When you use the u (unicode) modifier on a string that is not valid UTF-8, PCRE bails out and sets PREG_BAD_UTF8_ERROR:
<?php
$result = preg_match('/./u', "\x80"); // \x80 is not a valid UTF-8 sequence
if ($result === false && preg_last_error() === PREG_BAD_UTF8_ERROR) {
echo 'Invalid UTF-8 input: ' . preg_last_error_msg();
}
// Invalid UTF-8 input: Malformed UTF-8 characters, possibly incorrectly encodedThis is a frequent source of silent bugs when processing user-submitted data, so guarding against it is good practice.
Common gotchas
- Check it immediately.
preg_last_error()reflects only the most recent PCRE call. Any later regex call overwrites it, so read it right after the operation you care about. falsevs0. Always use===.preg_match()returns0for "no match" andfalsefor an error — they are not interchangeable.preg_replace()failures. Whenpreg_replace()returnsnull, it is also worth callingpreg_last_error()to learn why.
Conclusion
preg_last_error() turns an opaque false return into an actionable reason, making it essential for debugging regular expressions in PHP. By comparing return values with === and inspecting the error code (or preg_last_error_msg()), you can cleanly separate engine failures from simple non-matches. For the functions whose failures it reports, see preg_match(), preg_replace(), and preg_split().