diff --git a/NEWS b/NEWS
index 1c1cf6acae6..44c6a11c17c 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,8 @@ PHP NEWS
. Fixed bug #73182 (PHP SOAPClient does not support stream context HTTP
headers in array form). (nielsdos)
. Fixed bug #62900 (Wrong namespace on xsd import error message). (nielsdos)
+ . Fixed bug GH-15711 (SoapClient can't convert BackedEnum to scalar value).
+ (nielsdos)
12 Sep 2024, PHP 8.3.12
diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c
index 790a7029a63..fda34b080e3 100644
--- a/ext/soap/php_encoding.c
+++ b/ext/soap/php_encoding.c
@@ -826,11 +826,46 @@ static zval *to_zval_hexbin(zval *ret, encodeTypePtr type, xmlNodePtr data)
static zend_string *get_serialization_string_from_zval(zval *data)
{
switch (Z_TYPE_P(data)) {
+ case IS_OBJECT:
+ if (Z_OBJCE_P(data)->ce_flags & ZEND_ACC_ENUM) {
+ if (UNEXPECTED(Z_OBJCE_P(data)->enum_backing_type == IS_UNDEF)) {
+ zend_value_error("Non-backed enums have no default serialization");
+ return zend_empty_string;
+ } else {
+ zval *value = zend_enum_fetch_case_value(Z_OBJ_P(data));
+ return zval_get_string_func(value);
+ }
+ }
+ ZEND_FALLTHROUGH;
default:
return zval_get_string_func(data);
}
}
+static zend_long get_serialization_long_from_zval(zval *data)
+{
+ switch (Z_TYPE_P(data)) {
+ case IS_OBJECT:
+ if (Z_OBJCE_P(data)->ce_flags & ZEND_ACC_ENUM) {
+ if (UNEXPECTED(Z_OBJCE_P(data)->enum_backing_type != IS_LONG)) {
+ if (Z_OBJCE_P(data)->enum_backing_type == IS_UNDEF) {
+ zend_value_error("Non-backed enums have no default serialization");
+ } else {
+ zend_value_error("String-backed enum cannot be serialized as int");
+ }
+ return 0;
+ } else {
+ zval *value = zend_enum_fetch_case_value(Z_OBJ_P(data));
+ ZEND_ASSERT(Z_TYPE_P(value) == IS_LONG);
+ return Z_LVAL_P(value);
+ }
+ }
+ ZEND_FALLTHROUGH;
+ default:
+ return zval_get_long(data);
+ }
+}
+
static xmlNodePtr to_xml_string(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
{
xmlNodePtr ret, text;
@@ -1056,7 +1091,7 @@ static xmlNodePtr to_xml_long(encodeTypePtr type, zval *data, int style, xmlNode
snprintf(s, sizeof(s), "%0.0F",floor(Z_DVAL_P(data)));
xmlNodeSetContent(ret, BAD_CAST(s));
} else {
- zend_string *str = zend_long_to_str(zval_get_long(data));
+ zend_string *str = zend_long_to_str(get_serialization_long_from_zval(data));
xmlNodeSetContentLen(ret, BAD_CAST(ZSTR_VAL(str)), ZSTR_LEN(str));
zend_string_release_ex(str, 0);
}
diff --git a/ext/soap/tests/gh15711.phpt b/ext/soap/tests/gh15711.phpt
new file mode 100644
index 00000000000..a49ff280fee
--- /dev/null
+++ b/ext/soap/tests/gh15711.phpt
@@ -0,0 +1,88 @@
+--TEST--
+GH-15711 (SoapClient can't convert BackedEnum to scalar value)
+--EXTENSIONS--
+soap
+--INI--
+soap.wsdl_cache_enabled=0
+--FILE--
+ ['book' => 'book']]);
+
+echo "--- Test with backed enum ---\n";
+
+$book = new stdClass();
+$book->base64 = StringBackedEnum::First;
+$book->string = StringBackedEnum::Second;
+$book->any = StringBackedEnum::Third;
+$book->hexbin = StringBackedEnum::Fourth;
+$book->nmtokens = StringBackedEnum::Fifth;
+$book->integer = IntBackedEnum::First;
+$book->short = IntBackedEnum::Second;
+
+try {
+ $client->dotest($book);
+} catch (Throwable) {}
+
+echo "--- Test with non-backed enum ---\n";
+
+$book = new stdClass();
+$book->base64 = NonBackedEnum::First;
+$book->string = NonBackedEnum::First;
+$book->any = NonBackedEnum::First;
+$book->hexbin = NonBackedEnum::First;
+$book->nmtokens = NonBackedEnum::First;
+$book->integer = NonBackedEnum::First;
+$book->short = NonBackedEnum::First;
+
+try {
+ $client->dotest($book);
+} catch (ValueError $e) {
+ echo "ValueError: ", $e->getMessage(), "\n";
+}
+
+echo "--- Test with mismatched enum backing type ---\n";
+
+$book->integer = StringBackedEnum::First;
+$book->short = StringBackedEnum::First;
+try {
+ $client->dotest($book);
+} catch (ValueError $e) {
+ echo "ValueError: ", $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+--- Test with backed enum ---
+
+QmFja2luZ1ZhbHVlMQ==BackingValue2ThirdBackingValue34261636B696E6756616C756534BackingValue512
+--- Test with non-backed enum ---
+ValueError: Non-backed enums have no default serialization
+--- Test with mismatched enum backing type ---
+ValueError: String-backed enum cannot be serialized as int
diff --git a/ext/soap/tests/gh15711.wsdl b/ext/soap/tests/gh15711.wsdl
new file mode 100644
index 00000000000..b49ef987b82
--- /dev/null
+++ b/ext/soap/tests/gh15711.wsdl
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+