diff --git a/NEWS b/NEWS
index 190c6c16a935..0301fe46398b 100644
--- a/NEWS
+++ b/NEWS
@@ -80,6 +80,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 031b6751d853..5663e7ac81f2 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--
-SOAP-ENV:ServerSOAP-ERROR: Encoding: Violation of encoding rules
+SOAP-ENV:ServerSOAP-ERROR: Encoding: Invalid value for type 'integer'
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..6384b1ae40f7
--- /dev/null
+++ b/ext/soap/tests/scalar_error_messages.phpt
@@ -0,0 +1,50 @@
+--TEST--
+SOAP reports specific scalar encoding errors
+--EXTENSIONS--
+soap
+--FILE--
+response;
+ }
+}
+
+function soap_response(string $type, string $value): string {
+ return <<
+
+
+
+ $value
+
+
+
+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');
+?>
+--EXPECT--
+double: SOAP-ERROR: Encoding: Invalid value for type 'double'
+long: SOAP-ERROR: Encoding: Invalid value for type 'long'
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--
-env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules
+env:ReceiverSOAP-ERROR: Encoding: Type 'string' value must contain a single text or CDATA node
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--
-env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules
+env:ReceiverSOAP-ERROR: Encoding: Type 'int' value must contain a single text or CDATA node