diff --git a/.github/actions/apt-x64/action.yml b/.github/actions/apt-x64/action.yml index 7ae0a88ff755..3e654ef9fdc4 100644 --- a/.github/actions/apt-x64/action.yml +++ b/.github/actions/apt-x64/action.yml @@ -63,6 +63,7 @@ runs: snmp-mibs-downloader \ freetds-dev \ unixodbc-dev \ + libsqliteodbc \ llvm \ clang \ dovecot-core \ diff --git a/NEWS b/NEWS index fb54cab61c0f..6f2e69aa8294 100644 --- a/NEWS +++ b/NEWS @@ -93,6 +93,8 @@ PHP NEWS - Reflection: . Fixed bug GH-22683 (Reflection(Class)Constant::__toString() should not warn on NAN conversions). (Khaled Alam) + . Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes). + (DanielEScherzer) - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between @@ -103,6 +105,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/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 13df0b2bbb4d..ef620369bb46 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -294,9 +294,9 @@ static zend_object *reflection_objects_new(zend_class_entry *class_type) /* {{{ } /* }}} */ -static void _const_string(smart_str *str, const char *name, zval *value, const char *indent); +static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent); static void _function_string(smart_str *str, const zend_function *fptr, const zend_class_entry *scope, const char* indent); -static void _property_string(smart_str *str, const zend_property_info *prop, const char *prop_name, const char* indent); +static void _property_string(smart_str *str, const zend_property_info *prop, const zend_string *prop_name, const char* indent); static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent); @@ -311,7 +311,8 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const /* TBD: Repair indenting of doc comment (or is this to be done in the parser?) */ if (ce->doc_comment) { - smart_str_append_printf(str, "%s%s", indent, ZSTR_VAL(ce->doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, ce->doc_comment); smart_str_appendc(str, '\n'); } @@ -517,7 +518,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const if (prop_name && ZSTR_LEN(prop_name) && ZSTR_VAL(prop_name)[0]) { /* skip all private and protected properties */ if (!zend_hash_exists(&ce->properties_info, prop_name)) { count++; - _property_string(&prop_str, NULL, ZSTR_VAL(prop_name), ZSTR_VAL(sub_indent)); + _property_string(&prop_str, NULL, prop_name, ZSTR_VAL(sub_indent)); } } } ZEND_HASH_FOREACH_END(); @@ -572,7 +573,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const /* }}} */ /* {{{ _const_string */ -static void _const_string(smart_str *str, const char *name, zval *value, const char *indent) +static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent) { const char *type = zend_zval_type_name(value); uint32_t flags = Z_CONSTANT_FLAGS_P(value); @@ -602,7 +603,7 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c smart_str_appends(str, type); smart_str_appendc(str, ' '); - smart_str_appends(str, name); + smart_str_append(str, name); smart_str_appends(str, " ] { "); if (Z_TYPE_P(value) == IS_ARRAY) { @@ -635,7 +636,9 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value); if (c->doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(c->doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, c->doc_comment); + smart_str_appendc(str, '\n'); } smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", indent, final, visibility, type, ZSTR_VAL(name)); @@ -869,9 +872,13 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze * swallowed, leading to an unaligned comment. */ if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->op_array.doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, fptr->op_array.doc_comment); + smart_str_appendc(str, '\n'); } else if (fptr->type == ZEND_INTERNAL_FUNCTION && fptr->internal_function.doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->internal_function.doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, fptr->internal_function.doc_comment); + smart_str_appendc(str, '\n'); } smart_str_appends(str, indent); @@ -979,14 +986,18 @@ static zval *property_get_default(const zend_property_info *prop_info) { } /* {{{ _property_string */ -static void _property_string(smart_str *str, const zend_property_info *prop, const char *prop_name, const char* indent) +static void _property_string(smart_str *str, const zend_property_info *prop, const zend_string *prop_name, const char* indent) { if (prop && prop->doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(prop->doc_comment)); + smart_str_appends(str, indent); + smart_str_append(str, prop->doc_comment); + smart_str_appendc(str, '\n'); } smart_str_append_printf(str, "%sProperty [ ", indent); if (!prop) { - smart_str_append_printf(str, " public $%s", prop_name); + ZEND_ASSERT(prop_name && "Properties without info must have a name provided"); + smart_str_appends(str, " public $"); + smart_str_append(str, prop_name); } else { if (prop->flags & ZEND_ACC_ABSTRACT) { smart_str_appends(str, "abstract "); @@ -1032,11 +1043,15 @@ static void _property_string(smart_str *str, const zend_property_info *prop, con smart_str_appendc(str, ' '); zend_string_release(type_str); } + smart_str_appendc(str, '$'); if (!prop_name) { const char *class_name; - zend_unmangle_property_name(prop->name, &class_name, &prop_name); + const char *prop_name_cstr; + zend_unmangle_property_name(prop->name, &class_name, &prop_name_cstr); + smart_str_appends(str, prop_name_cstr); + } else { + smart_str_append(str, prop_name); } - smart_str_append_printf(str, "$%s", prop_name); const zval *default_value = property_get_default(prop); if (default_value && !Z_ISUNDEF_P(default_value)) { @@ -1092,9 +1107,21 @@ static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *st } smart_str_appends(str, "> ]\n"); - smart_str_append_printf(str, " Current = '%s'\n", ini_entry->value ? ZSTR_VAL(ini_entry->value) : ""); + if (ini_entry->value) { + smart_str_appends(str, " Current = '"); + smart_str_append(str, ini_entry->value); + smart_str_appends(str, "'\n"); + } else { + smart_str_appends(str, " Current = ''\n"); + } if (ini_entry->modified) { - smart_str_append_printf(str, " Default = '%s'\n", ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : ""); + if (ini_entry->orig_value) { + smart_str_appends(str, " Default = '"); + smart_str_append(str, ini_entry->orig_value); + smart_str_appends(str, "'\n"); + } else { + smart_str_appends(str, " Default = ''\n"); + } } smart_str_appends(str, " }\n"); } @@ -1183,7 +1210,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module) / ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), constant) { if (ZEND_CONSTANT_MODULE_NUMBER(constant) == module->module_number) { - _const_string(&str_constants, ZSTR_VAL(constant->name), &constant->value, " "); + _const_string(&str_constants, constant->name, &constant->value, " "); num_constants++; } } ZEND_HASH_FOREACH_END(); @@ -5744,7 +5771,7 @@ ZEND_METHOD(ReflectionProperty, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ref); - _property_string(&str, ref->prop, ZSTR_VAL(ref->unmangled_name), ""); + _property_string(&str, ref->prop, ref->unmangled_name, ""); RETURN_STR(smart_str_extract(&str)); } /* }}} */ @@ -8167,7 +8194,7 @@ ZEND_METHOD(ReflectionConstant, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(const_); - _const_string(&str, ZSTR_VAL(const_->name), &const_->value, ""); + _const_string(&str, const_->name, &const_->value, ""); RETURN_STR(smart_str_extract(&str)); } diff --git a/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt new file mode 100644 index 000000000000..5002f066ff69 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionClassConstant::__toString() +--FILE-- +getDocComment() ); + +echo new ReflectionClass(Demo::class); + +?> +--EXPECTF-- +/** F%0oo */ +Constant [ public bool DEMO ] { 1 } +string(11) "/** F%0oo */" +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [1] { + /** F%0oo */ + Constant [ public bool DEMO ] { 1 } + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt new file mode 100644 index 000000000000..fc34cd95b1e3 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionClass::__toString() +--FILE-- +getDocComment() ); + +?> +--EXPECTF-- +/** F%0oo */ +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [0] { + } +} +string(11) "/** F%0oo */" diff --git a/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt new file mode 100644 index 000000000000..c43397200c82 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-22681: null bytes in name truncate ReflectionConstant::__toString() +--FILE-- +getName() ); + +?> +--EXPECTF-- +Constant [ bool F%0oo ] { 1 } +string(4) "F%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt new file mode 100644 index 000000000000..68bde0bed637 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt @@ -0,0 +1,49 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionEnum::__toString() +--FILE-- +getDocComment() ); +?> +--EXPECTF-- +Enum [ enum Demo implements UnitEnum ] { + @@ %s(%d) : eval()'d code %d-%d + + - Enum cases [1] { + /** F + Case C + } + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [1] { + Method [ static public method cases ] { + + - Parameters [0] { + } + - Return [ array ] + } + } + + - Properties [1] { + Property [ public protected(set) readonly string $name ] + } + + - Methods [0] { + } +} +string(11) "/** F%0oo */" diff --git a/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt new file mode 100644 index 000000000000..f12088b3a9e0 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-22681: null bytes in INI values truncate ReflectionExtension::__toString() +--FILE-- +getINIEntries()['arg_separator.output'] ); + +?> +--EXPECTF-- +Entry [ arg_separator.output ] + Current = 'f%0oo' + Default = '&' + } + +string(4) "f%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt new file mode 100644 index 000000000000..fdd42903c3be --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionFunctionAbstract::__toString() +--FILE-- +getDocComment() ); + +$r = new ReflectionMethod(Demo::class, 'demo'); +echo $r; +var_dump( $r->getDocComment() ); + +?> +--EXPECTF-- +/** F%0oo */ +Function [ function demo ] { + @@ %s(%d) : eval()'d code %d - %d +} +string(11) "/** F%0oo */" +/** B%0ar */ +Method [ public method demo ] { + @@ %s(%d) : eval()'d code %d - %d +} +string(11) "/** B%0ar */" diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt new file mode 100644 index 000000000000..ca218216b2c3 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionProperty::__toString() +--FILE-- +getDocComment() ); + +echo new ReflectionClass(Demo::class); + +?> +--EXPECTF-- +/** F%0oo */ +Property [ public $prop = NULL ] +string(11) "/** F%0oo */" +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [1] { + /** F%0oo */ + Property [ public $prop = NULL ] + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt new file mode 100644 index 000000000000..d8cecb0002aa --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-22681: null bytes in name truncate ReflectionProperty::__toString() +--FILE-- + true]; +$r = new ReflectionProperty($obj, "F\0oo"); +echo $r; +var_dump( $r->getDocComment() ); + +echo new ReflectionObject($obj); + +?> +--EXPECTF-- +Property [ public $F%0oo ] +bool(false) +Object of class [ class stdClass ] { + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Dynamic properties [1] { + Property [ public $F%0oo ] + } + + - Methods [0] { + } +} 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..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-- +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'); +run_case('base64Binary node', 'xsd:base64Binary', 'abc'); +?> +--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-- -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