views:

1698

answers:

2

I'm using PHP 5, and want to call a webservice that is defined sort of like this:

webmethod ( AbstractBase obj );

I'm using a SoapClient (wsdl-based). The web method is expecting a subclass of AbstractBase. Yet, in PHP, calling the soap method gets me this error:

    Server was unable to read request. 
        ---> There is an error in XML document  
        ---> The specified type is abstract: name='AbstractBase'

I'm pretty sure the problem is I have to specify the type of the obj parameter in the Soap call - but I can't seem to find the magic word to make it so.

    $client = new SoapClient($WSDL, $soapSettings);
    $obj = array(
        'internal_id' => $internalId,
        'external_id' => $externald,
    );
    $params = array(
        'obj'      => $obj  // How do I say it is of type: DerivedClass?
    );

    $response = $client->webmethod($params);
A: 

I wonder if the array you are passing (instead of an object) is being cast into AbstractBase somewhere and the error is being thrown then. You might need to pass an actual object instead:

abstract class AbstractBase {
    public $internal_id;
    public $external_id;
}

class DerivedClass extends AbstractBase { }

$obj = new DerivedClass();
$obj->internal_id = $internalId;
$obj->external_id = $externalId;

$params = array(
   'obj'      => $obj  // Now an instance of DerivedClass
);

$response = $client->webmethod($params);
Tom Haigh
+1  A: 

That was a good suggestion, but it didn't work either. But it got me going in the right direction. I took your idea, created the 2 classes, and tried to explicitly set the type of the object with a SoapVar and XSD_ANYTYPE. That almost worked - but it did not set the name space (ns1:) on the fields in the class.

SO how did I eventually fix this? It took 2 things.

I discovered the wonderful XSD_ANYXML. This lets me roll my own XML for the request. By itself it failed to add the xsi namespace to the soap envelope. So I had to force one parameter to be an XSD_STRING to wake up the code that was building the request. My working code is:

$client = new SoapClient($WSDL, $soapSettings);
$myXml = "
  <ns1:obj xsi:type='ns1:DerivedClass'>
    <ns1:internal_id>$internalId</ns1:internal_id>
    <ns1:external_id>$externalId</ns1:external_id>
  </ns1:obj>
";

$params = array(
    // this is needed to force the XSI namespace in the header (there must be a better way)
    'foo' => new SoapVar('bar', XSD_STRING, 'String, 'http://www.w3.org/2001/XMLSchema-instance'),
    // this uses the XML I created
    'obj' => new SoapVar($myXml, XSD_ANYXML),
);

$response = $client->webmethod($params);
Dave C