From 64cfc82f0ea20a08911ce9dc8aff4ba339ff4098 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 30 Jul 2026 10:13:53 +0200 Subject: [PATCH 1/4] spprintf: introduce %p extensions to replace custom specifiers --- Zend/zend_compile.c | 4 ++-- main/spprintf.c | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9c80cccb7cb9..5958f2323213 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8583,8 +8583,8 @@ static void zend_compile_closure_binding(znode *closure, zend_op_array *op_array value = zend_hash_add(op_array->static_variables, var_name, &EG(uninitialized_zval)); if (!value) { - zend_error_noreturn_unchecked(E_COMPILE_ERROR, - "Cannot use variable $%S twice", var_name); + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use variable $%pS twice", var_name); } CG(zend_lineno) = zend_ast_get_lineno(var_name_ast); diff --git a/main/spprintf.c b/main/spprintf.c index 6553853d8104..be97cf5c2865 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -362,6 +362,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ break; } case 'S': { +format_zend_string:; zend_string *str = va_arg(ap, zend_string*); s_len = ZSTR_LEN(str); s = ZSTR_VAL(str); @@ -665,6 +666,13 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ * we print "%p" to indicate that we don't handle "%p". */ case 'p': + /* %p{Letter} extensions */ + switch (*(fmt+1)) { + case 'S': + fmt++; + goto format_zend_string; + } + /* Normal %p */ if (sizeof(char *) <= sizeof(uint64_t)) { ui_num = (uint64_t)((size_t) va_arg(ap, char *)); s = ap_php_conv_p2(ui_num, 4, 'x', From d7796bc88138254f6f1cc44683feb4ac1b1ae589 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 30 Jul 2026 18:14:32 +0200 Subject: [PATCH 2/4] ext/sqlite3: The 'p' length modifier is unsupported since 8.1 --- ext/sqlite3/sqlite3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index b81a0677a6c2..57271a629423 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -1660,7 +1660,7 @@ static int php_sqlite3_bind_params(php_sqlite3_stmt *stmt_obj) /* {{{ */ break; default: - php_sqlite3_error(stmt_obj->db_obj, 0, "Unknown parameter type: %pd for parameter %pd", param->type, param->param_number); + php_sqlite3_error(stmt_obj->db_obj, 0, "Unknown parameter type: " ZEND_LONG_FMT " for parameter " ZEND_LONG_FMT, param->type, param->param_number); return FAILURE; } } ZEND_HASH_FOREACH_END(); From d8363f7d748b7524553452d6e2b274500796b54e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 30 Jul 2026 18:15:02 +0200 Subject: [PATCH 3/4] Support %pp and trigger an error for unsupported %p specifiers --- main/spprintf.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/main/spprintf.c b/main/spprintf.c index be97cf5c2865..8d6b80258a72 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -666,11 +666,22 @@ format_zend_string:; * we print "%p" to indicate that we don't handle "%p". */ case 'p': - /* %p{Letter} extensions */ + /* %p[alnum]+ extensions */ switch (*(fmt+1)) { case 'S': + /* zend_string* */ fmt++; goto format_zend_string; + case 'p': + /* pointer */ + fmt++; + break; + default: + if (isalnum(*(fmt+1))) { + zend_error_noreturn(E_CORE_ERROR, + "Invalid printf specifier \"p%c\"", *(fmt+1)); + } + break; } /* Normal %p */ if (sizeof(char *) <= sizeof(uint64_t)) { From a57ff74348c4720d4e0911e9e2c8951da353c03e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 30 Jul 2026 18:23:37 +0200 Subject: [PATCH 4/4] UPGRADING.INTERNALS --- UPGRADING.INTERNALS | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 8b4b5912317a..7bdbe08ce567 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -14,6 +14,19 @@ PHP 8.6 INTERNALS UPGRADE NOTES 1. Internal API changes ======================== +- Breaking changes: + . String formatting functions now support the custom conversion specifiers + 'pS' (zend_string*) and 'pp' (same as 'p'). Following the 'p' specifier with + an alpha-numeric character other than 'S' or 'p' is now an error. + + Examples: + + zend_string *str; + zend_spprintf("%pS", str); // valid, same as "%S" + zend_spprintf("%pp", str); // valid, same as "%p" + zend_spprintf("%pA", str); // invalid + zend_spprintf("%ppA", str); // valid, same as zend_spprintf("%p%c", str, 'A') + - Removed: . The misnamed ZVAL_IS_NULL() has been removed. Use Z_ISNULL() instead. . The zval_is_true() alias of zend_is_true() has been removed. Call