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