Consuming SOAP complexType webservice with PHP

I recently had a client request that I communicate with a webservice via SOAP in order to do some credit card authorization for an Asterisk project they were implementing. After a couple of days of reading several posts I found via Google (which funny enough weren’t exactly what I was looking for, but gave me JUST enough information to finally start putting it all together), I had something that worked.

In order to potentially save someone the hassle of having to figure out how to consume a complexType in SOAP via PHP (and not using NuSoap as many of the posts pointed out), I’m writing this down. As with many of my posts, there is probably just enough information here that will be useful for me in the future should I need to do this again. If this helps someone else out, then great!

Note: I am not a programmer. What I’m showing may be entirely the WRONG approach to solving this issue, but it solves it for me. Feel free to provide better examples by commenting on this article. In addition, there is the potential I may be using the wrong terminology. Please feel free to correct me, and I will then update the article to reflect anything deemed to be incorrect.

First, you need to install the php-soap module for your PHP installation. This could require you to recompile PHP with SOAP support, or if you’re using a package based distro (like me), you may just need to do something like:

# yum install php-soap

In order to give you just enough of a reference point to figure out what I’m trying to do here, the following snippet of XML code is from the webservice I’m trying to consume. It is the complextype which I need to perform request against, and defines how the object should be passed. I’ll then show the result. This is *not* the entire WSDL.

First we’re looking at the element named ‘Purchase’. This element contains a complexType object named oPurchaseRequest and receives a request of the type PurchaseRequestType.

<s:element name="Purchase">
 <s:complexType>
   <s:sequence>
     <s:element minOccurs="0" maxOccurs="1" name="oPurchaseRequest" type="tns:PurchaseRequestType"/>
   </s:sequence>
 </s:complexType>
</s:element>

The structure of the PurchaseRequestType is as follows. This is how we need to structure our complexType.

<s:complexType name="PurchaseRequestType">
 <s:sequence>
   <s:element minOccurs="0" maxOccurs="1" name="MembershipNumber" type="s:string"/>
   <s:element minOccurs="0" maxOccurs="1" name="SessionID" type="s:string"/>
   <s:element minOccurs="0" maxOccurs="1" name="Password" type="s:string"/>
   <s:element minOccurs="0" maxOccurs="1" name="Gender" type="s:string"/>
   <s:element minOccurs="1" maxOccurs="1" name="RatePlanID" type="s:int"/>
   <s:element minOccurs="0" maxOccurs="1" name="CCNumber" type="s:string"/>
   <s:element minOccurs="1" maxOccurs="1" name="CCExpireMonth" type="s:int"/>
   <s:element minOccurs="1" maxOccurs="1" name="CCExpireYear" type="s:int"/>
   <s:element minOccurs="1" maxOccurs="1" name="CCCode" type="s:int"/>
   <s:element minOccurs="1" maxOccurs="1" name="Zip" type="s:int"/>
  </s:sequence>
 </s:complexType>

The magic really happens in the following code. It is how I built the object in order to be consumed by the webservice. It works by first creating a new standard class, and then creating another standard class inside the oPurchaseRequest object (which is the name of the complexType that the Purchase element is expecting). Then you build the oPurchaseRequest by adding the elements outlined by the PurchaseRequestType complexType, and assigning values to them.

$search_query = new StdClass();
$search_query->oPurchaseRequest = new StdClass();
$search_query->oPurchaseRequest->MembershipNumber = $MembershipNumber;
$search_query->oPurchaseRequest->SessionID = $SessionID;
$search_query->oPurchaseRequest->Password = $Password;
$search_query->oPurchaseRequest->Gender = $Gender;
$search_query->oPurchaseRequest->RatePlanID = $RatePlanID;
$search_query->oPurchaseRequest->CCNumber = $CCNumber;
$search_query->oPurchaseRequest->CCExpireMonth = $CCExpireMonth;
$search_query->oPurchaseRequest->CCExpireYear = $CCExpireYear;
$search_query->oPurchaseRequest->CCCode = $CCCode;
$search_query->oPurchaseRequest->Zip = $Zip;

The entire snippet of code is available below.

// We can take in our arguments from the console if we're using PHP CLI
$MembershipNumber = $argv[1];
$SessionID = $argv[2];
$Password = $argv[3];
$Gender = $argv[4];
$RatePlanID = $argv[5];
$CCNumber = $argv[6];
$CCExpireMonth = $argv[7];
$CCExpireYear = $argv[8];
$CCCode = $argv[9];
$Zip = $argv[10];

// Create the object we'll pass back over the SOAP interface. This is the MAGIC!
$search_query = new StdClass();
$search_query->oPurchaseRequest = new StdClass();
$search_query->oPurchaseRequest->MembershipNumber = $MembershipNumber;
$search_query->oPurchaseRequest->SessionID = $SessionID;
$search_query->oPurchaseRequest->Password = $Password;
$search_query->oPurchaseRequest->Gender = $Gender;
$search_query->oPurchaseRequest->RatePlanID = $RatePlanID;
$search_query->oPurchaseRequest->CCNumber = $CCNumber;
$search_query->oPurchaseRequest->CCExpireMonth = $CCExpireMonth;
$search_query->oPurchaseRequest->CCExpireYear = $CCExpireYear;
$search_query->oPurchaseRequest->CCCode = $CCCode;
$search_query->oPurchaseRequest->Zip = $Zip;

// setup some SOAP options
echo "Setting up SOAP options\n";
$soap_options = array(
        'trace'       =&gt; 1,     // traces let us look at the actual SOAP messages later
        'exceptions'  =&gt; 1 );

// configure our WSDL location
echo "Configuring WSDL\n";
$wsdl = "https://locationofservices.tld/asterisk.asmx?WSDL";

// Make sure the PHP-Soap module is installed
echo "Checking SoapClient exists\n";
if (!class_exists('SoapClient'))
{
        die ("You haven't installed the PHP-Soap module.");
}

// we use the WSDL file to create a connection to the web service
echo "Creating webservice connection to $wsdl\n";
$webservice = new SoapClient($wsdl,$soap_options);

echo "Attempting Purchase\n";
try {
        $result = $webservice-&gt;Purchase($search_query);

        // save our results to some variables
        $TransactionID = $result-&gt;PurchaseResult-&gt;TransactionID;
        $ResponseCode = $result-&gt;PurchaseResult-&gt;ResponseCode;
        $ResponseDetail = $result-&gt;PurchaseResult-&gt;ResponseDetail;
        $AddMinutes = $result-&gt;PurchaseResult-&gt;AddMinutes;

        // perform some logic, output the data to Asterisk, or whatever you want to do with it.

} catch (SOAPFault $f) {
        // handle the fault here
}

echo "Script complete\n\n";

You’ll notice that our results are passed back in the ‘try’ statement. We assign the values passed back to some variables we could use. The layout of the result is similar to the request. It is laid out as follows in the WSDL.

<s:element name="PurchaseResponse">
 <s:complexType>
  <s:sequence>
   <s:element minOccurs="0" maxOccurs="1" name="PurchaseResult" type="tns:PurchaseResponseType"/>
  </s:sequence>
 </s:complexType>
</s:element>

<s:complexType name="PurchaseResponseType">
 <s:sequence>
  <s:element minOccurs="0" maxOccurs="1" name="TransactionID" type="s:string"/>
  <s:element minOccurs="1" maxOccurs="1" name="ResponseCode" type="tns:ResponseCodes"/>
  <s:element minOccurs="0" maxOccurs="1" name="ResponseDetail" type="s:string"/>
  <s:element minOccurs="1" maxOccurs="1" name="AddMinutes" type="s:int"/>
 </s:sequence>
</s:complexType>

Hope that helps!

13 Responses to “Consuming SOAP complexType webservice with PHP”


  1. 1 MiguelColombia 2009/08/05 at 6:35 pm

    Hey, Leif, it appears to be that we got same type of project (mine is money transfer via IVR made in Asterisk with authorization via SOAP in PHP), so you saved me a lot of time researching. Thanks a lot.

  2. 3 AJ 2009/08/28 at 3:37 pm

    Hello Lief,

    That snippet was very helpful to me as well although I feel like you do:

    if I write a WSDL that imports schema definitions in, shouldn’t I be able to use those definitions in the schemas to declare the needed (top level complexType) variables rather than just creating the schema definitions all over again in PHP?

    I’m still searching if this is possible with PHP SOAP extension.

    Have you learned anything new?

    • 4 Leif Madsen 2009/08/29 at 9:46 am

      I haven’t learned anything new as I haven’t had to work with the AGI since I wrote it. If you learn anything new about a better way to do this, please do let me know!

      • 5 AJ 2009/08/31 at 9:47 am

        Thanks, sure thing. I do know that the schemas I imported via the WSDL don’t show up as any kind of PHP classes, objects or type definitions as you’d expect.

        I guess I’ll have to do validation of the schemas with PHP DOM or some other PHP XML library.

        I’ll post it when I’m done.

  3. 6 David Pascoe 2009/09/10 at 12:55 am

    Thanks for the web page, it was exactly what I was looking for – how could I create a variable of the right format to pass as a paramter type that was defined in a WSDL file to access a SOAP service.

    Can’t prove that it works for me yet as I still have another hurdle to jump – one of the data type names has a – in it, and PHP doesn’t like constructs like $param->name-part it thinks it is a subtraction part way through an identifier.

    Now to scratch my head as to how I can get a value into a name-part identifier :(

    The equivalent here would be if you had to do something like

    $search_query->oPurchaseRequest->Zip-Code = $zip

    in order to obey your WSDL specification. any ideas ?

    dp.

  4. 7 David Pascoe 2009/09/10 at 1:34 am

    In case someone else comes here wondering how to solve this problem, the syntax you need is

    $search_query->oPurchaseRequest->{‘Zip-Code’} = $zip

    and then you will be fine.

    davidp.

    • 8 Leif Madsen 2009/10/20 at 6:58 am

      Wow, thanks David! I had no idea how to do that :) I’m certainly not a PHP programmer by trade, and just happened to work out what I needed from reading enough blog posts. I’m sure that will help out a bunch of people. Thanks for following up!

  5. 9 Nil 2009/10/19 at 1:03 pm

    Hi :

    Which PHP SOAP version you where using?

    Thanks
    Nil

  6. 11 Nil 2009/10/19 at 4:51 pm

    Thanks Leif. Your posting helped me a lot. You explained things pretty well .Maybe you can post a link to your blog on PHP.net too. By the way, the pics are funny :)

  7. 13 Web Developer 2009/10/20 at 10:08 am

    Hi,
    Just want to confirm.. Is it possible to create the type locally and then send that over to the service?

    Thanks,
    James


Leave a Reply