I'm communicating with a SOAP API using PHP's SOAPClient class. One of the options there lets you remap the types specified in the WSDL file with your own classes:
The classmap option can be used to map some WSDL types to PHP classes. This option must be an array with WSDL types as keys and names of PHP classes as values.
I create my client as such:
$api = new SOAPClient('http://example.com/soap.wsdl', [
'location' => 'http://example.com/soap/endpoint',
'soap_version' => SOAP_1_2,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
'cache_wsdl' => WSDL_CACHE_BOTH,
'classmap' => [
'APIResultObject' => 'Result'
],
# TODO: Set for debug only?
'trace' => TRUE,
'exceptions' => TRUE
]);
This works and when I call $api->method('param')
, I get a Result
object back (instead of just a StdClass
object). The problem is that the Result::__construct()
method is never called, so some private properties of Result
are never set.
Here's what Result
is:
class DataClass{
protected $data;
function __construct(){
$this->data = ['a' => 0, 'b' => 1, 'c' => 2];
}
}
class Result extends DataClass{
public $value, $name, $quantity;
function __construct(array $values){
parent::__construct();
foreach(['value', 'name', 'quantity'] as $var){
$this->$var = isset($values[$var]) ? $values[$var] : NULL;
}
}
function getData(){
return $this->data[$this->name];
}
}
What's happening is I am doing $api->method('param')->getData()
and getting the following error:
Notice: Undefined property: Result::$data
How can I call the constructor function I need to when getting a SOAP response? I tried using __wakeup()
, but that didn't seem to work either.
P.S. I "solved" it with a small workaround, but I don't think it's ideal. Here's what I did:
function getData(){
if($this->data === NULL){
parent::__construct();
}
return $this->data[$this->name];
}
This is known behaviour (bug report).
As someoned advised in the bug report (miceleparkip at web dot de):
This is not a bug. It's quite normal.
The soap object is created on the server side. So the constructor is just called on the server.
I share her position.
A subsequent comment (php at hotblocks dot nl) in the same bug report disagrees :
The server doesn't create objects, it sends XML. The client decodes that XML and creates the objects.
While this is indisputably true from a technical pont of view, the "abstract" object is arguably created at server side. Whether it is first converted into XML then reconstructed at client side is a low-level concern that the application layer needs not be aware of.
If your application needs objects with more features than those provided by the server, I would create a local class that takes the object created by the SOAPClient
as a constructor argument:
class MySoapResultClass {
// whatever
}
class LocalApplicationClass {
public function __construct(MySoapResultClass $soapResult) {
// your local initialization code
$this->data = ['a' => 0, 'b' => 1, 'c' => 2];
// then either extract your data from $soapResult,
// or just store a reference to it
}
public function getData(){
return $this->data[$this->name];
}
}
$api = new SOAPClient(...);
$soapResult = $api->method('param');
$myUsefulObject = new LocalApplicationClass($soapResult);