0

I'm running into a problem with PHP SoapClient. I have the following WSDL https://api.mindbodyonline.com/0_5/DataService.asmx?WSDL and I'm calling the SelectDataXml. It returns the appropriate response, which includes an array of objects which correspond to the rows of data that the SQL query would return. I've checked the response in both Soap UI and through the __getLastResponse method (using the print_r routine and the NetBeans debugger window). I can see the complete response as a string but the array is being built with empty stdClasses. I've seen several answers here suggesting that caching be turned off to work around this. I tried that but nothing. I can't seem to find a way to make it build the objects correctly. Any help would be appreciated.

EDITED

The abstract superclass (sensitive data replaced with '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();
        }
    }
}

The concrete subclass:

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;
    }
}

The method in question is getSomething. The $since variable is irrelevant, it's used in the query as a cutoff date for the data being retrieved (reason for which I have a private _conditionalQuery( $since ) method).

The output of var_dump ($result->SelectDataXmlResult) I'm getting on my browser is:

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

Here's the Server's response:

<?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>

I've removed the actual content of each Row element due to it being customer data, but they are well formed XML elements. It's those elements that I'm getting as empty stdClass objects where I should be getting stdClass objects with members that have a 1:1 correspondence to the Row element's children.

14
  • you need an example about how to call a method via soapclient? Commented Jun 18, 2014 at 13:39
  • No, that I'm already doing just fine. The problem, as I've stated above, is that the call's returning empty objects where it shouldn't. Commented Jun 18, 2014 at 13:46
  • so could you paste your code here? Commented Jun 18, 2014 at 13:47
  • Added The code, plus the output of a var_dump :) Commented Jun 18, 2014 at 14:22
  • 1
    Impossible, I have no access to the server except as consumer of the service. In any case, I already solved the problem. It would seem that PHP's SoapClient internal parser fails silently under certain conditions (which I happen to hit). What I did was discard the object I was getting and requesting the raw XML response, which I passed to the simplexml_load_string routine to get an object I could actually use. I'll post the code later on as I'm currently unable to because of StackOverflow's restrictions. Commented Jun 18, 2014 at 20:19

2 Answers 2

2

I faced similar situation, and almost pulled my hair out, but eventually I sorted it out.

From a Soap server where I can't change anything, SoapUI and Java was able to get response, but the default behaviour with PHP \SoapClient->__soapCall(...) was returning some elements as empty StdClass objects.

The solution I found is not ideal, but allows to get it done.

Basically, I wanted the raw response, so I could parse it myself, and \SoapClient->__getLastResponse() will do just that, if and only if we set options key 'trace' to 1.

So, pseudo-code:

$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();

Now you have full access to the response, just have to parse it.

Hope this helps someone!!

Sign up to request clarification or add additional context in comments.

Comments

1

Solution found! It seems SoapClient has problems parsing responses in some cases. I solved this by rewriting the getSomething method to discard the incorrect result and use the simplexml_load_string routine to generate the object. I based this on the thread simplexml_load_string() will not read soap response with "soap:" in the tags. I had tried this before but was running into a brick wall as I hadn't taken into account the SOAP namespace conflict (i.e. I was using simplexml_load_string alone :P). Here's the working code:

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;
    }

Hope this helps anyone with similar problems.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.