From 8b13fcc13edfb49ca32f5a6c8563e7dd71ff5ed2 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 6 Sep 2026 10:58:53 -0400 Subject: [PATCH 1/2] simplexml: fix addChild() namespace filter on the returned element addChild() passed the caller's prefix to node_as_zval_str() with isprefix set to 0, so the returned element filtered its children by comparing that prefix against the namespace href and matched nothing. Take the prefix from the created node and mark it as one, but only when the caller asked for a namespace, so a plain addChild() keeps the unfiltered view that its attributes and non-namespaced children rely on. Closes GH-23599 --- NEWS | 3 + ext/simplexml/simplexml.c | 7 ++- .../addChild_ns_filter_returned_element.phpt | 56 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 ext/simplexml/tests/addChild_ns_filter_returned_element.phpt diff --git a/NEWS b/NEWS index 935583e87338..31cb928f0bd8 100644 --- a/NEWS +++ b/NEWS @@ -133,6 +133,9 @@ PHP NEWS - SimpleXML: . Fixed writing to a dimension of the object returned by attributes() not creating the attribute. (Ilia Alshanetsky) + . Fixed child elements of the element returned by + SimpleXMLElement::addChild() not being accessible by property name when + namespaces are involved. (Ilia Alshanetsky) - Zip: . Fixed bug GH-23276 (ZipArchive subclass storing its own stream cannot be diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 44fdef5e12d7..9e5f293fe787 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1679,6 +1679,7 @@ PHP_METHOD(SimpleXMLElement, addChild) xmlNodePtr node, newnode; xmlNsPtr nsptr = NULL; xmlChar *localname, *prefix = NULL; + const xmlChar *retprefix = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!s!", &qname, &qname_len, &value, &value_len, &nsuri, &nsuri_len) == FAILURE) { @@ -1727,7 +1728,11 @@ PHP_METHOD(SimpleXMLElement, addChild) } } - node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname, prefix, 0); + if ((prefix != NULL || nsuri != NULL) && newnode->ns != NULL) { + retprefix = newnode->ns->prefix; + } + + node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname, retprefix, 1); xmlFree(localname); if (prefix != NULL) { diff --git a/ext/simplexml/tests/addChild_ns_filter_returned_element.phpt b/ext/simplexml/tests/addChild_ns_filter_returned_element.phpt new file mode 100644 index 000000000000..b0452f30182b --- /dev/null +++ b/ext/simplexml/tests/addChild_ns_filter_returned_element.phpt @@ -0,0 +1,56 @@ +--TEST-- +SimpleXMLElement::addChild() wrong namespace filter on returned element +--EXTENSIONS-- +simplexml +--FILE-- +'); +$c = $x->addChild('a:kid', null, 'http://example.com'); +$c->addChild('inner', 'v'); +echo trim($x->asXML()), "\n"; +echo (string) $c->inner, "\n"; +var_dump(isset($c->inner)); + +$y = new SimpleXMLElement(''); +$d = $y->addChild('kid', null, 'http://example.com'); +$d->addChild('inner', 'w'); +echo trim($y->asXML()), "\n"; +echo (string) $d->inner, "\n"; + +$z = new SimpleXMLElement(''); +$e = $z->addChild('a:kid'); +$e->addChild('inner', 'z'); +echo trim($z->asXML()), "\n"; +echo (string) $e->inner, "\n"; +var_dump(isset($e->inner)); + +$q = new SimpleXMLElement(''); +$f = $q->addChild('kid'); +$f->addAttribute('id', '7'); +echo trim($q->asXML()), "\n"; +echo (string) $f['id'], "\n"; + +$m = new SimpleXMLElement(''); +$g = $m->addChild('kid', null, 'http://example.com'); +$g->addAttribute('id', '8'); +echo trim($m->asXML()), "\n"; +var_dump(isset($g['id'])); +?> +--EXPECT-- + +v +v +bool(true) + +w +w + +z +z +bool(true) + + +7 + + +bool(false) From b2abdea6646882868122d41b6b62e16386d058d9 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 8 Sep 2026 06:37:02 -0400 Subject: [PATCH 2/2] odbc: make odbc_sql_error() formattable, drop odbc_colattribute_failed (#23612) odbc_colattribute_failed() existed only because odbc_sql_error() could not take a column number. Make the reporter variadic so callers can format the context into it, and route the two SQLColAttribute sites through it. Those failures now also populate the connection's last-error state, so odbc_error() and odbc_errormsg() report them. Closes GH-23612 --- NEWS | 2 ++ ext/odbc/php_odbc.c | 34 +++++++++++----------------------- ext/odbc/php_odbc_includes.h | 4 +--- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/NEWS b/NEWS index 205b2f071ff8..f02dbf699c8b 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,8 @@ PHP NEWS . Fixed odbc_field_len(), odbc_field_scale() and odbc_field_type() returning uninitialized memory when SQLColAttribute fails. (Ilia Alshanetsky) + . odbc_error() and odbc_errormsg() now also report SQLColAttribute + failures. (Ilia Alshanetsky) - PCNTL: . Fixed the declared signature of pcntl_signal(), whose $restart_syscalls diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index d1cf2716cfca..4b05d8dc4455 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -577,7 +577,7 @@ PHP_MINFO_FUNCTION(odbc) /* }}} */ /* {{{ odbc_sql_error */ -void odbc_sql_error(ODBC_SQL_ERROR_PARAMS) +void odbc_sql_error(odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, const char *func, ...) { SQLINTEGER error; /* Not used */ SQLSMALLINT errormsgsize; /* Not used */ @@ -608,7 +608,14 @@ void odbc_sql_error(ODBC_SQL_ERROR_PARAMS) memcpy(conn_resource->lasterrormsg, ODBCG(lasterrormsg), sizeof(ODBCG(lasterrormsg))); } if (func) { - php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s in %s", ODBCG(lasterrormsg), ODBCG(laststate), func); + va_list args; + char *desc; + + va_start(args, func); + vspprintf(&desc, 0, func, args); + va_end(args); + php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s in %s", ODBCG(lasterrormsg), ODBCG(laststate), desc); + efree(desc); } else { php_error_docref(NULL, E_WARNING, "SQL error: %s, SQL state %s", ODBCG(lasterrormsg), ODBCG(laststate)); } @@ -785,25 +792,6 @@ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type) } /* }}} */ -static void odbc_colattribute_failed(odbc_result *result, zend_long pv_num) -{ -#if defined(ODBCVER) && (ODBCVER >= 0x0300) - SQLINTEGER diag_error; - SQLCHAR diag_state[6]; - SQLCHAR diag_text[128]; - - memset(diag_state, '\0', sizeof(diag_state)); - memset(diag_text, '\0', sizeof(diag_text)); - if (SQL_SUCCESS == SQLGetDiagRec(SQL_HANDLE_STMT, result->stmt, 1, diag_state, &diag_error, diag_text, sizeof(diag_text), NULL)) { - diag_state[sizeof(diag_state) - 1] = '\0'; - diag_text[sizeof(diag_text) - 1] = '\0'; - php_error_docref(NULL, E_WARNING, "SQLColAttribute failed for field #%d: [%s] %s", (int)pv_num, diag_state, diag_text); - return; - } -#endif - php_error_docref(NULL, E_WARNING, "SQLColAttribute failed for field #%d", (int)pv_num); -} - /* {{{ odbc_column_lengths */ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) { @@ -836,7 +824,7 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) rc = SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, (SQLUSMALLINT)(type ? SQL_COLUMN_SCALE : SQL_COLUMN_PRECISION), NULL, 0, NULL, &len); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - odbc_colattribute_failed(result, pv_num); + odbc_sql_error(result->conn_ptr, result->stmt, "SQLColAttribute column #" ZEND_LONG_FMT, pv_num); len = 0; } @@ -2368,7 +2356,7 @@ PHP_FUNCTION(odbc_field_type) tmp[0] = '\0'; rc = SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, SQL_COLUMN_TYPE_NAME, tmp, sizeof(tmp) - 1, &tmplen, NULL); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - odbc_colattribute_failed(result, pv_num); + odbc_sql_error(result->conn_ptr, result->stmt, "SQLColAttribute column #" ZEND_LONG_FMT, pv_num); RETURN_FALSE; } diff --git a/ext/odbc/php_odbc_includes.h b/ext/odbc/php_odbc_includes.h index 090dfc3089a9..ad51454ec15b 100644 --- a/ext/odbc/php_odbc_includes.h +++ b/ext/odbc/php_odbc_includes.h @@ -145,9 +145,7 @@ odbc_connection *odbc_get_conn(HashTable *list, int count); void odbc_del_conn(HashTable *list, int ind); void odbc_bindcols(odbc_result *result); -#define ODBC_SQL_ERROR_PARAMS odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, char *func - -void odbc_sql_error(ODBC_SQL_ERROR_PARAMS); +void odbc_sql_error(odbc_connection *conn_resource, ODBC_SQL_STMT_T stmt, const char *func, ...) ZEND_ATTRIBUTE_FORMAT(printf, 3, 4); #define IS_SQL_LONG(x) (x == SQL_LONGVARBINARY || x == SQL_LONGVARCHAR || x == SQL_WLONGVARCHAR)