Commit a9987fa80e2 for php.net
commit a9987fa80e20249d57ba4d951a72c5b00056142f
Author: David Carlier <devnexen@gmail.com>
Date: Sun Jun 14 13:44:18 2026 +0100
ext/soap: ignore empty SOAPAction header during server dispatch.
Fix #22285
Since 8.5 SoapServer::handle() uses the HTTP SOAPAction header to select
the operation, falling back to the request body otherwise. An empty
header made every request match the first WSDL operation. Skip empty
SOAPAction headers so dispatch falls back to body inspection.
close GH-22304
diff --git a/NEWS b/NEWS
index 235699b12be..bd575259600 100644
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,8 @@ PHP NEWS
- SOAP:
. Fixed bug GH-22218 (SoapServer::handle() crash on $_SERVER not being
an array). (David Carlier / Rex-Reynolds)
+ . Fixed bug GH-22285 (Soap server requires the raw input to be passed
+ to $server->handle). (David Carlier / ndossche)
- Sqlite:
. Fix error checks for column retrieval. (ndossche)
diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index b7ed4492987..2bb49ee4c8f 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -1394,7 +1394,7 @@ PHP_METHOD(SoapServer, handle)
}
}
- if ((soap_action_z = zend_hash_str_find(Z_ARRVAL_P(server_vars), ZEND_STRL("HTTP_SOAPACTION"))) != NULL && Z_TYPE_P(soap_action_z) == IS_STRING) {
+ if ((soap_action_z = zend_hash_str_find(Z_ARRVAL_P(server_vars), ZEND_STRL("HTTP_SOAPACTION"))) != NULL && Z_TYPE_P(soap_action_z) == IS_STRING && Z_STRLEN_P(soap_action_z) > 0) {
soap_action = Z_STRVAL_P(soap_action_z);
}
}
@@ -3178,6 +3178,10 @@ static sdlFunctionPtr find_function_using_soap_action(const sdl *sdl, const char
soap_action_length -= 2;
}
+ if (UNEXPECTED(soap_action_length == 0)) {
+ return NULL;
+ }
+
/* TODO: This may depend on a particular target namespace, in which case this won't find a match when multiple different
* target namespaces are used until #45282 is resolved. */
sdlFunctionPtr function;
diff --git a/ext/soap/tests/bugs/gh22285.phpt b/ext/soap/tests/bugs/gh22285.phpt
new file mode 100644
index 00000000000..8c7e0933588
--- /dev/null
+++ b/ext/soap/tests/bugs/gh22285.phpt
@@ -0,0 +1,45 @@
+--TEST--
+GH-22285 (SoapServer dispatches to the first function when the SOAPAction header is empty)
+--CREDITS--
+Jarkko Hyvärinen
+--EXTENSIONS--
+soap
+--INI--
+soap.wsdl_cache_enabled=0
+--SKIPIF--
+<?php
+ if (php_sapi_name()=='cli') echo 'skip';
+?>
+--POST--
+<SOAP-ENV:Envelope
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:ns1="urn:TestService">
+ <SOAP-ENV:Body>
+ <ns1:goodbyeIn>
+ <name>World</name>
+ </ns1:goodbyeIn>
+ </SOAP-ENV:Body>
+</SOAP-ENV:Envelope>
+--FILE--
+<?php
+class TestWS {
+ public function hello($params) {
+ return ['message' => 'Hello ' . $params->name];
+ }
+ public function goodbye($params) {
+ return ['message' => 'Goodbye ' . $params->name];
+ }
+}
+
+$server = new SoapServer(__DIR__ . '/gh22285.wsdl', [
+ 'cache_wsdl' => WSDL_CACHE_NONE,
+ 'encoding' => 'UTF-8',
+ 'soap_version' => SOAP_1_1,
+]);
+$server->setClass('TestWS');
+$_SERVER['HTTP_SOAPACTION'] = '""';
+$server->handle();
+?>
+--EXPECTF--
+<?xml version="1.0" encoding="UTF-8"?>
+<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:TestService"><SOAP-ENV:Body><ns1:goodbyeOut><message>Goodbye World</message></ns1:goodbyeOut></SOAP-ENV:Body></SOAP-ENV:Envelope>
diff --git a/ext/soap/tests/bugs/gh22285.wsdl b/ext/soap/tests/bugs/gh22285.wsdl
new file mode 100644
index 00000000000..66b9e674907
--- /dev/null
+++ b/ext/soap/tests/bugs/gh22285.wsdl
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wsdl:definitions
+ name="TestService"
+ targetNamespace="urn:TestService"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="urn:TestService"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
+
+ <wsdl:types>
+ <xsd:schema targetNamespace="urn:TestService">
+ <xsd:element name="helloIn">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="name" type="xsd:string" />
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="helloOut">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="message" type="xsd:string" />
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="goodbyeIn">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="name" type="xsd:string" />
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="goodbyeOut">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="message" type="xsd:string" />
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ </wsdl:types>
+
+ <wsdl:message name="helloRequest">
+ <wsdl:part element="tns:helloIn" name="helloIn" />
+ </wsdl:message>
+ <wsdl:message name="helloResponse">
+ <wsdl:part element="tns:helloOut" name="helloOut" />
+ </wsdl:message>
+ <wsdl:message name="goodbyeRequest">
+ <wsdl:part element="tns:goodbyeIn" name="goodbyeIn" />
+ </wsdl:message>
+ <wsdl:message name="goodbyeResponse">
+ <wsdl:part element="tns:goodbyeOut" name="goodbyeOut" />
+ </wsdl:message>
+
+ <wsdl:portType name="TestServicePortType">
+ <wsdl:operation name="hello">
+ <wsdl:input message="tns:helloRequest" name="helloRequest" />
+ <wsdl:output message="tns:helloResponse" name="helloResponse" />
+ </wsdl:operation>
+ <wsdl:operation name="goodbye">
+ <wsdl:input message="tns:goodbyeRequest" name="goodbyeRequest" />
+ <wsdl:output message="tns:goodbyeResponse" name="goodbyeResponse" />
+ </wsdl:operation>
+ </wsdl:portType>
+
+ <wsdl:binding name="TestServiceBinding" type="tns:TestServicePortType">
+ <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
+ <wsdl:operation name="hello">
+ <wsdlsoap:operation soapAction="" />
+ <wsdl:input name="helloRequest">
+ <wsdlsoap:body use="literal" />
+ </wsdl:input>
+ <wsdl:output name="helloResponse">
+ <wsdlsoap:body use="literal" />
+ </wsdl:output>
+ </wsdl:operation>
+ <wsdl:operation name="goodbye">
+ <wsdlsoap:operation soapAction="" />
+ <wsdl:input name="goodbyeRequest">
+ <wsdlsoap:body use="literal" />
+ </wsdl:input>
+ <wsdl:output name="goodbyeResponse">
+ <wsdlsoap:body use="literal" />
+ </wsdl:output>
+ </wsdl:operation>
+ </wsdl:binding>
+
+ <wsdl:service name="TestService">
+ <wsdl:port name="TestServicePort" binding="tns:TestServiceBinding">
+ <wsdlsoap:address location="http://localhost/server.php" />
+ </wsdl:port>
+ </wsdl:service>
+
+</wsdl:definitions>