为什么会出现SOAP-ERROR:编码:带有嵌套复杂类型的WSDL中的对象没有“税”?

时间:2018-08-02 14:57:32

标签: php arrays soap

我必须使用PHP使用SOAP Web服务,并且出现标题错误。

wsdl的(部分)结构是:

<xs:element name="Obligations" type="tns:ObligationsType"/>
<xs:complexType name="ObligationsType">
<xs:sequence>

<xs:element maxOccurs="99" name="Taxes" type="tns:TaxesType"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="TaxesType">
<xs:sequence>
<xs:element name="tax">
<xs:simpleType>
<xs:restriction base="xs:int">
<xs:maxInclusive value="9999"/>
<xs:minInclusive value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="amount">
<xs:simpleType>
<xs:restriction base="xs:double">
<xs:minInclusive value="0.01"/>
<xs:maxInclusive value="9999999999.99"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>

我发送的关联数组:

    $params = array(
            'token' => $TOKEN,
            'sign' => $SIGN,
            'paymentEntity' => 1001,
            'form' => array(
                'formNumber' => 6042,
                'idPaymentType' => 951,
                'Obligations' => array (
                                     array( 
                                         'Taxes' => array(
                                                        'tax' => 6041,
                                                        'amount' => 602.0 
                                                         )
                                           )
                                        )
                             )              
                );

我尝试对类进行操作,但遇到相同的错误。问题出在“义务”对象上。

我尝试过这样的嵌套:

  

'Obligations'=> array('Taxes'=> array('tax'=> 1,'amount'=> 1.0))我得到了无法识别的字段义务

     

'Obligations'=> array('tax'=> 1,'amount'=> 1.0)我得到的对象没有'Taxes'属性

     

'义务'=>数组(array('Taxes'=> array('tax'=> 1,'amount'=> 1.0)))我得到对象没有'tax'属性

     

'Obligations'=> array('Taxes'=> array('tax'=> 1))我得到对象没有'amount'属性

     

'Obligations'=> array('Taxes'=> array('amount'=> 1))我发现对象没有'tax'属性

最后一个请求xml

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="...">
<SOAP-ENV:Body>
<ns1:createForm>
<ns1:token>********</ns1:token>
<ns1:sign>********</ns1:sign>
<ns1:paymentEntity>1001</ns1:paymentEntity>
<ns1:form>
    <ns1:formNumber>6042</ns1:formNumber>
    <ns1:idPaymentType>951</ns1:idPaymentType>
    <ns1:Obligations>
        <ns1:Taxes>
            <tax>6041</tax>
            <amount>602.0</amount>
        </ns1:Taxes>
    </ns1:Obligations>
</ns1:form>
</ns1:createForm>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

结构

"createFormResponse createForm(createForm $parameters)" [2]=> string(74)
"struct createForm { string token; string sign; int paymentEntity; FormType form; }" 
"struct FormType { formNumber formNumber; idPaymentType idPaymentType; Obligations ObligationsType; }"
"struct ObligationsType { TaxesType Taxes; }"
"struct TaxesType { tax tax; amount amount; }" 

我有一个请求示例,它与我获得的示例相同。 它只有一个注释

  

税收。此属性是列表类型。

     

税额。此属性是列表类型。

但是它已经在wsdl中标记了。

任何建议,我将不胜感激。问候

1 个答案:

答案 0 :(得分:0)

在处理复杂类型时最好使用对象。它更干净,更易于操作。以下示例提示如何使用php处理复杂类型。

/**
 * Entity from complex type ObligationsType
 * @see xsd:complexType "ObligationsType"
 */ 
class ObligationsType
{
    /**
     * Is an array because the containing 'TaxtesType' can appear up to 99 times
     * @var \ArrayObject
     */
    protected $Taxes;

    public function getTaxes() : \ArrayObject
    {
        return $this->Taxes;
    }

    public function setTaxes(TaxesType $taxes) : ObligationsType
    {
        if ($this->Taxes === null) {
            $this->Taxes = new \ArrayObject();
        }

        $this->Taxes->append($taxes);
        return $this;
    }

    public function encode() :\SoapVar
    {
        $container = new \ArrayObject();
        foreach (get_obect_vars($this) as $property) {
            if ($property instanceof \ArrayObject) {
                foreach ($property as $element) {
                    $container->append($element);
                }
            } else {
                $container->append($property);
            }
        }

        return new \SoapVar(
            $container, 
            SOAP_ENC_OBJ, 
            null, 
            null, 
            'Obligations',
            'http://www.example.com/namespace/tns'
        );
    }
}

class TaxesType
{
    protected $tax;

    protected $amount;

    public function getTax() : int
    {
        return $this->tax;
    }

    public function setTax(int $tax) : TaxesType
    {
        $this->tax = $tax;
        return $this;
    }

    public function getAmount() : float
    {
        return $this->amount;
    }

    public function setAmount(float $amount) : TaxesType
    {
        $this->amount = $amount;
        return $this;
    }

    public function encode() : \SoapVar
    {
        return new \SoapVar(
            $this,
            SOAP_ENC_OBJ,
            null,
            null,
            'Taxes',
            'http://www.example.com/namespace/tns'
        );
    }
}

因此,现在我们在wsdl als PHP实体类中获得了两种复杂的类型。该类唯一要做的就是保存我们的数据并对其进行编码,以便通过soap客户端类发送数据。

如何使用它?

只需告诉您的肥皂客户端类,您就有用于预期数据的类。出于此目的,肥皂客户端可以选择classmap

$client = new \SoapClient(
    $path_to_your_wsdl,
    [
        'class_map' => [
            'ObligationsType' => ObligationsType::class,
            'TaxesType' => TaxesType::class,
        ],
        'exceptions' => true,
        'trace' => true,
    ],
);

$tax1 = (new TaxesType())
    ->setTax(19)
    ->setAmount(49.99)
    ->encode();

$tax2 = (new TaxesType())
    ->setTax(7)
    ->setAmount(29.49)
    ->encode();

$obligations = (new ObligationsType())
    ->setTaxes($tax1)
    ->setTaxes($tax2)
    ->encode();

$result = $client->YourWebserviceFunction($obligations);

这段简单的代码片段应说明xsd复杂类型如何与php soap客户端一起使用。形象地讲,每个复杂类型都是一个类,其中包含为该复杂类型描述的成员。这种理解使处理肥皂请求和响应中的数据变得容易。此外,每个Web服务功能都有在wsdl或任何绑定的xsd文件中描述的返回类型。找到此返回类型,并从中返回一个php类,并在soap客户端的classmap选项中提及它。 soap客户端可以轻松地在classmap选项中提到的返回类型类中对请求的返回进行水化处理。

如您所见,SOAP和PHP可以很好地协同工作。

请始终记住:上面显示的代码未经测试。请不要在生产中使用此代码。

编辑:如何在try / catch块中获取最后一个请求

正如您在此答案下的评论中提到的那样,您无法获得上一个请求。我将在接下来的几行中向您展示如何实现。只需将soap client init包装在try catch块中。如果soap客户端已初始化,则也可以在catch块中对其进行访问。

try {
    $client = new \SoapClient(
        $wsdl_path,
        $options
    );

    // call of your webservice methods here
    ...

} catch (\SoapFault $e) {
    // get the last request in the catch block
    if ($client !== null) {
        echo "<pre>";
        var_dump(htmlentities($client->__getLastRequest()));
        echo "</pre>";
    }
}

别忘了将trace选项设置为true。如果没有,您将无法调用soap客户端的__getLastRequest()方法。

请使用上一个xml请求编辑您的问题。