From 5668b16ad26d0751648fc185f35329804d12b663 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 29 Sep 2025 14:43:51 +0100 Subject: [PATCH 01/14] Revert "Pdo\Pgsql: Fix getColumnMeta() for GH-15287 Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0)" This reverts commit 898235127b362d7103fdae8142c05908b7191f60. --- NEWS | 4 --- ext/pdo_pgsql/pgsql_driver.c | 58 +++++++------------------------ ext/pdo_pgsql/pgsql_statement.c | 50 ++++++++------------------ ext/pdo_pgsql/php_pdo_pgsql_int.h | 8 ----- ext/pdo_pgsql/tests/gh15287.phpt | 15 -------- 5 files changed, 26 insertions(+), 109 deletions(-) diff --git a/NEWS b/NEWS index 7e9010e96cef8..2213ff10b1db7 100644 --- a/NEWS +++ b/NEWS @@ -7,8 +7,4 @@ PHP NEWS with a given skeleton, locale, collapse type and identity fallback. (BogdanUngureanu) -- PDO_PGSQL: - . Fixed Pdo\Pgsql::getColumnMeta() when Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0). - (outtersg) - <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index c8aea7f0594e2..be865c1f86838 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -36,7 +36,6 @@ #include "pgsql_driver_arginfo.h" static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh); -void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode); static char * _pdo_pgsql_trim_message(const char *message, int persistent) { @@ -110,37 +109,6 @@ int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char * } /* }}} */ -static zend_always_inline void pgsql_finish_running_stmt(pdo_pgsql_db_handle *H) -{ - if (H->running_stmt) { - pgsql_stmt_finish(H->running_stmt, 0); - } -} - -static zend_always_inline void pgsql_discard_running_stmt(pdo_pgsql_db_handle *H) -{ - if (H->running_stmt) { - pgsql_stmt_finish(H->running_stmt, FIN_DISCARD); - } - - PGresult *pgsql_result; - bool first = true; - while ((pgsql_result = PQgetResult(H->server))) { - /* We should not arrive here, where libpq has a result to deliver without us - * having registered a running statement: - * every result discarding should go through the unified pgsql_stmt_finish, - * but maybe there still is an internal query that we omitted to adapt. - * So instead of asserting let's just emit an informational notice, - * and consume anyway (results consumption is handle-wise, so we have no formal - * need for the statement). */ - if (first) { - php_error_docref(NULL, E_NOTICE, "Internal error: unable to link a libpq result to consume, to its origin statement"); - first = false; - } - PQclear(pgsql_result); - } -} - static void _pdo_pgsql_notice(void *context, const char *message) /* {{{ */ { pdo_dbh_t * dbh = (pdo_dbh_t *)context; @@ -290,10 +258,6 @@ static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ PQfinish(H->server); H->server = NULL; } - if (H->cached_table_name) { - efree(H->cached_table_name); - H->cached_table_name = NULL; - } if (H->einfo.errmsg) { pefree(H->einfo.errmsg, dbh->is_persistent); H->einfo.errmsg = NULL; @@ -387,7 +351,6 @@ static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) bool in_trans = pgsql_handle_in_transaction(dbh); - pgsql_finish_running_stmt(H); if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) { /* fatal error */ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); @@ -463,7 +426,6 @@ static zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string * PGresult *res; ExecStatusType status; - pgsql_finish_running_stmt(H); if (name == NULL) { res = PQexec(H->server, "SELECT LASTVAL()"); } else { @@ -627,7 +589,6 @@ static bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) PGresult *res; bool ret = true; - pgsql_finish_running_stmt(H); res = PQexec(H->server, cmd); if (PQresultStatus(res) != PGRES_COMMAND_OK) { @@ -735,8 +696,9 @@ void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) /* Obtain db Handle */ H = (pdo_pgsql_db_handle *)dbh->driver_data; - pgsql_discard_running_stmt(H); - + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } pgsql_result = PQexec(H->server, query); efree(query); @@ -858,8 +820,9 @@ void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS) H = (pdo_pgsql_db_handle *)dbh->driver_data; - pgsql_discard_running_stmt(H); - + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } pgsql_result = PQexec(H->server, query); efree(query); @@ -953,7 +916,9 @@ void pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS) RETURN_FALSE; } - pgsql_discard_running_stmt(H); + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ if (pg_fields) { @@ -1042,7 +1007,9 @@ void pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS) H = (pdo_pgsql_db_handle *)dbh->driver_data; - pgsql_discard_running_stmt(H); + while ((pgsql_result = PQgetResult(H->server))) { + PQclear(pgsql_result); + } /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */ if (pg_fields) { @@ -1494,7 +1461,6 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ H->attached = 1; H->pgoid = -1; - H->cached_table_oid = InvalidOid; dbh->methods = &pgsql_methods; dbh->alloc_own_columns = 1; diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 4335e374c5ab2..426a878d8eff7 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -56,14 +56,14 @@ #define FLOAT8LABEL "float8" #define FLOAT8OID 701 +#define FIN_DISCARD 0x1 +#define FIN_CLOSE 0x2 +#define FIN_ABORT 0x4 -void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode) -{ - if (!S) { - return; - } +static void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode) +{ pdo_pgsql_db_handle *H = S->H; if (S->is_running_unbuffered && S->result && (fin_mode & FIN_ABORT)) { @@ -113,10 +113,9 @@ void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode) } S->is_prepared = false; - } - - if (H->running_stmt == S && (fin_mode & (FIN_CLOSE|FIN_ABORT))) { - H->running_stmt = NULL; + if (H->running_stmt == S) { + H->running_stmt = NULL; + } } } @@ -189,8 +188,9 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt) * and returns a PGRES_FATAL_ERROR when PQgetResult gets called for stmt 2 if DEALLOCATE * was called for stmt 1 inbetween * (maybe it will change with pipeline mode in libpq 14?) */ - if (H->running_stmt && H->running_stmt->is_unbuffered) { + if (S->is_unbuffered && H->running_stmt) { pgsql_stmt_finish(H->running_stmt, FIN_CLOSE); + H->running_stmt = NULL; } /* ensure that we free any previous unfetched results */ pgsql_stmt_finish(S, 0); @@ -702,29 +702,12 @@ static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pd return 1; } -static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, pdo_pgsql_db_handle *H) +static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) { - PGconn *conn = H->server; char *table_name = NULL; PGresult *tmp_res; char *querystr = NULL; - if (oid == H->cached_table_oid) { - return H->cached_table_name; - } - - if (H->running_stmt && H->running_stmt->is_unbuffered) { - /* in single-row mode, libpq forbids passing a new query - * while we're still flushing the current one's result */ - return NULL; - } - - if (H->cached_table_name) { - efree(H->cached_table_name); - H->cached_table_name = NULL; - H->cached_table_oid = InvalidOid; - } - spprintf(&querystr, 0, "SELECT RELNAME FROM PG_CLASS WHERE OID=%d", oid); if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) { @@ -741,8 +724,6 @@ static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, pdo_p return 0; } - H->cached_table_oid = oid; - H->cached_table_name = estrdup(table_name); table_name = estrdup(table_name); PQclear(tmp_res); @@ -771,9 +752,10 @@ static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *r table_oid = PQftable(S->result, colno); add_assoc_long(return_value, "pgsql:table_oid", table_oid); - table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H); + table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server); if (table_name) { - add_assoc_string(return_value, "table", S->H->cached_table_name); + add_assoc_string(return_value, "table", table_name); + efree(table_name); } switch (S->cols[colno].pgsql_type) { @@ -812,10 +794,6 @@ static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *r break; default: /* Fetch metadata from Postgres system catalogue */ - if (S->H->running_stmt && S->H->running_stmt->is_unbuffered) { - /* libpq forbids calling a query while we're still reading the preceding one's */ - break; - } spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type); res = PQexec(S->H->server, q); efree(q); diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index a6718b86a49ca..881b4e7046504 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -43,8 +43,6 @@ typedef struct { unsigned _reserved:31; pdo_pgsql_error_info einfo; Oid pgoid; - Oid cached_table_oid; - char *cached_table_name; unsigned int stmt_counter; bool emulate_prepares; bool disable_prepares; @@ -92,12 +90,6 @@ extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const extern const struct pdo_stmt_methods pgsql_stmt_methods; -#define FIN_DISCARD 0x1 -#define FIN_CLOSE 0x2 -#define FIN_ABORT 0x4 - -extern void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode); - #define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE) enum { diff --git a/ext/pdo_pgsql/tests/gh15287.phpt b/ext/pdo_pgsql/tests/gh15287.phpt index 44cf9fea1d402..821c22a8f1c5a 100644 --- a/ext/pdo_pgsql/tests/gh15287.phpt +++ b/ext/pdo_pgsql/tests/gh15287.phpt @@ -132,17 +132,6 @@ $res = []; while (($re = $stmt->fetch())) $res[] = $re; display($res); $stmt->execute([ 0 ]); $res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res); display($pdo->query("select * from t2")->fetchAll()); - -// Metadata calls the server for some operations (notably table oid-to-name conversion). -// This will break libpq (that forbids a second PQexec before we consumed the first one). -// Instead of either letting libpq return an error, or blindly forbid this call, we expect -// being transparently provided at least attributes which do not require a server roundtrip. -// And good news: column name is one of those "local" attributes. -echo "=== meta ===\n"; -$stmt = $pdo->query("select * from t limit 2"); -echo "Starting with column " . $stmt->getColumnMeta(0)['name'] . ":\n"; -display($stmt->fetchAll()); - ?> --EXPECTF-- === non regression === @@ -192,7 +181,3 @@ multiple calls to the same prepared statement, some interrupted before having re 0 1 678 ok -=== meta === -Starting with column n: -0 original -1 non original From 584ae5eec2649391b8c3181e79b9d15c25076caa Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 25 Sep 2025 11:57:35 +0100 Subject: [PATCH 02/14] Zend/zend_execute_API.c: add const qualifiers --- Zend/zend_execute.h | 8 +++---- Zend/zend_execute_API.c | 46 ++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index c08adf2a41b90..920c702785ca4 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -48,11 +48,11 @@ ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_ ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value); ZEND_API void execute_ex(zend_execute_data *execute_data); ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value); -ZEND_API bool zend_is_valid_class_name(zend_string *name); +ZEND_API bool zend_is_valid_class_name(const zend_string *name); ZEND_API zend_class_entry *zend_lookup_class(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags); -ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex); -ZEND_API zend_object *zend_get_this_object(zend_execute_data *ex); +ZEND_API zend_class_entry *zend_get_called_scope(const zend_execute_data *ex); +ZEND_API zend_object *zend_get_this_object(const zend_execute_data *ex); ZEND_API zend_result zend_eval_string(const char *str, zval *retval_ptr, const char *string_name); ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *retval_ptr, const char *string_name); ZEND_API zend_result zend_eval_string_ex(const char *str, zval *retval_ptr, const char *string_name, bool handle_exceptions); @@ -453,7 +453,7 @@ ZEND_API const char *get_active_class_name(const char **space); ZEND_API const char *get_active_function_name(void); ZEND_API const char *get_active_function_arg_name(uint32_t arg_num); ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t arg_num); -ZEND_API zend_function *zend_active_function_ex(zend_execute_data *execute_data); +ZEND_API zend_function *zend_active_function_ex(const zend_execute_data *execute_data); static zend_always_inline zend_function *zend_active_function(void) { diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 35154e8afdc3c..5809e5ad7a5f8 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -88,7 +88,7 @@ static void zend_handle_sigsegv(void) /* {{{ */ /* }}} */ #endif -static void zend_extension_activator(zend_extension *extension) /* {{{ */ +static void zend_extension_activator(const zend_extension *extension) /* {{{ */ { if (extension->activate) { extension->activate(); @@ -96,7 +96,7 @@ static void zend_extension_activator(zend_extension *extension) /* {{{ */ } /* }}} */ -static void zend_extension_deactivator(zend_extension *extension) /* {{{ */ +static void zend_extension_deactivator(const zend_extension *extension) /* {{{ */ { if (extension->deactivate) { extension->deactivate(); @@ -113,14 +113,14 @@ static int clean_non_persistent_constant_full(zval *zv) /* {{{ */ static int clean_non_persistent_function_full(zval *zv) /* {{{ */ { - zend_function *function = Z_PTR_P(zv); + const zend_function *function = Z_PTR_P(zv); return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; } /* }}} */ static int clean_non_persistent_class_full(zval *zv) /* {{{ */ { - zend_class_entry *ce = Z_PTR_P(zv); + const zend_class_entry *ce = Z_PTR_P(zv); return (ce->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; } /* }}} */ @@ -536,7 +536,7 @@ void shutdown_executor(void) /* {{{ */ /* return class name and "::" or "". */ ZEND_API const char *get_active_class_name(const char **space) /* {{{ */ { - zend_function *func; + const zend_function *func; if (!zend_is_executing()) { if (space) { @@ -551,7 +551,7 @@ ZEND_API const char *get_active_class_name(const char **space) /* {{{ */ case ZEND_USER_FUNCTION: case ZEND_INTERNAL_FUNCTION: { - zend_class_entry *ce = func->common.scope; + const zend_class_entry *ce = func->common.scope; if (space) { *space = ce ? "::" : ""; @@ -569,7 +569,7 @@ ZEND_API const char *get_active_class_name(const char **space) /* {{{ */ ZEND_API const char *get_active_function_name(void) /* {{{ */ { - zend_function *func; + const zend_function *func; if (!zend_is_executing()) { return NULL; @@ -579,7 +579,7 @@ ZEND_API const char *get_active_function_name(void) /* {{{ */ switch (func->type) { case ZEND_USER_FUNCTION: { - zend_string *function_name = func->common.function_name; + const zend_string *function_name = func->common.function_name; if (function_name) { return ZSTR_VAL(function_name); @@ -597,7 +597,7 @@ ZEND_API const char *get_active_function_name(void) /* {{{ */ } /* }}} */ -ZEND_API zend_function *zend_active_function_ex(zend_execute_data *execute_data) +ZEND_API zend_function *zend_active_function_ex(const zend_execute_data *execute_data) { zend_function *func = EX(func); @@ -636,7 +636,7 @@ ZEND_API const char *get_active_function_arg_name(uint32_t arg_num) /* {{{ */ return NULL; } - zend_function *func = zend_active_function(); + const zend_function *func = zend_active_function(); return get_function_arg_name(func, arg_num); } @@ -658,7 +658,7 @@ ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t a ZEND_API const char *zend_get_executed_filename(void) /* {{{ */ { - zend_string *filename = zend_get_executed_filename_ex(); + const zend_string *filename = zend_get_executed_filename_ex(); return filename != NULL ? ZSTR_VAL(filename) : "[no active file]"; } /* }}} */ @@ -670,7 +670,7 @@ ZEND_API zend_string *zend_get_executed_filename_ex(void) /* {{{ */ return filename_override; } - zend_execute_data *ex = EG(current_execute_data); + const zend_execute_data *ex = EG(current_execute_data); while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) { ex = ex->prev_execute_data; @@ -690,7 +690,7 @@ ZEND_API uint32_t zend_get_executed_lineno(void) /* {{{ */ return lineno_override; } - zend_execute_data *ex = EG(current_execute_data); + const zend_execute_data *ex = EG(current_execute_data); while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) { ex = ex->prev_execute_data; @@ -739,7 +739,7 @@ ZEND_API zend_result ZEND_FASTCALL zval_update_constant_with_ctx(zval *p, zend_c if (ast->kind == ZEND_AST_CONSTANT) { zend_string *name = zend_ast_get_constant_name(ast); - zval *zv = zend_get_constant_ex(name, scope, ast->attr); + const zval *zv = zend_get_constant_ex(name, scope, ast->attr); if (UNEXPECTED(zv == NULL)) { return FAILURE; } @@ -1158,7 +1158,7 @@ static const uint32_t valid_chars[8] = { 0xffffffff, }; -ZEND_API bool zend_is_valid_class_name(zend_string *name) { +ZEND_API bool zend_is_valid_class_name(const zend_string *name) { for (size_t i = 0; i < ZSTR_LEN(name); i++) { unsigned char c = ZSTR_VAL(name)[i]; if (!ZEND_BIT_TEST(valid_chars, c)) { @@ -1298,7 +1298,7 @@ ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ } /* }}} */ -ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex) /* {{{ */ +ZEND_API zend_class_entry *zend_get_called_scope(const zend_execute_data *ex) /* {{{ */ { while (ex) { if (Z_TYPE(ex->This) == IS_OBJECT) { @@ -1316,7 +1316,7 @@ ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex) /* {{{ * } /* }}} */ -ZEND_API zend_object *zend_get_this_object(zend_execute_data *ex) /* {{{ */ +ZEND_API zend_object *zend_get_this_object(const zend_execute_data *ex) /* {{{ */ { while (ex) { if (Z_TYPE(ex->This) == IS_OBJECT) { @@ -1689,7 +1689,7 @@ void zend_unset_timeout(void) /* {{{ */ } /* }}} */ -static ZEND_COLD void report_class_fetch_error(zend_string *class_name, uint32_t fetch_type) +static ZEND_COLD void report_class_fetch_error(const zend_string *class_name, uint32_t fetch_type) { if (fetch_type & ZEND_FETCH_CLASS_SILENT) { return; @@ -1858,7 +1858,7 @@ ZEND_API zend_array *zend_rebuild_symbol_table(void) /* {{{ */ ZEND_API void zend_attach_symbol_table(zend_execute_data *execute_data) /* {{{ */ { - zend_op_array *op_array = &execute_data->func->op_array; + const zend_op_array *op_array = &execute_data->func->op_array; HashTable *ht = execute_data->symbol_table; /* copy real values from symbol table into CV slots and create @@ -1873,7 +1873,7 @@ ZEND_API void zend_attach_symbol_table(zend_execute_data *execute_data) /* {{{ * if (zv) { if (Z_TYPE_P(zv) == IS_INDIRECT) { - zval *val = Z_INDIRECT_P(zv); + const zval *val = Z_INDIRECT_P(zv); ZVAL_COPY_VALUE(var, val); } else { @@ -1893,7 +1893,7 @@ ZEND_API void zend_attach_symbol_table(zend_execute_data *execute_data) /* {{{ * ZEND_API void zend_detach_symbol_table(zend_execute_data *execute_data) /* {{{ */ { - zend_op_array *op_array = &execute_data->func->op_array; + const zend_op_array *op_array = &execute_data->func->op_array; HashTable *ht = execute_data->symbol_table; /* copy real values from CV slots into symbol table */ @@ -1927,7 +1927,7 @@ ZEND_API zend_result zend_set_local_var(zend_string *name, zval *value, bool for if (execute_data) { if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_ulong h = zend_string_hash_val(name); - zend_op_array *op_array = &execute_data->func->op_array; + const zend_op_array *op_array = &execute_data->func->op_array; if (EXPECTED(op_array->last_var)) { zend_string **str = op_array->vars; @@ -1970,7 +1970,7 @@ ZEND_API zend_result zend_set_local_var_str(const char *name, size_t len, zval * if (execute_data) { if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_ulong h = zend_hash_func(name, len); - zend_op_array *op_array = &execute_data->func->op_array; + const zend_op_array *op_array = &execute_data->func->op_array; if (EXPECTED(op_array->last_var)) { zend_string **str = op_array->vars; zend_string **end = str + op_array->last_var; From 2f6afda40b0f95b38af152f1b339ab02cd07edcd Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 25 Sep 2025 11:57:49 +0100 Subject: [PATCH 03/14] Zend/zend_execute_API.c: reduce variable scope --- Zend/zend_execute_API.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 5809e5ad7a5f8..b51f1546d668d 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -270,9 +270,6 @@ void shutdown_destructors(void) /* {{{ */ /* Free values held by the executor. */ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) { - zend_string *key; - zval *zv; - EG(flags) |= EG_FLAGS_IN_RESOURCE_SHUTDOWN; zend_try { zend_close_rsrc_list(&EG(regular_list)); @@ -282,12 +279,15 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) EG(active) = 0; if (!fast_shutdown) { + zval *zv; + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); /* Constants may contain objects, destroy them before the object store. */ if (EG(full_tables_cleanup)) { zend_hash_reverse_apply(EG(zend_constants), clean_non_persistent_constant_full); } else { + zend_string *key; ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_VAL(EG(zend_constants), key, zv) { zend_constant *c = Z_PTR_P(zv); if (_idx == EG(persistent_constants_count)) { @@ -434,8 +434,6 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) void shutdown_executor(void) /* {{{ */ { - zend_string *key; - zval *zv; #if ZEND_DEBUG bool fast_shutdown = 0; #elif defined(__SANITIZE_ADDRESS__) @@ -477,6 +475,8 @@ void shutdown_executor(void) /* {{{ */ zend_hash_reverse_apply(EG(function_table), clean_non_persistent_function_full); zend_hash_reverse_apply(EG(class_table), clean_non_persistent_class_full); } else { + zend_string *key; + zval *zv; ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_VAL(EG(function_table), key, zv) { zend_function *func = Z_PTR_P(zv); if (_idx == EG(persistent_functions_count)) { @@ -808,7 +808,6 @@ zend_result _call_user_function_impl(zval *object, zval *function_name, zval *re zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /* {{{ */ { - uint32_t i; zend_execute_data *call; zend_fcall_info_cache fci_cache_local; zend_function *func; @@ -871,7 +870,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } } - for (i=0; iparam_count; i++) { + for (uint32_t i = 0; i < fci->param_count; i++) { zval *param = ZEND_CALL_ARG(call, i+1); zval *arg = &fci->params[i]; bool must_wrap = false; From ec314379734ba78842b1dd4d386dd2def707c0fd Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 25 Sep 2025 12:06:12 +0100 Subject: [PATCH 04/14] Zend/zend_execute_API.c: use uint32_t type instead of int --- Zend/zend_execute_API.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index b51f1546d668d..1fd0913076534 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -229,7 +229,7 @@ static void zend_unclean_zval_ptr_dtor(zval *zv) /* {{{ */ } /* }}} */ -static ZEND_COLD void zend_throw_or_error(int fetch_type, zend_class_entry *exception_ce, const char *format, ...) /* {{{ */ +static ZEND_COLD void zend_throw_or_error(uint32_t fetch_type, zend_class_entry *exception_ce, const char *format, ...) /* {{{ */ { va_list va; char *message = NULL; From d94846c3b4d6cfc2564b59aebcb9abe6b347ace6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:18:03 +0200 Subject: [PATCH 05/14] Fix GH-19988: zend_string_init with NULL pointer in simplexml (UB) Normally, simplexml cannot import document nodes, but xsl allows to circumvent this. A document does not have a name, so we return the empty string in that case. While we could add an explicit check, we might as well switch the macro to a form that would be more optimal anyway as many tag names can be single characters. The test was added in xsl because adding it in simplexml would break out-of-tree builds of simplexml. Closes GH-19990. --- NEWS | 4 ++++ ext/simplexml/simplexml.c | 2 +- ext/xsl/tests/gh19988.phpt | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 ext/xsl/tests/gh19988.phpt diff --git a/NEWS b/NEWS index fc3c0662b9328..249ef8f98ed8e 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,10 @@ PHP NEWS of the curl_copy_handle() function to clone a CurlHandle. (timwolla) . Fix curl build failure on macOS+curl 8.16. (nielsdos) +- SimpleXML: + . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). + (nielsdos) + - Soap: . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 11f497a6673ea..6eae5650340c5 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1661,7 +1661,7 @@ PHP_METHOD(SimpleXMLElement, getName) node = php_sxe_get_first_node(sxe, node); if (node) { namelen = xmlStrlen(node->name); - RETURN_STRINGL((char*)node->name, namelen); + RETURN_STRINGL_FAST((const char *) node->name, namelen); } else { RETURN_EMPTY_STRING(); } diff --git a/ext/xsl/tests/gh19988.phpt b/ext/xsl/tests/gh19988.phpt new file mode 100644 index 0000000000000..174af282f9c09 --- /dev/null +++ b/ext/xsl/tests/gh19988.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-19988 (zend_string_init with NULL pointer in simplexml (UB)) +--EXTENSIONS-- +simplexml +xsl +--CREDITS-- +YuanchengJiang +--FILE-- +load(__DIR__ . '/53965/collection.xsl'); +$processor->importStylesheet($dom); +$result = $processor->transformToDoc($sxe, SimpleXMLElement::class); +var_dump($result->getName()); +?> +--EXPECT-- +string(0) "" From 190f427198b2e789f33f8ba8a225a9140458786a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 29 Sep 2025 16:05:33 +0200 Subject: [PATCH 06/14] Fix NEWS order --- NEWS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 249ef8f98ed8e..3955fab9ed22d 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,11 @@ PHP NEWS . Fixed bug GH-19480 (error_log php.ini cannot be unset when open_basedir is configured). (nielsdos) +- Curl: + . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead + of the curl_copy_handle() function to clone a CurlHandle. (timwolla) + . Fix curl build failure on macOS+curl 8.16. (nielsdos) + - Date: . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. (nielsdos) @@ -22,11 +27,6 @@ PHP NEWS - DBA: . Fixed GH-19885 (dba_fetch() overflow on skip argument). (David Carlier) -- Curl: - . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead - of the curl_copy_handle() function to clone a CurlHandle. (timwolla) - . Fix curl build failure on macOS+curl 8.16. (nielsdos) - - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) From 8987c0b66120e8e3143796f58a64211ac8522baa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 12 Sep 2025 22:06:10 +0200 Subject: [PATCH 07/14] Fix uninitialized soap lang_en string on ZTS Replaces GH-19772. Closes GH-19772. Fixes GH-19773. Closes GH-19819. --- NEWS | 5 ++++- ext/soap/php_http.c | 24 ++++++++++++------------ ext/soap/php_packet_soap.c | 30 +++++++++++++++--------------- ext/soap/php_soap.h | 3 ++- ext/soap/soap.c | 16 +++++++++------- ext/soap/tests/bugs/bug38005.phpt | 2 +- ext/soap/tests/bugs/bug68996.phpt | 2 +- ext/soap/tests/soap12/T63.phpt | 2 +- 8 files changed, 45 insertions(+), 39 deletions(-) diff --git a/NEWS b/NEWS index 10d9064782291..a6749b8ed4040 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ PHP NEWS - URI: . Fixed Uri\WhatWg\Url::withPort() when an invalid value is passed. (timwolla) +- SOAP: + . Fixed bug GH-19773 (SIGSEGV due to uninitialized soap_globals->lang_en). + (nielsdos, KaseyJenkins) + 25 Sep 2025, PHP 8.5.0RC1 - Core: @@ -771,5 +775,4 @@ PHP NEWS opening HTTP URLs). (nielsdos) . Implemented GH-17668 (zlib streams should support locking). (nielsdos) - <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index ac5f0f0f9d1ed..7d8538b36c2db 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -460,7 +460,7 @@ int make_http_soap_request( if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -474,7 +474,7 @@ int make_http_soap_request( if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -487,7 +487,7 @@ int make_http_soap_request( if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL, soap_lang_en); PG(allow_url_fopen) = old_allow_url_fopen; smart_str_free(&soap_headers_z); efree(http_msg); @@ -540,7 +540,7 @@ int make_http_soap_request( if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL, soap_lang_en); PG(allow_url_fopen) = old_allow_url_fopen; smart_str_free(&soap_headers_z); efree(http_msg); @@ -911,14 +911,14 @@ int make_http_soap_request( php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); - add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; } smart_str_free(&soap_headers); } else { - add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -935,7 +935,7 @@ int make_http_soap_request( ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); - add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -1124,7 +1124,7 @@ int make_http_soap_request( php_stream_close(stream); zend_string_release_ex(http_headers, 0); ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); - add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL, soap_lang_en); if (http_msg) { efree(http_msg); } @@ -1190,7 +1190,7 @@ int make_http_soap_request( uri = new_uri; if (--redirect_max < 1) { - add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL, soap_lang_en); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -1326,7 +1326,7 @@ int make_http_soap_request( if (http_msg) { efree(http_msg); } - add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL, soap_lang_en); return FALSE; } zend_call_known_function(decompression_fn, NULL, NULL, &retval, 1, params, NULL); @@ -1338,7 +1338,7 @@ int make_http_soap_request( efree(content_encoding); zend_string_release_ex(http_headers, 0); zend_string_release_ex(http_body, 0); - add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL, soap_lang_en); if (http_msg) { efree(http_msg); } @@ -1372,7 +1372,7 @@ int make_http_soap_request( if (error) { zval_ptr_dtor(return_value); ZVAL_UNDEF(return_value); - add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL, soap_lang_en); efree(http_msg); return FALSE; } diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index 31ed094ef2fc7..1019949093f0e 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -40,11 +40,11 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio response = soap_xmlParseMemory(buffer, buffer_size); if (!response) { - add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL, soap_lang_en); return false; } if (xmlGetIntSubset(response) != NULL) { - add_soap_fault(this_ptr, "Client", "DTD are not supported by SOAP", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "DTD are not supported by SOAP", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } @@ -63,7 +63,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio envelope_ns = SOAP_1_2_ENV_NAMESPACE; soap_version = SOAP_1_2; } else { - add_soap_fault(this_ptr, "VersionMismatch", "Wrong Version", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "VersionMismatch", "Wrong Version", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } @@ -71,7 +71,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio trav = trav->next; } if (env == NULL) { - add_soap_fault(this_ptr, "Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } @@ -79,16 +79,16 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio attr = env->properties; while (attr != NULL) { if (attr->ns == NULL) { - add_soap_fault(this_ptr, "Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { - add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } @@ -120,7 +120,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio trav = trav->next; } if (body == NULL) { - add_soap_fault(this_ptr, "Client", "Body must be present in a SOAP envelope", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "Body must be present in a SOAP envelope", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } @@ -128,17 +128,17 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio while (attr != NULL) { if (attr->ns == NULL) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Body", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Body", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { - add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } @@ -146,7 +146,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio attr = attr->next; } if (trav != NULL && soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } @@ -155,16 +155,16 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio attr = head->properties; while (attr != NULL) { if (attr->ns == NULL) { - add_soap_fault(this_ptr, "Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Header", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Header", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { - add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, SOAP_GLOBAL(lang_en)); + add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; } diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index 48f8fb50591cb..aa3fb79e57095 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -171,9 +171,10 @@ ZEND_BEGIN_MODULE_GLOBALS(soap) HashTable wsdl_cache; int cur_uniq_ref; HashTable *ref_map; - zend_string *lang_en; ZEND_END_MODULE_GLOBALS(soap) +extern zend_string *soap_lang_en; + #ifdef ZTS #include "TSRM.h" #endif diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 167dd4bb30af8..55bcdf3eb8841 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -194,6 +194,8 @@ static zend_object_handlers soap_server_object_handlers; static zend_object_handlers soap_url_object_handlers; static zend_object_handlers soap_sdl_object_handlers; +zend_string *soap_lang_en; + typedef struct { soapServicePtr service; zend_object std; @@ -472,7 +474,7 @@ PHP_MSHUTDOWN_FUNCTION(soap) zend_hash_destroy(SOAP_GLOBAL(mem_cache)); free(SOAP_GLOBAL(mem_cache)); } - zend_string_release_ex(SOAP_GLOBAL(lang_en), true); + zend_string_release_ex(soap_lang_en, true); UNREGISTER_INI_ENTRIES(); return SUCCESS; } @@ -552,7 +554,7 @@ PHP_MINIT_FUNCTION(soap) old_error_handler = zend_error_cb; zend_error_cb = soap_error_handler; - SOAP_GLOBAL(lang_en) = zend_string_init_interned(ZEND_STRL("en"), true); + soap_lang_en = zend_string_init_interned(ZEND_STRL("en"), true); return SUCCESS; } @@ -712,7 +714,7 @@ PHP_METHOD(SoapFault, __construct) } this_ptr = ZEND_THIS; - set_soap_fault(this_ptr, fault_code_ns, fault_code, fault_string, fault_actor, details, name, lang); + set_soap_fault(this_ptr, fault_code_ns, fault_code, fault_string, fault_actor, details, name, soap_lang_en); if (headerfault != NULL) { ZVAL_COPY(Z_FAULT_HEADERFAULT_P(this_ptr), headerfault); } @@ -1859,7 +1861,7 @@ static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *acto static ZEND_NORETURN void soap_server_fault_en(char* code, char* string, char *actor, zval* details, zend_string* name) { - soap_server_fault(code, string, actor, details, name, SOAP_GLOBAL(lang_en)); + soap_server_fault(code, string, actor, details, name, soap_lang_en); } static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, zend_string *error_filename, const uint32_t error_lineno, zend_string *message) /* {{{ */ @@ -1928,7 +1930,7 @@ static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, z } ZVAL_NULL(&fault_obj); - set_soap_fault(&fault_obj, NULL, code, ZSTR_VAL(buffer), NULL, &outbuf, NULL, SOAP_GLOBAL(lang_en)); + set_soap_fault(&fault_obj, NULL, code, ZSTR_VAL(buffer), NULL, &outbuf, NULL, soap_lang_en); zend_string_release(buffer); fault = 1; } @@ -2941,7 +2943,7 @@ static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fa static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) { - add_soap_fault_ex(fault, obj, fault_code, fault_string, fault_actor, fault_detail, SOAP_GLOBAL(lang_en)); + add_soap_fault_ex(fault, obj, fault_code, fault_string, fault_actor, fault_detail, soap_lang_en); } void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ @@ -2953,7 +2955,7 @@ void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) { - add_soap_fault(obj, fault_code, fault_string, fault_actor, fault_detail, SOAP_GLOBAL(lang_en)); + add_soap_fault(obj, fault_code, fault_string, fault_actor, fault_detail, soap_lang_en); } static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang) /* {{{ */ diff --git a/ext/soap/tests/bugs/bug38005.phpt b/ext/soap/tests/bugs/bug38005.phpt index 67837861ea5eb..dc2437baa9e1f 100644 --- a/ext/soap/tests/bugs/bug38005.phpt +++ b/ext/soap/tests/bugs/bug38005.phpt @@ -42,4 +42,4 @@ echo($client->__getLastResponse()); --EXPECT-- This is our fault: Ä -TestThis is our fault: Ä +TestThis is our fault: Ä diff --git a/ext/soap/tests/bugs/bug68996.phpt b/ext/soap/tests/bugs/bug68996.phpt index bcc0e66cbebf9..8aa858e6545ec 100644 --- a/ext/soap/tests/bugs/bug68996.phpt +++ b/ext/soap/tests/bugs/bug68996.phpt @@ -56,4 +56,4 @@ handleFormatted($s, $HTTP_RAW_POST_DATA); some msg -some msg +some msg diff --git a/ext/soap/tests/soap12/T63.phpt b/ext/soap/tests/soap12/T63.phpt index a8cf3c431b24b..810207e50ee50 100644 --- a/ext/soap/tests/soap12/T63.phpt +++ b/ext/soap/tests/soap12/T63.phpt @@ -21,5 +21,5 @@ include "soap12-test.inc"; ?> --EXPECT-- -Country code must be 2 letters.env:SenderNot a valid country code +Country code must be 2 letters.env:SenderNot a valid country code ok From 6b470cf5d8383fdaf1ce71078a6dcfb769840eae Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 14 Aug 2025 21:51:29 +0100 Subject: [PATCH 08/14] Zend: Convert _zend_oparray_context.var_size field to uint32_t --- Zend/zend_compile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index c815248f78071..df95cdbbfb434 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -197,7 +197,7 @@ typedef struct _zend_oparray_context { struct _zend_oparray_context *prev; zend_op_array *op_array; uint32_t opcodes_size; - int vars_size; + uint32_t vars_size; int literals_size; uint32_t fast_call_var; uint32_t try_catch_offset; From 299bd63177881defd75f4eee0f3214a56f95b9e1 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 14 Aug 2025 21:51:56 +0100 Subject: [PATCH 09/14] Zend: Change return type of lookup_cv from int to uint32_t --- Zend/zend_compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d0ce85dd3c6fb..a36a4fcc9f868 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -536,7 +536,7 @@ static zend_always_inline uint32_t get_temporary_variable(void) /* {{{ */ } /* }}} */ -static int lookup_cv(zend_string *name) /* {{{ */{ +static uint32_t lookup_cv(zend_string *name) /* {{{ */{ zend_op_array *op_array = CG(active_op_array); int i = 0; zend_ulong hash_value = zend_string_hash_val(name); From 18ce83b2e3d540f4eeca7446b0cd04c1a58c282a Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 14 Aug 2025 21:53:13 +0100 Subject: [PATCH 10/14] Zend: Convert _zend_op_array.cache_size field to uint32_t --- Zend/zend_compile.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index df95cdbbfb434..7ca992104fe07 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -530,9 +530,9 @@ struct _zend_op_array { const zend_property_info *prop_info; /* The corresponding prop_info if this is a hook. */ /* END of common elements */ - int cache_size; /* number of run_time_cache_slots * sizeof(void*) */ - int last_var; /* number of CV variables */ - uint32_t last; /* number of opcodes */ + uint32_t cache_size; /* number of run_time_cache_slots * sizeof(void*) */ + int last_var; /* number of CV variables */ + uint32_t last; /* number of opcodes */ zend_op *opcodes; ZEND_MAP_PTR_DEF(HashTable *, static_variables_ptr); From a94dc485679afc6e29b0dc2827a814b896c35cdc Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 14 Aug 2025 21:12:25 +0100 Subject: [PATCH 11/14] Zend: Convert _zend_op_array.last_live_range field to uint32_t --- Zend/Optimizer/zend_dump.c | 4 ++-- Zend/zend_compile.h | 2 +- ext/opcache/jit/zend_jit.c | 3 +-- ext/opcache/jit/zend_jit_trace.c | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c index 4b7ca1615a862..8642d4488c241 100644 --- a/Zend/Optimizer/zend_dump.c +++ b/Zend/Optimizer/zend_dump.c @@ -1056,7 +1056,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl } if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { fprintf(stderr, "LIVE RANGES:\n"); - for (int i = 0; i < op_array->last_live_range; i++) { + for (uint32_t i = 0; i < op_array->last_live_range; i++) { fprintf(stderr, " %u: %04u - %04u ", EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), @@ -1116,7 +1116,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl } if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { fprintf(stderr, "LIVE RANGES:\n"); - for (int i = 0; i < op_array->last_live_range; i++) { + for (uint32_t i = 0; i < op_array->last_live_range; i++) { fprintf(stderr, " %u: %04u - %04u ", EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 7ca992104fe07..a58004638a653 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -541,7 +541,7 @@ struct _zend_op_array { uint32_t *refcount; - int last_live_range; + uint32_t last_live_range; int last_try_catch; zend_live_range *live_range; zend_try_catch_element *try_catch_array; diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 7e5eb6abbbc95..feede18999b91 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1268,13 +1268,12 @@ static void zend_jit_allocate_registers(zend_jit_ctx *ctx, const zend_op_array * && ssa->vars[i].definition >= 0 && ssa->ops[op_num].result_def == i) { const zend_live_range *range = op_array->live_range; - int j; op_num++; if (op_array->opcodes[op_num].opcode == ZEND_OP_DATA) { op_num++; } - for (j = 0; j < op_array->last_live_range; range++, j++) { + for (uint32_t j = 0; j < op_array->last_live_range; range++, j++) { if (range->start > op_num) { /* further blocks will not be relevant... */ break; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 5e10237d62f18..9dc6ea1c3b003 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6583,10 +6583,9 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t var_num = opline->result.var; uint32_t op_num = opline - op_array->opcodes; const zend_live_range *range = op_array->live_range; - int j; op_num += zend_jit_trace_op_len(opline); - for (j = 0; j < op_array->last_live_range; range++, j++) { + for (uint32_t j = 0; j < op_array->last_live_range; range++, j++) { if (range->start > op_num) { /* further blocks will not be relevant... */ break; From 0a47dd9bb4f33e0d579a7444670d6ba19e3d8f43 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 14 Aug 2025 21:23:49 +0100 Subject: [PATCH 12/14] Zend: Convert _zend_op_array.last_try_catch field to uint32_t --- Zend/Optimizer/block_pass.c | 4 ++-- Zend/Optimizer/dfa_pass.c | 2 +- Zend/Optimizer/nop_removal.c | 3 +-- Zend/Optimizer/zend_cfg.c | 5 ++--- Zend/Optimizer/zend_dump.c | 4 ++-- Zend/zend_compile.c | 4 ++-- Zend/zend_compile.h | 2 +- Zend/zend_generators.c | 3 +-- Zend/zend_opcode.c | 4 +--- Zend/zend_vm_def.h | 6 +++--- Zend/zend_vm_execute.h | 12 ++++++------ sapi/phpdbg/phpdbg_utils.c | 4 ++-- 12 files changed, 24 insertions(+), 29 deletions(-) diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c index f038c9e8a984a..9ca8b60f6c457 100644 --- a/Zend/Optimizer/block_pass.c +++ b/Zend/Optimizer/block_pass.c @@ -1125,7 +1125,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op /* adjust exception jump targets & remove unused try_catch_array entries */ if (op_array->last_try_catch) { - int i, j; + uint32_t i, j; uint32_t *map; ALLOCA_FLAG(use_heap); @@ -1165,7 +1165,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op while (opline < end) { if (opline->opcode == ZEND_FAST_RET && opline->op2.num != (uint32_t)-1 && - opline->op2.num < (uint32_t)j) { + opline->op2.num < j) { opline->op2.num = map[opline->op2.num]; } opline++; diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index b64f6d8fa817c..796e998493d76 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -226,7 +226,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op } /* update try/catch array */ - for (j = 0; j < op_array->last_try_catch; j++) { + for (uint32_t j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; if (op_array->try_catch_array[j].finally_op) { diff --git a/Zend/Optimizer/nop_removal.c b/Zend/Optimizer/nop_removal.c index fd5a87cbfb287..c39c4bb1e4ff7 100644 --- a/Zend/Optimizer/nop_removal.c +++ b/Zend/Optimizer/nop_removal.c @@ -34,7 +34,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx { zend_op *end, *opline; uint32_t new_count, i, shift; - int j; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); @@ -81,7 +80,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx } /* update try/catch array */ - for (j = 0; j < op_array->last_try_catch; j++) { + for (uint32_t j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; if (op_array->try_catch_array[j].finally_op) { diff --git a/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c index d2c54c9131962..32d5f49ef7df4 100644 --- a/Zend/Optimizer/zend_cfg.c +++ b/Zend/Optimizer/zend_cfg.c @@ -274,7 +274,6 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, { uint32_t flags = 0; uint32_t i; - int j; uint32_t *block_map; zend_function *fn; int blocks_count = 0; @@ -449,7 +448,7 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, } if (op_array->last_try_catch) { - for (j = 0; j < op_array->last_try_catch; j++) { + for (uint32_t j = 0; j < op_array->last_try_catch; j++) { BB_START(op_array->try_catch_array[j].try_op); if (op_array->try_catch_array[j].catch_op) { BB_START(op_array->try_catch_array[j].catch_op); @@ -494,7 +493,7 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, blocks_count++; /* Build CFG, Step 3: Calculate successors */ - for (j = 0; j < blocks_count; j++) { + for (int j = 0; j < blocks_count; j++) { zend_basic_block *block = &blocks[j]; zend_op *opline; if (block->len == 0) { diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c index 8642d4488c241..9c51ad223e060 100644 --- a/Zend/Optimizer/zend_dump.c +++ b/Zend/Optimizer/zend_dump.c @@ -1083,7 +1083,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl } if (op_array->last_try_catch) { fprintf(stderr, "EXCEPTION TABLE:\n"); - for (int i = 0; i < op_array->last_try_catch; i++) { + for (uint32_t i = 0; i < op_array->last_try_catch; i++) { fprintf(stderr, " BB%u", cfg->map[op_array->try_catch_array[i].try_op]); if (op_array->try_catch_array[i].catch_op) { @@ -1143,7 +1143,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl } if (op_array->last_try_catch) { fprintf(stderr, "EXCEPTION TABLE:\n"); - for (int i = 0; i < op_array->last_try_catch; i++) { + for (uint32_t i = 0; i < op_array->last_try_catch; i++) { fprintf(stderr, " %04u", op_array->try_catch_array[i].try_op); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a36a4fcc9f868..cff9f62e87ca8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5886,7 +5886,7 @@ static void zend_compile_break_continue(zend_ast *ast) /* {{{ */ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */ { zend_label *dest; - int current, remove_oplines = opline->op1.num; + int remove_oplines = opline->op1.num; zval *label; uint32_t opnum = opline - op_array->opcodes; @@ -5903,7 +5903,7 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */ zval_ptr_dtor_str(label); ZVAL_NULL(label); - current = opline->extended_value; + uint32_t current = opline->extended_value; for (; current != dest->brk_cont; current = CG(context).brk_cont_array[current].parent) { if (current == -1) { CG(in_compilation) = 1; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a58004638a653..ce660d026aaab 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -542,7 +542,7 @@ struct _zend_op_array { uint32_t *refcount; uint32_t last_live_range; - int last_try_catch; + uint32_t last_try_catch; zend_live_range *live_range; zend_try_catch_element *try_catch_array; diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index d5c9b1b1e313d..13f1887027c8e 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -244,7 +244,6 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_generator *current_generator = zend_generator_get_current(generator); zend_execute_data *ex = generator->execute_data; uint32_t op_num, try_catch_offset; - int i; /* If current_generator is running in a fiber, there are 2 cases to consider: * - If generator is also marked with ZEND_GENERATOR_IN_FIBER, then the @@ -289,7 +288,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ try_catch_offset = -1; /* Find the innermost try/catch that we are inside of. */ - for (i = 0; i < ex->func->op_array.last_try_catch; i++) { + for (uint32_t i = 0; i < ex->func->op_array.last_try_catch; i++) { zend_try_catch_element *try_catch = &ex->func->op_array.try_catch_array[i]; if (op_num < try_catch->try_op) { break; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 43c6efb9107a9..bf02d6a4164bd 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -686,9 +686,7 @@ static void zend_extension_op_array_handler(zend_extension *extension, zend_op_a static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num, uint32_t dst_num) { - int i; - - for (i = 0; i < op_array->last_try_catch; i++) { + for (uint32_t i = 0; i < op_array->last_try_catch; i++) { if ((op_num < op_array->try_catch_array[i].finally_op || op_num >= op_array->try_catch_array[i].finally_end) && (dst_num >= op_array->try_catch_array[i].finally_op && diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 3ebf816cf3817..b90117e70f226 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8164,7 +8164,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) } uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes; - int i, current_try_catch_offset = -1; + uint32_t current_try_catch_offset = -1; if ((throw_op->opcode == ZEND_FREE || throw_op->opcode == ZEND_FE_FREE) && throw_op->extended_value & ZEND_FREE_ON_RETURN) { @@ -8175,7 +8175,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) const zend_live_range *range = find_live_range( &EX(func)->op_array, throw_op_num, throw_op->op1.var); /* free op1 of the corresponding RETURN */ - for (i = throw_op_num; i < range->end; i++) { + for (uint32_t i = throw_op_num; i < range->end; i++) { if (EX(func)->op_array.opcodes[i].opcode == ZEND_FREE || EX(func)->op_array.opcodes[i].opcode == ZEND_FE_FREE) { /* pass */ @@ -8191,7 +8191,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) } /* Find the innermost try/catch/finally the exception was thrown in */ - for (i = 0; i < EX(func)->op_array.last_try_catch; i++) { + for (uint32_t i = 0; i < EX(func)->op_array.last_try_catch; i++) { zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i]; if (try_catch->try_op > throw_op_num) { /* further blocks will not be relevant... */ diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b0d6f2bc33d96..9d940cfadbcb1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3392,7 +3392,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_HANDLE_EXCEPT } uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes; - int i, current_try_catch_offset = -1; + uint32_t current_try_catch_offset = -1; if ((throw_op->opcode == ZEND_FREE || throw_op->opcode == ZEND_FE_FREE) && throw_op->extended_value & ZEND_FREE_ON_RETURN) { @@ -3403,7 +3403,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_HANDLE_EXCEPT const zend_live_range *range = find_live_range( &EX(func)->op_array, throw_op_num, throw_op->op1.var); /* free op1 of the corresponding RETURN */ - for (i = throw_op_num; i < range->end; i++) { + for (uint32_t i = throw_op_num; i < range->end; i++) { if (EX(func)->op_array.opcodes[i].opcode == ZEND_FREE || EX(func)->op_array.opcodes[i].opcode == ZEND_FE_FREE) { /* pass */ @@ -3419,7 +3419,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_HANDLE_EXCEPT } /* Find the innermost try/catch/finally the exception was thrown in */ - for (i = 0; i < EX(func)->op_array.last_try_catch; i++) { + for (uint32_t i = 0; i < EX(func)->op_array.last_try_catch; i++) { zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i]; if (try_catch->try_op > throw_op_num) { /* further blocks will not be relevant... */ @@ -58815,7 +58815,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HANDLE_EXCEPTION_S } uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes; - int i, current_try_catch_offset = -1; + uint32_t current_try_catch_offset = -1; if ((throw_op->opcode == ZEND_FREE || throw_op->opcode == ZEND_FE_FREE) && throw_op->extended_value & ZEND_FREE_ON_RETURN) { @@ -58826,7 +58826,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HANDLE_EXCEPTION_S const zend_live_range *range = find_live_range( &EX(func)->op_array, throw_op_num, throw_op->op1.var); /* free op1 of the corresponding RETURN */ - for (i = throw_op_num; i < range->end; i++) { + for (uint32_t i = throw_op_num; i < range->end; i++) { if (EX(func)->op_array.opcodes[i].opcode == ZEND_FREE || EX(func)->op_array.opcodes[i].opcode == ZEND_FE_FREE) { /* pass */ @@ -58842,7 +58842,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HANDLE_EXCEPTION_S } /* Find the innermost try/catch/finally the exception was thrown in */ - for (i = 0; i < EX(func)->op_array.last_try_catch; i++) { + for (uint32_t i = 0; i < EX(func)->op_array.last_try_catch; i++) { zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i]; if (try_catch->try_op > throw_op_num) { /* further blocks will not be relevant... */ diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index 329ee9a8830e3..55d97acc38759 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -612,7 +612,7 @@ int phpdbg_is_auto_global(char *name, int len) { PHPDBG_API bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zend_object *exception) { const zend_op *op; zend_op *cur; - uint32_t op_num, i; + uint32_t op_num; zend_op_array *op_array = &execute_data->func->op_array; if (execute_data->opline >= EG(exception_op) && execute_data->opline < EG(exception_op) + 3 && EG(opline_before_exception)) { @@ -623,7 +623,7 @@ PHPDBG_API bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zend_obj op_num = op - op_array->opcodes; - for (i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op <= op_num; i++) { + for (uint32_t i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op <= op_num; i++) { uint32_t catch = op_array->try_catch_array[i].catch_op, finally = op_array->try_catch_array[i].finally_op; if (op_num <= catch || op_num <= finally) { if (finally) { From c515c04b0acd51cb0e677cff2a5061d3e17abe14 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 14 Aug 2025 21:42:28 +0100 Subject: [PATCH 13/14] Zend: Convert _zend_op_array literal fields to uint32_t --- Zend/Optimizer/compact_literals.c | 13 +++++++------ Zend/Optimizer/zend_optimizer.c | 6 +++--- Zend/Optimizer/zend_optimizer_internal.h | 2 +- Zend/zend_compile.c | 2 +- Zend/zend_compile.h | 4 ++-- sapi/phpdbg/phpdbg_info.c | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index d0aaccec7ce2c..a2fa32994feeb 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -110,7 +110,7 @@ static zend_string *create_str_cache_key(zval *literal, uint8_t num_related) void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *opline, *end; - int i, j, n, *map; + int n, *map; uint32_t cache_size; zval zv, *pos; literal_info *info; @@ -124,6 +124,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot, *jmp_slot; if (op_array->last_literal) { + uint32_t j; info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info)); /* Mark literals of specific types */ @@ -258,9 +259,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx op_array->function_name ? op_array->function_name->val : "main"); fprintf(stderr, "Literals table size %d\n", op_array->last_literal); - for (int i = 0; i < op_array->last_literal; i++) { + for (uint32_t i = 0; i < op_array->last_literal; i++) { zend_string *str = zval_get_string(op_array->literals + i); - fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str)); + fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str)); zend_string_release(str); } fflush(stderr); @@ -272,7 +273,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0); map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int)); memset(map, 0, op_array->last_literal * sizeof(int)); - for (i = 0; i < op_array->last_literal; i++) { + for (uint32_t i = 0; i < op_array->last_literal; i++) { if (!info[i].num_related) { /* unset literal */ zval_ptr_dtor_nogc(&op_array->literals[i]); @@ -770,9 +771,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx { fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal); - for (int i = 0; i < op_array->last_literal; i++) { + for (uint32_t i = 0; i < op_array->last_literal; i++) { zend_string *str = zval_get_string(op_array->literals + i); - fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str)); + fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str)); zend_string_release(str); } fflush(stderr); diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index 4e5ecca8a0f80..a206238b552b3 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -210,9 +210,9 @@ void zend_optimizer_convert_to_free_op1(zend_op_array *op_array, zend_op *opline } } -int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv) +uint32_t zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv) { - int i = op_array->last_literal; + uint32_t i = op_array->last_literal; op_array->last_literal++; op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval)); ZVAL_COPY_VALUE(&op_array->literals[i], zv); @@ -220,7 +220,7 @@ int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv) return i; } -static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) { +static inline uint32_t zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) { zval zv; ZVAL_STR(&zv, str); zend_string_hash_val(str); diff --git a/Zend/Optimizer/zend_optimizer_internal.h b/Zend/Optimizer/zend_optimizer_internal.h index 896fe8fb47289..9c238690932ef 100644 --- a/Zend/Optimizer/zend_optimizer_internal.h +++ b/Zend/Optimizer/zend_optimizer_internal.h @@ -79,7 +79,7 @@ static inline bool zend_optimizer_is_loop_var_free(const zend_op *opline) { } void zend_optimizer_convert_to_free_op1(zend_op_array *op_array, zend_op *opline); -int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv); +uint32_t zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv); bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy); void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value); bool zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index cff9f62e87ca8..358b6723e0054 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -588,7 +588,7 @@ static inline void zend_insert_literal(zend_op_array *op_array, zval *zv, int li static int zend_add_literal(zval *zv) /* {{{ */ { zend_op_array *op_array = CG(active_op_array); - int i = op_array->last_literal; + uint32_t i = op_array->last_literal; op_array->last_literal++; if (i >= CG(context).literals_size) { while (i >= CG(context).literals_size) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index ce660d026aaab..ab90b2dcaa836 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -198,7 +198,7 @@ typedef struct _zend_oparray_context { zend_op_array *op_array; uint32_t opcodes_size; uint32_t vars_size; - int literals_size; + uint32_t literals_size; uint32_t fast_call_var; uint32_t try_catch_offset; int current_brk_cont; @@ -550,7 +550,7 @@ struct _zend_op_array { uint32_t line_start; uint32_t line_end; - int last_literal; + uint32_t last_literal; uint32_t num_dynamic_func_defs; zval *literals; diff --git a/sapi/phpdbg/phpdbg_info.c b/sapi/phpdbg/phpdbg_info.c index b329bdac728bb..9c93cd2b88efb 100644 --- a/sapi/phpdbg/phpdbg_info.c +++ b/sapi/phpdbg/phpdbg_info.c @@ -307,7 +307,7 @@ PHPDBG_INFO(literal) /* {{{ */ bool in_executor = PHPDBG_G(in_execution) && EG(current_execute_data) && EG(current_execute_data)->func; if (in_executor || PHPDBG_G(ops)) { zend_op_array *ops = in_executor ? &EG(current_execute_data)->func->op_array : PHPDBG_G(ops); - int literal = 0, count = ops->last_literal - 1; + uint32_t literal = 0, count = ops->last_literal - 1; if (ops->function_name) { if (ops->scope) { From 294a0801a5cf5688ec28235778e8ebb027137169 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Mon, 29 Sep 2025 12:45:45 -0300 Subject: [PATCH 14/14] ODBC fetch refactoring (#19848) * Merge odbc_fetch_into into odbc_fetch_hash Now that we can assume fetch_hash exists, there's a lot of redundancy in these functions. Merge their implementations, and smooth over the differences in how they handle returning their result set as an array. * Convert php_odbc_fetch_hash to ZPP * Use SQLFetchScroll instead of SQLExtendedFetch Fixes GH-19522 * Convert result type constants to an enum * Implement odbc_fetch_row in terms of php_odbc_fetch_hash These are also doing extremely similar jobs, but with slightly different behaviours for the return value (in this case, none, as it's tended to be used with odbc_result). Unify this too. The $row value deprecation for 0/-1 is only handled for odbc_fetch_row; it's too late to do so for PHP 8.5. Should probably unify it for PHP 8.6. * Rename php_odbc_fetch_hash to remove _hash Since this is a much more shared fetch function now. --- ext/odbc/php_odbc.c | 283 +++++++++++++------------------------------- 1 file changed, 84 insertions(+), 199 deletions(-) diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 491f59b2bce3f..a6af7469cbac3 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1306,11 +1306,14 @@ PHP_FUNCTION(odbc_exec) } /* }}} */ -#define ODBC_NUM 1 -#define ODBC_OBJECT 2 +typedef enum php_odbc_fetch_result_type_t { + ODBC_NONE = 0, + ODBC_NUM = 1, + ODBC_OBJECT = 2, +} php_odbc_fetch_result_type_t; -/* {{{ php_odbc_fetch_hash */ -static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) +/* {{{ php_odbc_fetch */ +static void php_odbc_fetch(INTERNAL_FUNCTION_PARAMETERS, bool return_array, php_odbc_fetch_result_type_t result_type) { int i; odbc_result *result; @@ -1319,29 +1322,56 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) char *buf = NULL; zend_long pv_row = 0; bool pv_row_is_null = true; - zval *pv_res, tmp; - SQLULEN crow; - SQLUSMALLINT RowStatus[1]; + zval *pv_res, *pv_res_arr, tmp; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) { - RETURN_THROWS(); + if (return_array || result_type == ODBC_NONE) { + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(pv_row, pv_row_is_null) + ZEND_PARSE_PARAMETERS_END(); + /* So we can use pv_res_arr for both return value and passed */ + pv_res_arr = return_value; + } else { + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_ZVAL(pv_res_arr) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(pv_row, pv_row_is_null) + ZEND_PARSE_PARAMETERS_END(); } result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); - /* TODO deprecate $row argument values less than 1 after PHP 8.4 */ + /* TODO deprecate $row argument values less than 1 after PHP 8.4 + * for functions other than odbc_fetch_row (see GH-13910) + */ + if (!result_type && !pv_row_is_null && pv_row < 1) { + php_error_docref(NULL, E_WARNING, "Argument #3 ($row) must be greater than or equal to 1"); + RETURN_FALSE; + } if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); RETURN_FALSE; } + /* If we're initializing a passed value into an array, do it before the fetch + * so that an empty result set will still be an array. + */ + if (!return_array && result_type) { + pv_res_arr = zend_try_array_init(pv_res_arr); + if (!pv_res_arr) { + RETURN_THROWS(); + } + } + if (result->fetch_abs) { if (!pv_row_is_null && pv_row > 0) { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN)pv_row,&crow,RowStatus); + rc = SQLFetchScroll(result->stmt, SQL_FETCH_ABSOLUTE, (SQLLEN)pv_row); } else { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus); + rc = SQLFetchScroll(result->stmt, SQL_FETCH_NEXT, 1); } } else { rc = SQLFetch(result->stmt); @@ -1349,18 +1379,28 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { if (rc == SQL_ERROR) { - odbc_sql_error(result->conn_ptr, result->stmt, "SQLExtendedFetch"); + odbc_sql_error(result->conn_ptr, result->stmt, "SQLFetchScroll"); } RETURN_FALSE; } - array_init(return_value); + /* ...but if returning an array, init only if we have a result set */ + if (return_array && result_type) { + array_init(pv_res_arr); + } if (!pv_row_is_null && pv_row > 0 && result->fetch_abs) result->fetched = (SQLLEN)pv_row; else result->fetched++; + /* For fetch_row, we don't return anything other than true, + * odbc_result will be used to fetch values instead. + */ + if (result_type == ODBC_NONE) { + RETURN_TRUE; + } + for(i = 0; i < result->numcols; i++) { sql_c_type = SQL_C_CHAR; @@ -1375,6 +1415,8 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) if (result->binmode == 1) { sql_c_type = SQL_C_BINARY; } + + /* TODO: Check this is the intended behaviour */ ZEND_FALLTHROUGH; case SQL_LONGVARCHAR: case SQL_WLONGVARCHAR: @@ -1391,7 +1433,9 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) if (rc == SQL_ERROR) { odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData"); efree(buf); - zval_ptr_dtor(return_value); + if (return_array) { + zval_ptr_dtor(pv_res_arr); + } RETURN_FALSE; } @@ -1425,18 +1469,23 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) } if (result_type & ODBC_NUM) { - zend_hash_index_update(Z_ARRVAL_P(return_value), i, &tmp); + zend_hash_index_update(Z_ARRVAL_P(pv_res_arr), i, &tmp); } else { if (!*(result->values[i].name) && Z_TYPE(tmp) == IS_STRING) { - zend_hash_update(Z_ARRVAL_P(return_value), Z_STR(tmp), &tmp); + zend_hash_update(Z_ARRVAL_P(pv_res_arr), Z_STR(tmp), &tmp); } else { - zend_hash_str_update(Z_ARRVAL_P(return_value), result->values[i].name, strlen(result->values[i].name), &tmp); + zend_hash_str_update(Z_ARRVAL_P(pv_res_arr), result->values[i].name, strlen(result->values[i].name), &tmp); } } } if (buf) { efree(buf); } + + /* return_value was set to an array otherwise */ + if (!return_array) { + RETURN_LONG(result->numcols); + } } /* }}} */ @@ -1444,7 +1493,7 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) /* {{{ Fetch a result row as an object */ PHP_FUNCTION(odbc_fetch_object) { - php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, ODBC_OBJECT); + php_odbc_fetch(INTERNAL_FUNCTION_PARAM_PASSTHRU, true, ODBC_OBJECT); if (Z_TYPE_P(return_value) == IS_ARRAY) { object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value)); } @@ -1454,184 +1503,21 @@ PHP_FUNCTION(odbc_fetch_object) /* {{{ Fetch a result row as an associative array */ PHP_FUNCTION(odbc_fetch_array) { - php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, ODBC_OBJECT); + php_odbc_fetch(INTERNAL_FUNCTION_PARAM_PASSTHRU, true, ODBC_OBJECT); } /* }}} */ /* {{{ Fetch one result row into an array */ PHP_FUNCTION(odbc_fetch_into) { - int i; - odbc_result *result; - RETCODE rc; - SQLSMALLINT sql_c_type; - char *buf = NULL; - zval *pv_res, *pv_res_arr, tmp; - zend_long pv_row = 0; - bool pv_row_is_null = true; - SQLULEN crow; - SQLUSMALLINT RowStatus[1]; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|l!", &pv_res, odbc_result_ce, &pv_res_arr, &pv_row, &pv_row_is_null) == FAILURE) { - RETURN_THROWS(); - } - - result = Z_ODBC_RESULT_P(pv_res); - CHECK_ODBC_RESULT(result); - - /* TODO deprecate $row argument values less than 1 after PHP 8.4 */ - - if (result->numcols == 0) { - php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); - RETURN_FALSE; - } - - pv_res_arr = zend_try_array_init(pv_res_arr); - if (!pv_res_arr) { - RETURN_THROWS(); - } - - if (result->fetch_abs) { - if (!pv_row_is_null && pv_row > 0) { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN)pv_row,&crow,RowStatus); - } else { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus); - } - } else { - rc = SQLFetch(result->stmt); - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - if (rc == SQL_ERROR) { - odbc_sql_error(result->conn_ptr, result->stmt, "SQLExtendedFetch"); - } - RETURN_FALSE; - } - - if (!pv_row_is_null && pv_row > 0 && result->fetch_abs) - result->fetched = (SQLLEN)pv_row; - else - result->fetched++; - - for(i = 0; i < result->numcols; i++) { - sql_c_type = SQL_C_CHAR; - - switch(result->values[i].coltype) { - case SQL_BINARY: - case SQL_VARBINARY: - case SQL_LONGVARBINARY: - if (result->binmode <= 0) { - ZVAL_EMPTY_STRING(&tmp); - break; - } - if (result->binmode == 1) sql_c_type = SQL_C_BINARY; - - /* TODO: Check this is the intended behaviour */ - ZEND_FALLTHROUGH; - case SQL_LONGVARCHAR: - case SQL_WLONGVARCHAR: - if (IS_SQL_LONG(result->values[i].coltype) && result->longreadlen <= 0) { - ZVAL_EMPTY_STRING(&tmp); - break; - } - - if (buf == NULL) { - buf = emalloc(result->longreadlen + 1); - } - rc = SQLGetData(result->stmt, (SQLUSMALLINT)(i + 1),sql_c_type, buf, result->longreadlen + 1, &result->values[i].vallen); - - if (rc == SQL_ERROR) { - odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData"); - efree(buf); - RETURN_FALSE; - } - if (rc == SQL_SUCCESS_WITH_INFO) { - ZVAL_STRINGL(&tmp, buf, result->longreadlen); - } else if (rc != SQL_SUCCESS) { - php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (retcode %u)", i + 1, rc); - ZVAL_FALSE(&tmp); - } else if (result->values[i].vallen == SQL_NULL_DATA) { - ZVAL_NULL(&tmp); - break; - } else if (result->values[i].vallen == SQL_NO_TOTAL) { - php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", i + 1); - ZVAL_FALSE(&tmp); - } else { - ZVAL_STRINGL(&tmp, buf, result->values[i].vallen); - } - break; - - default: - if (result->values[i].vallen == SQL_NULL_DATA) { - ZVAL_NULL(&tmp); - break; - } else if (result->values[i].vallen == SQL_NO_TOTAL) { - php_error_docref(NULL, E_WARNING, "Cannot get data of column #%d (driver cannot determine length)", i + 1); - ZVAL_FALSE(&tmp); - break; - } - ZVAL_STRINGL(&tmp, result->values[i].value, result->values[i].vallen); - break; - } - zend_hash_index_update(Z_ARRVAL_P(pv_res_arr), i, &tmp); - } - if (buf) efree(buf); - RETURN_LONG(result->numcols); + php_odbc_fetch(INTERNAL_FUNCTION_PARAM_PASSTHRU, false, ODBC_NUM); } /* }}} */ /* {{{ Fetch a row */ PHP_FUNCTION(odbc_fetch_row) { - odbc_result *result; - RETCODE rc; - zval *pv_res; - zend_long pv_row = 0; - bool pv_row_is_null = true; - SQLULEN crow; - SQLUSMALLINT RowStatus[1]; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) { - RETURN_THROWS(); - } - - result = Z_ODBC_RESULT_P(pv_res); - CHECK_ODBC_RESULT(result); - - if (!pv_row_is_null && pv_row < 1) { - php_error_docref(NULL, E_WARNING, "Argument #3 ($row) must be greater than or equal to 1"); - RETURN_FALSE; - } - - if (result->numcols == 0) { - php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); - RETURN_FALSE; - } - - if (result->fetch_abs) { - if (!pv_row_is_null) { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_ABSOLUTE,(SQLLEN)pv_row,&crow,RowStatus); - } else { - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus); - } - } else { - rc = SQLFetch(result->stmt); - } - - if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - if (rc == SQL_ERROR) { - odbc_sql_error(result->conn_ptr, result->stmt, "SQLExtendedFetch"); - } - RETURN_FALSE; - } - - if (!pv_row_is_null) { - result->fetched = (SQLLEN)pv_row; - } else { - result->fetched++; - } - - RETURN_TRUE; + php_odbc_fetch(INTERNAL_FUNCTION_PARAM_PASSTHRU, false, ODBC_NONE); } /* }}} */ @@ -1648,8 +1534,6 @@ PHP_FUNCTION(odbc_result) RETCODE rc; SQLLEN fieldsize; zval *pv_res; - SQLULEN crow; - SQLUSMALLINT RowStatus[1]; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) @@ -1700,14 +1584,15 @@ PHP_FUNCTION(odbc_result) if (result->fetched == 0) { /* User forgot to call odbc_fetch_row(), or wants to reload the results, do it now */ - if (result->fetch_abs) - rc = SQLExtendedFetch(result->stmt, SQL_FETCH_NEXT, 1, &crow,RowStatus); - else + if (result->fetch_abs) { + rc = SQLFetchScroll(result->stmt, SQL_FETCH_NEXT, 1); + } else { rc = SQLFetch(result->stmt); + } if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { if (rc == SQL_ERROR) { - odbc_sql_error(result->conn_ptr, result->stmt, "SQLExtendedFetch"); + odbc_sql_error(result->conn_ptr, result->stmt, "SQLFetchScroll"); } RETURN_FALSE; } @@ -1850,8 +1735,6 @@ PHP_FUNCTION(odbc_result_all) char *pv_format = NULL; size_t i, pv_format_len = 0; SQLSMALLINT sql_c_type; - SQLULEN crow; - SQLUSMALLINT RowStatus[1]; if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s", &pv_res, odbc_result_ce, &pv_format, &pv_format_len) == FAILURE) { RETURN_THROWS(); @@ -1864,10 +1747,11 @@ PHP_FUNCTION(odbc_result_all) php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); RETURN_FALSE; } - if (result->fetch_abs) - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus); - else + if (result->fetch_abs) { + rc = SQLFetchScroll(result->stmt, SQL_FETCH_NEXT, 1); + } else { rc = SQLFetch(result->stmt); + } if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { php_printf("

No rows found

\n"); @@ -1962,10 +1846,11 @@ PHP_FUNCTION(odbc_result_all) } php_printf("\n"); - if (result->fetch_abs) - rc = SQLExtendedFetch(result->stmt,SQL_FETCH_NEXT,1,&crow,RowStatus); - else + if (result->fetch_abs) { + rc = SQLFetchScroll(result->stmt, SQL_FETCH_NEXT, 1); + } else { rc = SQLFetch(result->stmt); + } } php_printf("\n"); if (buf) efree(buf);