为什么PHP 7.x SoapServer为非重复数组返回XML引用(例如href =“#ref1”)?

I have a problem using PHP's built-in SoapServer to return a response containing two different arrays. PHP thinks my fruit and vegetables arrays are duplicates (they are not). The response uses a Wrapper class making use of PHP's __get() magic overloading method, which seems to be part of the problem.

My code works perfectly fine in PHP versions 5.3, 5.4, 5.5 and 5.6 and generates a correct SOAP XML response:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test-uri/">
<SOAP-ENV:Body>
    <ns1:fooResponse>
        <result>
            <fruit>
                <item>apple</item>
                <item>orange</item>
                <item>banana</item>
            </fruit>
            <vegetables>
                <item>carrots</item>
                <item>broccoli</item>
            </vegetables>
        </result>
    </ns1:fooResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The exact same code in PHP versions 7.0, 7.1 and 7.2RC2 produces the following unexpected XML, which contains a reference in the vegetables collection, pointing back to the fruit collection:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test-uri/">
<SOAP-ENV:Body>
    <ns1:fooResponse>
        <result>
            <fruit id="ref1">
                <item>apple</item>
                <item>orange</item>
                <item>banana</item>
            </fruit>
            <vegetables href="#ref1"/>
        </result>
    </ns1:fooResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

My questions are, why does PHP 7.x think fruits and vegetables are exactly the same, causing it to return an XML reference? Why does the behavior change in the 7.x versions? How can I continue using a wrapper class and the __get() method and achieve the same response as earlier versions of PHP?

Here is my verifiable example, self-contained in one PHP file. It can be run directly from the command line (no web server required):

$wsdl  = <<<WSDL
<?xml version="1.0" encoding="utf-8"?>
<definitions name="SoapArrayTest"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
                  xmlns:tns="http://test-uri/"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns="http://schemas.xmlsoap.org/wsdl/"
                  targetNamespace="http://test-uri/"
  >
  <types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://test-uri/">
      <complexType name="Baz">
        <sequence>
          <element name="fruit" type="tns:StringArray"/>
          <element name="vegetables" type="tns:StringArray"/>
        </sequence>
      </complexType>
      <element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="BazElement" type="tns:Baz"/>
      <complexType name="StringArray">
        <sequence>
          <element name="item" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
        </sequence>
      </complexType>
      <element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="StringArrayElement" type="tns:StringArray"/>
    </schema>
  </types>
  <message name="fooRequest">
  </message>
  <message name="fooResponse">
    <part name="result" type="tns:Baz"/>
  </message>
  <portType name="TestPortType">
    <operation name="foo">
      <input message="tns:fooRequest"/>
      <output message="tns:fooResponse"/>
    </operation>
  </portType>
  <binding  name="TestBinding" type="tns:TestPortType">
    <soap:binding  style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation  name="foo">
      <soap:operation  soapAction="#foo" style="rpc"/>
      <input />
      <output >
        <soap:body  parts="result" use="literal" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </output>
    </operation>
  </binding>
  <service  name="TestService">
    <port  name="TestPort" binding="tns:TestBinding">
      <soap:address location="http://example.com"/>
    </port>
  </service>
</definitions>
WSDL;

class Wrapper
{
  private $object;

  public function __construct($object)
  {
    $this->object = $object;
  }

  public function __get($property)
  {
    $value = $this->object->$property;
    return $value;
  }
}

function foo()
{
  $baz = new stdClass();
  $arr1 = array( "apple", "orange", "banana");
  $baz->fruit = $arr1;
  $arr2 = array("carrots", "broccoli");
  $baz->vegetables = $arr2;
  $bar = new Wrapper($baz);
  return $bar;
}

$fname = tempnam (__DIR__, "wsdl");
$f = fopen($fname,"w");
fwrite($f,$wsdl);
fclose($f);

$server = new SoapServer($fname, array('cache_wsdl' => WSDL_CACHE_NONE, 'soap_version' => SOAP_1_1));
$server->addFunction("foo");

$soapRequest = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="blah" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <ns1:foo>
    </ns1:foo>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML;


$server->handle($soapRequest);

Updates:

  • The solution to this similar question PHP SoapClient creating XML references for identical elements, makes it unacceptable for service doesn't work. My elements are not identical. If my elements were identical I would be OK with the XML references.
  • Changing the WSDL so that it contains two separate StringArray structures does not work
  • I tried adding 'item' to my arrays like this (it didn't work):

    function foo()
    {
      $baz = new stdClass();
    
      $fruits = array( "item" => array("apple", "orange", "banana"));
      $baz->fruit = $fruits;
    
      $veggies = array("item" => array("carrots", "broccoli"));
      $baz->vegetables = $veggies;
    
      $bar = new Wrapper($baz);
      return $bar;
    }
    
  • I can fix the issue in PHP 7.x by using objects with item property like this:

    function foo()
    {
      $baz = new stdClass();
    
      $fruits = new stdClass();
      $fruits->item = array("apple", "orange", "banana");
      $baz->fruit = $fruits;
    
      $veggies = new stdClass();
      $veggies->item = array("carrots", "broccoli");
      $baz->vegetables = $veggies;
    
      $bar = new Wrapper($baz);
      return $bar;
    }
    

    However I am not satisfied with this. I still don't know why using arrays won't work in PHP 7.x.

dongliang1223
dongliang1223 不,我还没有提交错误报告。我怀疑这是一个错误,因为7.x已经出现了很长时间。肯定会被现在发现?
2 年多之前 回复
doufan9395
doufan9395 你有没有向PHP提交错误报告?
2 年多之前 回复
douci1615
douci1615 肯定是因为它更宽容,但PHP有许多未记录的特征。你试过我的其他答案吗?我认为这是原因,你能测试一下吗?
接近 3 年之前 回复
duannao8450
duannao8450 如果我使用stdClass或带有classmap选项的类,问题就会消失(请参阅问题中的最新更新)。问题是当使用数组而不是对象时-我需要知道为什么它在5.x中工作但在7.x中不工作。也许我的代码一直被打破,PHP5.x更宽容。
接近 3 年之前 回复
dsj8000
dsj8000 PHP有一个定义类映射的选项,你使用它吗?$classmap=array('Meeting'=>'Meeting');$server=newSoapServer('soap.wsdl',array('classmap'=>$classmap));$服务器->setClass(“MySoapServer”);$服务器->手柄();
接近 3 年之前 回复

1个回答



您的xsd似乎不一定是它需要的样子。 要将一个元素定义为序列(数组),您需要在Baz定义中使用类似的内容:</ p>

 &lt; xs:element name =“fruits”&gt; 
&lt; xs :complexType&gt;
&lt; xs:sequence&gt;
&lt; xs:element name =“fruit”maxOccurs =“unbounded”/&gt;
&lt; / xs:sequence&gt;
&lt; / xs:complexType&gt;
</ code> </ pre>

</ p>

 &lt; xs:element name =“vegetables”&gt; 
&lt; xs:complexType&gt;
&lt; xs:sequence&gt;
&lt; xs:element name =“vegetable maxOccurs =”unbounded“/&gt;
&lt; / xs:sequence&gt;
&lt; / xs:complexType&gt;
</ code> </ pre>

</ p>

参考 </ p>
</ div>

展开原文

原文

Your xsd seems not exactly how it needs to be. To define one element as a sequence (array), you need something like this inside Baz definition:

<xs:element name="fruits">
<xs:complexType>
  <xs:sequence>
    <xs:element name="fruit" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

<xs:element name="vegetables">
<xs:complexType>
  <xs:sequence>
    <xs:element name="vegetable maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

Reference

douche5961
douche5961 它不起作用。 这是同样的问题,除了返回的XML略有不同:<ns1:fooResponse> <result> <fruits id =“ref1”> <fruit> apple </ fruit> <fruit> orange </ fruit> <fruit> banana < / fruit> </ fruits> <vegetables href =“#ref1”/> </ result> </ ns1:fooResponse>
接近 3 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐