我正在Hacklang中创建一个Soap web服务,在进行一些测试时,我注意到在某些情况下我会收到SoapFault-Bad Request。为了找出问题所在,我在php中编写了简化的类,并准备了适当的wsdl文件。经过一些测试,我发现在纯非hhvm php中,它工作得很好,但当我在hhvm上运行它时,它并没有像预期的那样工作——对于一些请求,它工作,而对于其他请求(只是有点不同),它抛出SoapFault。
soap.php:
<?php
if (isset($_GET['wsdl'])) {
header('Content-Type:application/xml');
echo file_get_contents('soap.wsdl');
exit(0);
}
class WebService {
public function addContacts($addContactsRequest) {
return new SoapVar(
array(
'status' => 0,
'contactsCount' => 4,
),
constant('SOAP_ENC_OBJECT'),
'AddContactsResponse',
'https://example.com/api/soap/'
);
}
}
$server = new SoapServer('soap.wsdl');
$server->setClass('WebService');
$server->handle();
soap.wsdl:
<wsdl:definitions xmlns:tns="https://example.com/api/soap/" targetNamespace="https://example.com/api/soap/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<wsdl:types>
<s:schema targetNamespace="https://example.com/api/soap/">
<s:complexType name="AddContactsRequest">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" nillable="false" name="contacts" type="tns:ContactArray">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="groups" type="tns:stringArray">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="createGroupsIfNotExist" type="s:boolean">
</s:element>
</s:sequence>
</s:complexType>
<s:complexType name="ContactArray">
<s:annotation>
<s:documentation>
Array of Contact
</s:documentation>
</s:annotation>
<s:complexContent>
<s:restriction base="soapenc:Array">
<s:attribute ref="soapenc:arrayType" wsdl:arrayType="tns:Contact[]">
</s:attribute>
</s:restriction>
</s:complexContent>
</s:complexType>
<s:complexType name="AddContactsResponse">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" nillable="false" name="status" type="s:integer">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="false" name="contactsCount" type="s:integer">
</s:element>
</s:sequence>
</s:complexType>
<s:complexType name="Contact">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" nillable="false" name="phone" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="name" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="firstName" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="lastName" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="customField1" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="customField2" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="customField3" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="customField4" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="customField5" type="s:string">
</s:element>
<s:element minOccurs="1" maxOccurs="1" nillable="true" name="customField6" type="s:string">
</s:element>
</s:sequence>
</s:complexType>
</s:schema>
</wsdl:types>
<wsdl:message name="addContactsSoapIn">
<wsdl:part name="request" type="tns:AddContactsRequest">
</wsdl:part>
</wsdl:message>
<wsdl:message name="addContactsSoapOut">
<wsdl:part name="return" type="tns:AddContactsResponse">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="WebServiceSoap">
<wsdl:operation name="addContacts">
<wsdl:input message="tns:addContactsSoapIn">
</wsdl:input>
<wsdl:output message="tns:addContactsSoapOut">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="WebServiceSoap" type="tns:WebServiceSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc">
</soap:binding>
<wsdl:operation name="addContacts">
<soap:operation soapAction="https://example.com/api/soap/addContacts">
</soap:operation>
<wsdl:input>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="https://example.com/api/soap/" parts="request">
</soap:body>
</wsdl:input>
<wsdl:output>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="https://example.com/api/soap/" parts="return">
</soap:body>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="WebService">
<wsdl:port name="WebServiceSoap" binding="tns:WebServiceSoap">
<soap:address location="https://example.com/api/soap/soap.php">
</soap:address>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
请求1(成功):
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="https://example.com/api/soap/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:addContacts>
<request xsi:type="ns1:AddContactsRequest">
<contacts SOAP-ENC:arrayType="ns1:Contact[3]" xsi:type="ns1:ContactArray">
<item xsi:type="ns1:Contact">
<phone xsi:type="xsd:string">500555444</phone>
<name xsi:nil="true"/>
<firstName xsi:nil="true"/>
<lastName xsi:nil="true"/>
<customField1 xsi:nil="true"/>
<customField2 xsi:nil="true"/>
<customField3 xsi:nil="true"/>
<customField4 xsi:nil="true"/>
<customField5 xsi:nil="true"/>
<customField6 xsi:nil="true"/>
</item>
<item xsi:type="ns1:Contact">
<phone xsi:type="xsd:string">500555777</phone>
<name xsi:type="xsd:string">Test 1</name>
<firstName xsi:nil="true"/>
<lastName xsi:nil="true"/>
<customField1 xsi:nil="true"/>
<customField2 xsi:nil="true"/>
<customField3 xsi:nil="true"/>
<customField4 xsi:nil="true"/>
<customField5 xsi:nil="true"/>
<customField6 xsi:nil="true"/>
</item>
<item xsi:type="ns1:Contact">
<phone xsi:type="xsd:string">500123456</phone>
<name xsi:type="xsd:string">testName</name>
<firstName xsi:type="xsd:string">testFirstName</firstName>
<lastName xsi:type="xsd:string">testLastName</lastName>
<customField1 xsi:type="xsd:string">testCustomField1</customField1>
<customField2 xsi:type="xsd:string">testCustomField2</customField2>
<customField3 xsi:type="xsd:string">testCustomField3</customField3>
<customField4 xsi:type="xsd:string">testCustomField4</customField4>
<customField5 xsi:type="xsd:string">testCustomField5</customField5>
<customField6 xsi:type="xsd:string">testCustomField6</customField6>
</item>
</contacts>
<groups xsi:nil="true"/>
<createGroupsIfNotExist xsi:nil="true"/>
</request>
</ns1:addContacts>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
请求2(SoapFault错误请求)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="https://example.com/api/soap/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:addContacts>
<request xsi:type="ns1:AddContactsRequest">
<contacts SOAP-ENC:arrayType="ns1:Contact[4]" xsi:type="ns1:ContactArray">
<item xsi:type="ns1:Contact">
<phone xsi:type="xsd:string">500555444</phone>
<name xsi:nil="true"/>
<firstName xsi:nil="true"/>
<lastName xsi:nil="true"/>
<customField1 xsi:nil="true"/>
<customField2 xsi:nil="true"/>
<customField3 xsi:nil="true"/>
<customField4 xsi:nil="true"/>
<customField5 xsi:nil="true"/>
<customField6 xsi:nil="true"/>
</item>
<item xsi:type="ns1:Contact">
<phone xsi:type="xsd:string">500555666</phone>
<name xsi:nil="true"/>
<firstName xsi:nil="true"/>
<lastName xsi:nil="true"/>
<customField1 xsi:nil="true"/>
<customField2 xsi:nil="true"/>
<customField3 xsi:nil="true"/>
<customField4 xsi:nil="true"/>
<customField5 xsi:nil="true"/>
<customField6 xsi:nil="true"/>
</item>
<item xsi:type="ns1:Contact">
<phone xsi:type="xsd:string">500555777</phone>
<name xsi:type="xsd:string">Test 1</name>
<firstName xsi:nil="true"/>
<lastName xsi:nil="true"/>
<customField1 xsi:nil="true"/>
<customField2 xsi:nil="true"/>
<customField3 xsi:nil="true"/>
<customField4 xsi:nil="true"/>
<customField5 xsi:nil="true"/>
<customField6 xsi:nil="true"/>
</item>
<item xsi:type="ns1:Contact">
<phone xsi:type="xsd:string">500123456</phone>
<name xsi:type="xsd:string">testName</name>
<firstName xsi:type="xsd:string">testFirstName</firstName>
<lastName xsi:type="xsd:string">testLastName</lastName>
<customField1 xsi:type="xsd:string">testCustomField1</customField1>
<customField2 xsi:type="xsd:string">testCustomField2</customField2>
<customField3 xsi:type="xsd:string">testCustomField3</customField3>
<customField4 xsi:type="xsd:string">testCustomField4</customField4>
<customField5 xsi:type="xsd:string">testCustomField5</customField5>
<customField6 xsi:type="xsd:string">testCustomField6</customField6>
</item>
</contacts>
<groups xsi:nil="true"/>
<createGroupsIfNotExist xsi:nil="true"/>
</request>
</ns1:addContacts>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
这些请求之间的唯一区别是第二个请求中多了一个<item xsi:type="ns1:Contact">...</item>
元素。
我发现这是SoapServer::handle
方法在没有参数($soap_request)的情况下调用时的问题。如果我通过$HTTP_RAW_POST_DATA
:,它可以正常工作
global $HTTP_RAW_POST_DATA;
$server->handle($HTTP_RAW_POST_DATA);