SoapClient返回带有空stdClass对象的响应

时间:2014-06-18 13:31:51

标签: php soap-client

我遇到了PHP SoapClient的问题。我有以下WSDL https://api.mindbodyonline.com/0_5/DataService.asmx?WSDL,我正在调用SelectDataXml。它返回相应的响应,其中包含一个对象数组,这些对象对应于SQL查询将返回的数据行。我已经在Soap UI和__getLastResponse方法中检查了响应(使用print_r例程和NetBeans调试器窗口)。我可以看到完整的响应作为字符串,但是数组是用空的stdClasses构建的。我在这里看到了几个答案,建议关闭缓存来解决这个问题。我尝试了但没有。我似乎无法找到使其正确构建对象的方法。任何帮助将不胜感激。

EDITED

抽象超类(用'REMOVED'替换的敏感数据):

abstract class Mindbody_service {
    protected $_userCredentials = array ( "Username" => 'REMOVED', "Password" => 'REMOVED', 'SiteIDs' => array ( 'REMOVED' ) ),
        $_sourceCredentials = array ( "SourceName" => 'REMOVED', "Password" => 'REMOVED', 'SiteIDs' => array ( 'REMOVED' ) ),
        $_endPoint;

    public function __construct( $wsdl, $options = array () )
    {
        try {
            $this->_endPoint = new SoapClient( $wsdl, $options );
        } catch ( SoapFault $fault ) {
            echo $fault->getMessage();
        }
    }
}

具体子类:

include_once ('Mindbody_service.php');

class MindbodyDataServiceResponse {

    public $Status,
        $ErrorCode,
        $XMLDetail,
        $ResultCount,
        $CurrentPageIndex,
        $TotalPageCount,
        $Results;

    function __construct()
    {
        $Status = '';
        $ErrorCode = 0;
        $XMLDetail = '';
        $ResultCount = 0;
        $CurrentPageIndex = 0;
        $TotalPageCount = 0;
        $Results = new MindbodyDataServiceResults();
    }
}

class MindbodyDataServiceResults {

    public $Row;

    function __construct()
    {
        $row = array ();
    }
}

class Mindbody_data_service extends Mindbody_service {

    private $_query = "Very long SQL command that makes sense to the server";

    public function __construct()
    {
        $wsdl = 'https://api.mindbodyonline.com/0_5/DataService.asmx?wsdl';
        $options = array ();
        $classMap = array ( 'SelectDataXmlResult' => 'MindbodyDataServiceResponse' );

        $options [ 'trace' ] = TRUE;
        $options [ 'cache_wsdl' ] = WSDL_CACHE_NONE;
        $options [ 'compression' ] = SOAP_COMPRESSION_ACCEPT | SOAP   _COMPRESSION_GZIP;
        $options [ 'classmap' ] = $classMap;

        parent::__construct( $wsdl, $options );
    }

    public function getSomething( $since = null )
    {
        $request = array ( 'SourceCredentials' => $this->_sourceCredentials );

        $request [ 'SourceCredentials' ] = $this->_sourceCredentials;
        $request [ 'UserCredentials' ] = $this->_userCredentials;
        $request [ 'XMLDetail' ] = 'Full';
        $request [ 'PageSize' ] = 0;
        $request [ 'CurrentPageIndex' ] = 0;
        $request [ 'SelectSql' ] = $this->_conditionalQuery( $since ) . ' ORDER BY Sales.SaleDate;';

        try {
            $result = $this->_endPoint->SelectDataXml( array ( 'Request' => $request ) );
        } catch ( SoapFault $fault ) {
            echo 'ERROR: [' . $fault->faultcode . '] ' . $fault->faultstring . '.';
            exit;
        } catch ( Exception $e ) {
            echo 'ERROR: ' . $e->getMessage() . '.';
            exit;
        }

        echo '<p>';
        var_dump ($result->SelectDataXmlResult);
        echo '</p>';

        return $result->SelectDataXmlResult;
    }
}

有问题的方法是getSomething。 $ since变量是无关紧要的,它在查询中用作检索数据的截止日期(我有一个私有_conditionalQuery($ since)方法的原因)。

我在浏览器上看到的var_dump($ result-&gt; SelectDataXmlResult)的输出是:

object(MindbodyDataServiceResponse)[22]
  public 'Status' => string 'Success' (length=7)
  public 'ErrorCode' => int 200
  public 'XMLDetail' => string 'Full' (length=4)
  public 'ResultCount' => int 0
  public 'CurrentPageIndex' => int 0
  public 'TotalPageCount' => int 0
  public 'Results' => 
    object(stdClass)[23]
      public 'Row' => 
        array (size=4)
          0 => 
            object(stdClass)[24]
              ...
          1 => 
            object(stdClass)[25]
              ...
          2 => 
            object(stdClass)[26]
              ...
          3 => 
            object(stdClass)[27]
              ...

EDITED

这是服务器的回复:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <SelectDataXmlResponse xmlns="http://clients.mindbodyonline.com/api/0_5">
            <SelectDataXmlResult>
                <Status>Success</Status>
                <ErrorCode>200</ErrorCode>
                <XMLDetail>Bare</XMLDetail>
                <ResultCount>0</ResultCount>
                <CurrentPageIndex>0</CurrentPageIndex>
                <TotalPageCount>0</TotalPageCount>
                <Results>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                </Results>
            </SelectDataXmlResult>
        </SelectDataXmlResponse>
    </soap:Body>
</soap:Envelope>

由于它是客户数据,我删除了每个Row元素的实际内容,但它们是格式良好的XML元素。这是我作为空stdClass对象得到的那些元素,我应该得到stdClass对象,其成员与Row元素的子元素具有1:1的对应关系。

2 个答案:

答案 0 :(得分:1)

找到解决方案!在某些情况下,似乎SoapClient在解析响应时遇到问题。我通过重写getSomething方法来丢弃不正确的结果并使用simplexml_load_string例程来生成对象来解决这个问题。我以线程simplexml_load_string() will not read soap response with "soap:" in the tags为基础。我之前尝试过这个,但是因为我没有考虑到SOAP命名空间冲突(即我单独使用simplexml_load_string:P),因此遇到了问题。这是工作代码:

public function getSoldProducts( $since = null )
    {
        $request = array ( 'SourceCredentials' => $this->_sourceCredentials );

        $request [ 'UserCredentials' ] = $this->_userCredentials;
        $request [ 'XMLDetail' ] = 'Full';
        $request [ 'PageSize' ] = 0;
        $request [ 'CurrentPageIndex' ] = 0;
        $request [ 'SelectSql' ] = $this->_conditionalQuery( $since ) . ' ORDER BY Sales.SaleDate;';

        try {
            $this->_endPoint->SelectDataXml( array ( 'Request' => $request ) );
            $xml = simplexml_load_string($this->_endPoint->__getLastResponse ());
            $xml->registerXPathNamespace("soap", "http://www.w3.org/2003/05/soap-envelope");
            $result = $xml->xpath('//soap:Body');
        } catch ( SoapFault $fault ) {
            echo 'ERROR: [' . $fault->faultcode . '] ' . $fault->faultstring . '.';
            exit;
        } catch ( Exception $e ) {
            echo 'ERROR: ' . $e->getMessage() . '.';
            exit;
        }

        return $result[0]->SelectDataXmlResponse->SelectDataXmlResult;
    }

希望这可以帮助任何有类似问题的人。

答案 1 :(得分:0)

我遇到了类似的情况,几乎拔掉了头发,但最终我把头发整理了下来。

从我无法更改任何东西的Soap服务器上,SoapUI和Java能够获得响应,但是PHP \ SoapClient-> __ soapCall(...)的默认行为是将某些元素作为空的StdClass对象返回。 / p>

我发现的解决方案并不理想,但是可以解决它。

基本上,我需要原始响应,因此我可以自己解析它,并且\ SoapClient-> __ getLastResponse()会这样做,并且前提是我们将选项键'trace'设置为1。

所以,伪代码:

$options = [
   // (login, password, etc...)
   'trace' => 1, 
];
$client = new \SoapClient($wsdl, $options);

// (prepare the data for the call...)

$result = $client->__soapCall('save', [$callContext, $name, $inputXml]);

// Because of trace = 1 in options, this will now work.
$rawResult = $soapClient->__getLastResponse();

现在您可以完全访问响应了,只需解析它即可。

希望这对某人有帮助!