Skip to content

Add preg_last_error_msg() function #5185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions ext/pcre/php_pcre.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,6 @@ struct _pcre_cache_entry {
uint32_t refcount;
};

enum {
PHP_PCRE_NO_ERROR = 0,
PHP_PCRE_INTERNAL_ERROR,
PHP_PCRE_BACKTRACK_LIMIT_ERROR,
PHP_PCRE_RECURSION_LIMIT_ERROR,
PHP_PCRE_BAD_UTF8_ERROR,
PHP_PCRE_BAD_UTF8_OFFSET_ERROR,
PHP_PCRE_JIT_STACKLIMIT_ERROR
};


PHPAPI ZEND_DECLARE_MODULE_GLOBALS(pcre)

#ifdef HAVE_PCRE_JIT_SUPPORT
Expand Down Expand Up @@ -138,6 +127,33 @@ static void pcre_handle_exec_error(int pcre_code) /* {{{ */
}
/* }}} */

static const char *php_pcre_get_error_msg(php_pcre_error_code error_code) /* {{{ */
{
switch (error_code) {
case PHP_PCRE_NO_ERROR:
return "No error";
case PHP_PCRE_INTERNAL_ERROR:
return "Internal error";
case PHP_PCRE_BAD_UTF8_ERROR:
return "Malformed UTF-8 characters, possibly incorrectly encoded";
case PHP_PCRE_BAD_UTF8_OFFSET_ERROR:
return "The offset did not correspond to the beginning of a valid UTF-8 code point";
case PHP_PCRE_BACKTRACK_LIMIT_ERROR:
return "Backtrack limit exhausted";
case PHP_PCRE_RECURSION_LIMIT_ERROR:
return "Recursion limit exhausted";

#ifdef HAVE_PCRE_JIT_SUPPORT
case PHP_PCRE_JIT_STACKLIMIT_ERROR:
return "JIT stack limit exhausted";
#endif

default:
return "Unknown error";
}
}
/* }}} */

static void php_free_pcre_cache(zval *data) /* {{{ */
{
pcre_cache_entry *pce = (pcre_cache_entry *) Z_PTR_P(data);
Expand Down Expand Up @@ -2957,6 +2973,16 @@ static PHP_FUNCTION(preg_last_error)
}
/* }}} */

/* {{{ proto string preg_last_error_msg()
Returns the error message of the last regexp execution. */
static PHP_FUNCTION(preg_last_error_msg)
{
ZEND_PARSE_PARAMETERS_NONE();

RETURN_STRING(php_pcre_get_error_msg(PCRE_G(error_code)));
}
/* }}} */

/* {{{ module definition structures */

static const zend_function_entry pcre_functions[] = {
Expand All @@ -2970,6 +2996,7 @@ static const zend_function_entry pcre_functions[] = {
PHP_FE(preg_quote, arginfo_preg_quote)
PHP_FE(preg_grep, arginfo_preg_grep)
PHP_FE(preg_last_error, arginfo_preg_last_error)
PHP_FE(preg_last_error_msg, arginfo_preg_last_error_msg)
PHP_FE_END
};

Expand Down
12 changes: 11 additions & 1 deletion ext/pcre/php_pcre.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ extern zend_module_entry pcre_module_entry;

typedef struct _pcre_cache_entry pcre_cache_entry;

typedef enum {
PHP_PCRE_NO_ERROR = 0,
PHP_PCRE_INTERNAL_ERROR,
PHP_PCRE_BACKTRACK_LIMIT_ERROR,
PHP_PCRE_RECURSION_LIMIT_ERROR,
PHP_PCRE_BAD_UTF8_ERROR,
PHP_PCRE_BAD_UTF8_OFFSET_ERROR,
PHP_PCRE_JIT_STACKLIMIT_ERROR
} php_pcre_error_code;

PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(zend_string *regex);
PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, int locale_aware);

Expand Down Expand Up @@ -70,7 +80,7 @@ ZEND_BEGIN_MODULE_GLOBALS(pcre)
zend_bool jit;
#endif
zend_bool per_request_cache;
int error_code;
php_pcre_error_code error_code;
/* Used for unmatched subpatterns in OFFSET_CAPTURE mode */
zval unmatched_null_pair;
zval unmatched_empty_pair;
Expand Down
2 changes: 2 additions & 0 deletions ext/pcre/php_pcre.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ function preg_quote(string $str, ?string $delim_char = null): string {}
function preg_grep(string $regex, array $input, int $flags = 0): array|false {}

function preg_last_error(): int {}

function preg_last_error_msg(): string {}
3 changes: 3 additions & 0 deletions ext/pcre/php_pcre_arginfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_preg_last_error, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_preg_last_error_msg, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()
15 changes: 15 additions & 0 deletions ext/pcre/tests/errors01.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Test preg_split() function : error conditions - Recursion limit exhausted
--INI--
pcre.recursion_limit=1
--FILE--
<?php

var_dump(preg_last_error_msg() === 'No error');
preg_split('/(\d*)/', 'ab2c3u');
var_dump(preg_last_error_msg() === 'Recursion limit exhausted');

?>
--EXPECT--
bool(true)
bool(true)
12 changes: 12 additions & 0 deletions ext/pcre/tests/errors02.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Test preg_split() function : error conditions - Malformed UTF-8
--FILE--
<?php

var_dump(preg_split('/a/u', "a\xff"));
var_dump(preg_last_error_msg() === 'Malformed UTF-8 characters, possibly incorrectly encoded');

?>
--EXPECT--
bool(false)
bool(true)
13 changes: 13 additions & 0 deletions ext/pcre/tests/errors03.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Test preg_match() function : error conditions - Internal error
--FILE--
<?php

var_dump(preg_match('/', 'Hello world'));
var_dump(preg_last_error_msg() === 'Internal error');

?>
--EXPECTF--
Warning: preg_match(): No ending delimiter '/' found in %s on line %d
bool(false)
bool(true)
26 changes: 26 additions & 0 deletions ext/pcre/tests/errors04.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Test preg_match_all() function : error conditions - Backtracking limit
--SKIPIF--
<?php
if (@preg_match_all('/\p{N}/', '0123456789', $dummy) === false) {
die("skip no support for \p support PCRE library");
}
?>
--INI--
pcre.backtrack_limit=2
pcre.jit=0
--FILE--
<?php

var_dump(preg_match_all('/.*\p{N}/', '0123456789', $dummy));
var_dump(preg_last_error_msg() === 'Backtrack limit exhausted');

var_dump(preg_match_all('/\p{Nd}/', '0123456789', $dummy));
var_dump(preg_last_error_msg() === 'No error');

?>
--EXPECT--
bool(false)
bool(true)
int(10)
bool(true)
18 changes: 18 additions & 0 deletions ext/pcre/tests/errors05.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Test preg_match() function : error conditions - jit stacklimit exhausted
--SKIPIF--
<?php
if (ini_get('pcre.jit') === false) {
die("skip no jit built");
}
?>
--INI--
pcre.jit=1
--FILE--
<?php
var_dump(preg_match('/^(foo)+$/', str_repeat('foo', 1024*8192)));
var_dump(preg_last_error_msg() === 'JIT stack limit exhausted');
?>
--EXPECT--
bool(false)
bool(true)
11 changes: 11 additions & 0 deletions ext/pcre/tests/errors06.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test preg_match() function : error conditions - Malformed UTF-8 offset
--FILE--
<?php
preg_match('/a/u', "\xE3\x82\xA2", $m, 0, 1);
var_dump(preg_last_error() === PREG_BAD_UTF8_OFFSET_ERROR);
var_dump(preg_last_error_msg() === 'The offset did not correspond to the beginning of a valid UTF-8 code point');
?>
--EXPECT--
bool(true)
bool(true)
6 changes: 6 additions & 0 deletions sapi/cli/tests/006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ string(%d) "Extension [ <persistent> extension #%d pcre version %s ] {
}
- Return [ int ]
}
Function [ <internal:pcre> function preg_last_error_msg ] {

- Parameters [0] {
}
- Return [ string ]
}
}
}

Expand Down