[php-src] master: ext/soap: Improve SOAP scalar decoding error messages (#22706)

Weilin Du via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Weilin Du (LamentXU123)
Committer: GitHub (web-flow)
Pusher: LamentXU123
Date: 2026-07-14T04:27:52+08:00

Commit: https://github.com/php/php-src/commit/4fa10e251364cb0497efdef3ab6a642b6c784805
Raw diff: https://github.com/php/php-src/commit/4fa10e251364cb0497efdef3ab6a642b6c784805.diff

ext/soap: Improve SOAP scalar decoding error messages (#22706)

SOAP encoding errors now report the affected type or failing operation in
the error message instead of the generic "Encoding: Violation of encoding
rules" message. Code that compares the exact message may need to be
updated.

Changed paths:
  A  ext/soap/tests/scalar_error_messages.phpt
  M  NEWS
  M  UPGRADING
  M  ext/soap/php_encoding.c
  M  ext/soap/tests/bugs/bug39832.phpt
  M  ext/soap/tests/hexbin_odd_length.phpt
  M  ext/soap/tests/soap12/T27.phpt
  M  ext/soap/tests/soap12/T58.phpt


Diff:

diff --git a/NEWS b/NEWS
index fb54cab61c0f..702d9d04fe3d 100644
--- a/NEWS
+++ b/NEWS
@@ -103,6 +103,8 @@ PHP                                                                        NEWS
     do_request() parameters). (David Carlier)
   . Fixed xsd:hexBinary decoding to reject odd-length values instead of
     silently truncating the last nibble. (Weilin Du)
+  . Made SOAP encoding errors report the affected type or failing operation
+    instead of the generic "Violation of encoding rules" message. (Weilin Du)
 
 - Standard:
   . Fixed sleep() and usleep() to reject values that overflow the underlying
diff --git a/UPGRADING b/UPGRADING
index 729dfed0363e..74a5e066483f 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -147,6 +147,10 @@ PHP 8.6 UPGRADE NOTES
   . WSDL/XML Schema parsing now rejects out-of-range integer values for
     occurrence constraints and integer restriction facets. Negative minOccurs
     and maxOccurs values are rejected as well.
+  . SOAP encoding errors now report the affected type or failing operation in
+    the error message instead of the generic "Encoding: Violation of encoding
+    rules" message. Code that compares the exact message may need to be
+    updated.
 
 - Sodium:
   . The password-hashing functions sodium_crypto_pwhash(),
diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c
index 301cdd8588b4..d08c5a683189 100644
--- a/ext/soap/php_encoding.c
+++ b/ext/soap/php_encoding.c
@@ -82,6 +82,11 @@ static xmlNodePtr to_xml_any(encodeTypePtr type, zval *data, int style, xmlNodeP
 static zval *guess_zval_convert(zval *ret, encodeTypePtr type, xmlNodePtr data);
 static xmlNodePtr guess_xml_convert(encodeTypePtr type, zval *data, int style, xmlNodePtr parent);
 
+static zend_always_inline const char *soap_type_name(encodeTypePtr type)
+{
+	return (type && type->type_str) ? type->type_str : "unknown";
+}
+
 static encodePtr get_array_type(xmlNodePtr node, zval *array, smart_str *out_type);
 
 static xmlNodePtr check_and_resolve_href(xmlNodePtr data);
@@ -660,7 +665,7 @@ static zval *to_zval_string(zval *ret, encodeTypePtr type, xmlNodePtr data)
 		} else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) {
 			ZVAL_STRING(ret, (char*)data->children->content);
 		} else {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 		}
 	} else {
 		ZVAL_EMPTY_STRING(ret);
@@ -693,7 +698,7 @@ static zval *to_zval_stringr(zval *ret, encodeTypePtr type, xmlNodePtr data)
 		} else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) {
 			ZVAL_STRING(ret, (char*)data->children->content);
 		} else {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 		}
 	} else {
 		ZVAL_EMPTY_STRING(ret);
@@ -726,7 +731,7 @@ static zval *to_zval_stringc(zval *ret, encodeTypePtr type, xmlNodePtr data)
 		} else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) {
 			ZVAL_STRING(ret, (char*)data->children->content);
 		} else {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 		}
 	} else {
 		ZVAL_EMPTY_STRING(ret);
@@ -745,17 +750,17 @@ static zval *to_zval_base64(zval *ret, encodeTypePtr type, xmlNodePtr data)
 			whiteSpace_collapse(data->children->content);
 			str = php_base64_decode(data->children->content, strlen((char*)data->children->content));
 			if (!str) {
-				soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+				soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type));
 			}
 			ZVAL_STR(ret, str);
 		} else if (data->children->type == XML_CDATA_SECTION_NODE && data->children->next == NULL) {
 			str = php_base64_decode(data->children->content, strlen((char*)data->children->content));
 			if (!str) {
-				soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+				soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type));
 			}
 			ZVAL_STR(ret, str);
 		} else {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 		}
 	} else {
 		ZVAL_EMPTY_STRING(ret);
@@ -776,12 +781,12 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data)
 		if (data->children->type == XML_TEXT_NODE && data->children->next == NULL) {
 			whiteSpace_collapse(data->children->content);
 		} else if (data->children->type != XML_CDATA_SECTION_NODE || data->children->next != NULL) {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 			return ret;
 		}
 		content_len = strlen((char*) data->children->content);
 		if (content_len % 2 != 0) {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain an even number of hexadecimal digits", soap_type_name(type));
 			return ret;
 		}
 		str = zend_string_alloc(content_len / 2, 0);
@@ -794,7 +799,7 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data)
 			} else if (c >= 'A' && c <= 'F') {
 				ZSTR_VAL(str)[i] = (c - 'A' + 10) << 4;
 			} else {
-				soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+				soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type));
 			}
 			c = data->children->content[j++];
 			if (c >= '0' && c <= '9') {
@@ -804,7 +809,7 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data)
 			} else if (c >= 'A' && c <= 'F') {
 				ZSTR_VAL(str)[i] |= c - 'A' + 10;
 			} else {
-				soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+				soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type));
 			}
 		}
 		ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
@@ -1025,11 +1030,11 @@ static zval *to_zval_double(zval *ret, encodeTypePtr type, xmlNodePtr data)
 					} else if (strncasecmp((char*)data->children->content, "-INF", sizeof("-INF")-1) == 0) {
 						ZVAL_DOUBLE(ret, -php_get_inf());
 					} else {
-						soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+						soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type));
 					}
 			}
 		} else {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 		}
 	} else {
 		ZVAL_NULL(ret);
@@ -1058,10 +1063,10 @@ static zval *to_zval_long(zval *ret, encodeTypePtr type, xmlNodePtr data)
 					ZVAL_DOUBLE(ret, dval);
 					break;
 				default:
-					soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+					soap_error1(E_ERROR, "Encoding: Invalid value for type '%s'", soap_type_name(type));
 			}
 		} else {
-			soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+			soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 		}
 	} else {
 		ZVAL_NULL(ret);
@@ -1127,7 +1132,7 @@ static zval *to_zval_bool(zval *ret, encodeTypePtr type, xmlNodePtr data)
 	}
 	if (data->children->type != XML_TEXT_NODE || data->children->next != NULL) {
 		// TODO Convert to exception?
-		soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+		soap_error1(E_ERROR, "Encoding: Type '%s' value must contain a single text or CDATA node", soap_type_name(type));
 	}
 
 	whiteSpace_collapse(data->children->content);
@@ -3096,7 +3101,9 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP
 				}
 				smart_str_appends(&list, (char*)dummy->children->content);
 			} else {
-				soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+				soap_error2(E_ERROR,
+					"Encoding: Failed to encode list item of type '%s' for list type '%s'",
+					soap_type_name(&list_enc->details), soap_type_name(enc));
 			}
 			xmlUnlinkNode(dummy);
 			xmlFreeNode(dummy);
@@ -3138,7 +3145,9 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP
 				}
 				smart_str_appends(&list, (char*)dummy->children->content);
 			} else {
-				soap_error0(E_ERROR, "Encoding: Violation of encoding rules");
+				soap_error2(E_ERROR,
+					"Encoding: Failed to encode list item of type '%s' for list type '%s'",
+					soap_type_name(&list_enc->details), soap_type_name(enc));
 			}
 			xmlUnlinkNode(dummy);
 			xmlFreeNode(dummy);
diff --git a/ext/soap/tests/bugs/bug39832.phpt b/ext/soap/tests/bugs/bug39832.phpt
index f5742c709840..bc8e8e861ab3 100644
--- a/ext/soap/tests/bugs/bug39832.phpt
+++ b/ext/soap/tests/bugs/bug39832.phpt
@@ -26,4 +26,4 @@ $x->handle($HTTP_RAW_POST_DATA);
 ?>
 --EXPECT--
 <?xml version="1.0" encoding="UTF-8"?>
-<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Server</faultcode><faultstring>SOAP-ERROR: Encoding: Violation of encoding rules</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Server</faultcode><faultstring>SOAP-ERROR: Encoding: Invalid value for type 'integer'</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>
diff --git a/ext/soap/tests/hexbin_odd_length.phpt b/ext/soap/tests/hexbin_odd_length.phpt
index bfe84ab643db..cc1cdcbab1fd 100644
--- a/ext/soap/tests/hexbin_odd_length.phpt
+++ b/ext/soap/tests/hexbin_odd_length.phpt
@@ -34,4 +34,4 @@ try {
 }
 ?>
 --EXPECT--
-SOAP-ERROR: Encoding: Violation of encoding rules
+SOAP-ERROR: Encoding: Type 'hexBinary' value must contain an even number of hexadecimal digits
diff --git a/ext/soap/tests/scalar_error_messages.phpt b/ext/soap/tests/scalar_error_messages.phpt
new file mode 100644
index 000000000000..fde4449d6d84
--- /dev/null
+++ b/ext/soap/tests/scalar_error_messages.phpt
@@ -0,0 +1,52 @@
+--TEST--
+SOAP reports specific scalar encoding errors
+--EXTENSIONS--
+soap
+--FILE--
+<?php
+class ScalarErrorClient extends SoapClient {
+    public string $response;
+
+    public function __doRequest($request, $location, $action, $version, $one_way = false, ?string $uriParserClass = null): string {
+        return $this->response;
+    }
+}
+
+function soap_response(string $type, string $value): string {
+    return <<<XML
+<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <SOAP-ENV:Body>
+    <ns1:testResponse xmlns:ns1="urn:test">
+      <return xsi:type="$type">$value</return>
+    </ns1:testResponse>
+  </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+XML;
+}
+
+function run_case(string $label, string $type, string $value): void {
+    $client = new ScalarErrorClient(null, [
+        'location' => 'test://',
+        'uri' => 'urn:test',
+        'exceptions' => true,
+    ]);
+    $client->response = soap_response($type, $value);
+
+    try {
+        $client->__soapCall('test', []);
+    } catch (SoapFault $e) {
+        echo $label, ': ', $e->faultstring, "\n";
+    }
+}
+
+run_case('double', 'xsd:double', 'abc');
+run_case('long', 'xsd:long', 'abc');
+run_case('base64Binary node', 'xsd:base64Binary', '<value>abc</value>');
+?>
+--EXPECT--
+double: SOAP-ERROR: Encoding: Invalid value for type 'double'
+long: SOAP-ERROR: Encoding: Invalid value for type 'long'
+base64Binary node: SOAP-ERROR: Encoding: Type 'base64Binary' value must contain a single text or CDATA node
diff --git a/ext/soap/tests/soap12/T27.phpt b/ext/soap/tests/soap12/T27.phpt
index 09591b7faebd..5bb6572e3df2 100644
--- a/ext/soap/tests/soap12/T27.phpt
+++ b/ext/soap/tests/soap12/T27.phpt
@@ -25,4 +25,4 @@ include "soap12-test.inc";
 ?>
 --EXPECT--
 <?xml version="1.0" encoding="UTF-8"?>
-<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"><env:Body><env:Fault><env:Code><env:Value>env:Receiver</env:Value></env:Code><env:Reason><env:Text xml:lang="en">SOAP-ERROR: Encoding: Violation of encoding rules</env:Text></env:Reason></env:Fault></env:Body></env:Envelope>
+<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"><env:Body><env:Fault><env:Code><env:Value>env:Receiver</env:Value></env:Code><env:Reason><env:Text xml:lang="en">SOAP-ERROR: Encoding: Type 'string' value must contain a single text or CDATA node</env:Text></env:Reason></env:Fault></env:Body></env:Envelope>
diff --git a/ext/soap/tests/soap12/T58.phpt b/ext/soap/tests/soap12/T58.phpt
index 69c20a4f8670..a8619d20b298 100644
--- a/ext/soap/tests/soap12/T58.phpt
+++ b/ext/soap/tests/soap12/T58.phpt
@@ -24,4 +24,4 @@ include "soap12-test.inc";
 ?>
 --EXPECT--
 <?xml version="1.0" encoding="UTF-8"?>
-<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"><env:Body><env:Fault><env:Code><env:Value>env:Receiver</env:Value></env:Code><env:Reason><env:Text xml:lang="en">SOAP-ERROR: Encoding: Violation of encoding rules</env:Text></env:Reason></env:Fault></env:Body></env:Envelope>
+<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"><env:Body><env:Fault><env:Code><env:Value>env:Receiver</env:Value></env:Code><env:Reason><env:Text xml:lang="en">SOAP-ERROR: Encoding: Type 'int' value must contain a single text or CDATA node</env:Text></env:Reason></env:Fault></env:Body></env:Envelope>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.