6. New Functions
========================================
+- PCRE:
+ . Added preg_last_error_msg(), which returns a human-readable message for
+ the last PCRE error. It complements preg_last_error(), which returns an
+ integer enum instead.
+
- SQLite3:
. Add SQLite3::setAuthorizer() and respective class constants to set a
userland callback that will be used to authorize or not an action on the
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
}
/* }}} */
+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);
}
/* }}} */
+/* {{{ 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[] = {
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
};
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);
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;
function preg_grep(string $regex, array $input, int $flags = 0): array|false {}
function preg_last_error(): int {}
+
+function preg_last_error_msg(): string {}
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()
--- /dev/null
+--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)
--- /dev/null
+--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)
--- /dev/null
+--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)
--- /dev/null
+--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)
--- /dev/null
+--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)
--- /dev/null
+--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)
}
- Return [ int ]
}
+ Function [ <internal:pcre> function preg_last_error_msg ] {
+
+ - Parameters [0] {
+ }
+ - Return [ string ]
+ }
}
}