Signature
preg_last_error(): intPurpose
preg_last_error() tells you whether the most recent PCRE function call finished cleanly or hit a runtime problem. The preg_* functions usually signal trouble by returning false or null, and those values do not say what went wrong. This function supplies the reason as an integer code that you compare with the PREG_*_ERROR constants.
It is most useful when you process text you do not control, such as user input that may contain invalid UTF-8, or patterns that may backtrack heavily.
Signature
Parameters and defaults
The function takes no parameters, so there is nothing to configure. It reports on the last PCRE execution only. Call it straight after the preg_* call you care about, before any other regex function runs.
Return value and failure cases
The function always returns an int. It does not throw and does not emit warnings. The possible values are these constants:
PREG_NO_ERROR: the last call finished without a PCRE error.PREG_INTERNAL_ERROR: an internal PCRE error occurred.PREG_BACKTRACK_LIMIT_ERROR: the backtracking limit was exhausted (seepcre.backtrack_limit).PREG_RECURSION_LIMIT_ERROR: the recursion limit was exhausted (seepcre.recursion_limit).PREG_BAD_UTF8_ERROR: the subject is not valid UTF-8 while theumodifier is in use.PREG_BAD_UTF8_OFFSET_ERROR: the supplied offset does not fall at the start of a UTF-8 character.PREG_JIT_STACKLIMIT_ERROR: the JIT stack ran out of space.
Compare against the named constants, not literal numbers. For a readable message, preg_last_error_msg() returns text for the same state.
Example 1: a clean call reports no error
After a normal match, the code is PREG_NO_ERROR, which is 0.
preg_match('/a/', 'abc');
echo json_encode([preg_last_error(), preg_last_error_msg()], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[0,"No error"]
Example 2: invalid UTF-8 in the subject
With the u modifier, PCRE validates the subject. The byte \xff can never appear in valid UTF-8, so preg_match() returns false and the error code explains why.
$result = preg_match('/./u', "\xff");
echo json_encode([
$result,
preg_last_error() === PREG_BAD_UTF8_ERROR,
preg_last_error_msg(),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[false,true,"Malformed UTF-8 characters, possibly incorrectly encoded"]
Example 3: a wrapper that separates errors from non-matches
A plain "no match" is not an error. The helper below throws only when the call failed, and otherwise returns a boolean.
function matchOrFail(string $pattern, string $subject): bool
{
$result = preg_match($pattern, $subject);
if ($result === false || preg_last_error() !== PREG_NO_ERROR) {
throw new RuntimeException(preg_last_error_msg());
}
return $result === 1;
}
$results = [];
foreach (['abc7', 'abc', "\xff"] as $subject) {
try {
$results[] = matchOrFail('/\d/u', $subject);
} catch (RuntimeException $e) {
$results[] = $e->getMessage();
}
}
echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[true,false,"Malformed UTF-8 characters, possibly incorrectly encoded"]
Edge cases
- A subject with no match gives
0frompreg_match()whilepreg_last_error()stays atPREG_NO_ERROR. Only afalseresult (ornullfrom some functions such aspreg_replace()) points at a runtime error. - The value describes the last regex call only. If you run another
preg_*function in between, even for logging or cleanup, you lose the information from the earlier call. - Limit-related errors depend on configuration such as
pcre.backtrack_limit,pcre.recursion_limitand whether JIT is active. The same pattern may therefore behave differently on different setups, so do not build logic that assumes a specific limit error for a given input.
Common mistakes
- Treating
0frompreg_match()as a failure. It only means the pattern did not match. Check forfalsefirst, then consultpreg_last_error(). - Using loose truthiness.
if (!preg_match(...))merges a non-match with an error. Compare withfalseusing===. - Checking too late. Any intervening
preg_*call replaces the stored state, so read it right away. - Hard-coding numbers. Use
PREG_BAD_UTF8_ERRORand the other constants, which are clearer and safer. - Expecting an exception. PCRE runtime errors do not throw by themselves. Add your own check, like the wrapper above, if you want exceptions.
Related functions
preg_last_error_msg()returns the matching human-readable message.preg_match(),preg_match_all(),preg_replace(),preg_replace_callback()andpreg_split()are the calls whose outcome it reports.
Recap
preg_last_error() takes no arguments and returns an int code describing the last PCRE call. Pair it with a strict === false check on the regex function's result, compare against the PREG_* constants, and call it immediately after the operation. Use preg_last_error_msg() when you need text for logs or exceptions.
Tested examples
preg_match('/a/', 'abc');
echo json_encode([preg_last_error(), preg_last_error_msg()], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[0,"No error"]
$result = preg_match('/./u', "\xff");
echo json_encode([
$result,
preg_last_error() === PREG_BAD_UTF8_ERROR,
preg_last_error_msg(),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[false,true,"Malformed UTF-8 characters, possibly incorrectly encoded"]
function matchOrFail(string $pattern, string $subject): bool
{
$result = preg_match($pattern, $subject);
if ($result === false || preg_last_error() !== PREG_NO_ERROR) {
throw new RuntimeException(preg_last_error_msg());
}
return $result === 1;
}
$results = [];
foreach (['abc7', 'abc', "\xff"] as $subject) {
try {
$results[] = matchOrFail('/\d/u', $subject);
} catch (RuntimeException $e) {
$results[] = $e->getMessage();
}
}
echo json_encode($results, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[true,false,"Malformed UTF-8 characters, possibly incorrectly encoded"]