PHP 5 Web Service Client

Until yesterday, I’d never written a single line of PHP. In fact, if you’ve read much of this blog you’ll see that most of what I do is c# and SQL Server related. Many of my posts are an attempt to simplify something that ought to have been easy but turned out to take 5 or 10 times as long as I thought it would. That was definitely the case with PHP and web services. Below are the steps that take you from a blank slate (PHP-wise) to successfully calling a .net web service using complex and composited types. In case you’re wondering why I wrote a web service client in PHP, a client was having problems accessing our web service using PHP.

My first step was to acquire and install the Uniform Server. I installed from UniServer3_5.exe. Uniform Server would have worked great if I didn’t already have IIS running on port 80. I’ve done a normal Apache install before under these circumstances, and Apache very kindly tells you the problem and how to fix it. Unfortunately, Uniform Server hides these problems. Basically, it doesn’t work and you have no idea why. If you do need to change the port, you’ll need to edit the http.conf under ..\udrive\usr\local\apache2\conf. “..\” refers to the directory where you extracted the Uniform Server (it can be anywhere you like). Search on 80 and replace with 8080 or whatever you want. The three lines I changed were:

Listen 8080
ServerName localhost:8080
<VirtualHost *>
	ServerName localhost:8080

Note that even after doing this, Start_Server will still try to take you to a URL without the port specified.

Next, you will need install and enable the Soap extension which you can find in here as ext\php_soap.dll. Copy this file to ..\udrive\usr\local\php\extensions. Now you will need to enable the extension by adding the following line to your ..\udrive\usr\local\php\php.ini:

extension=php_soap.dll

Save the changes and then stop and start the server using the supplied batch files or your changes won’t take effect.

Now we can write some code. I created my files using UltraEdit. I’m sure there are some great PHP IDEs out there but I wasn’t familiar with any. To run a php program, you simply create a file in the ..\udrive\www directory ending with the .php extension and then point your browser to http://localhost:8080/file.php. Note that you can omit the port in the URL if you are using the standard port setting of 80. In IE 7, I needed to hit ctrl-F5 to rerun my page after I had saved changes.

If you’ve looked at some other resources, keep in mind that we are using the built-in PHP soap support in PHP 5, not the older, uglier things like NuSoap, etc. The latest, built-in web service support works from wsdls like all the other modern web service platforms. However, PHP is still PHP, so even though creating the client is now easy, figuring out what to pass is not. Here’s the code to create the client object from a wsdl (if your wsdl URL starts with HTTPS, see below):

<?php
$client = new SoapClient("http://www.thomas-bayer.com/names-service/soap?wsdl");
&#91;/sourcecode&#93;

Note the first line.  I will be omitting it from all the rest of the code samples.

Here we are working with a web method that in c# took just a plain, old string as the parameter.  I could invoke it in c# with: 

client.getNameInfo("John");

By analogy, I expected to do something similar in PHP:

&#91;sourcecode language='php'&#93;
$client = new SoapClient("http://www.thomas-bayer.com/names-service/soap?wsdl");
print $client->getNameInfo("John");

Imagine my dismay when I received this error message:

Fatal error: Uncaught SoapFault exception: [ns2:Server] java.lang.NullPointerException

I’m not even using Java here! What’s going on? This service must be implemented using Java. More importantly, you need some way to see what the problems really are and what messages were sent and/or received. Here’s where reading this post really pays off:

<?php

$client = new SoapClient(
	"http://www.thomas-bayer.com/names-service/soap?wsdl"
	,array("trace" => 1, "exceptions" => 0)
);

$functions = $client->__getFunctions();
print_r($functions);
$types = $client->__getTypes();
print_r($types);

$response = $client->getNameInfo("John");
print "<br><br>";
print_r($response);

print "<br><br><pre>\n";
	  print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
	  print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n";
	  print "</pre>";

Note the trace and exceptions options in line 4. In combination with lines 16-19, they allow you to see what XML was actually sent and received even if you encounter errors or Soap faults. This is very helpful in debugging. We also want to see how PHP interpreted the WSDL which is what lines 7 – 10 display. Without this information, it’s very difficult to construct the object that you will pass to the web method.

Here’s the output of the program above:

Array ( [0] => getCountriesResponse getCountries(getCountries $parameters) 
[1] => getNamesInCountryResponse getNamesInCountry(getNamesInCountry $parameters) 
[2] => getNameInfoResponse getNameInfo(getNameInfo $parameters) ) 
Array ( [0] => struct getNameInfo { string name; } 
[1] => struct getNameInfoResponse { nameInfo nameinfo; } 
[2] => struct nameInfo { string name; string gender; boolean male; boolean female; countries countries; } 
[3] => struct countries { string country; } 
[4] => struct getNamesInCountry { string country; } 
[5] => struct getNamesInCountryResponse { string name; } 
[6] => struct getCountries { } 
[7] => struct getCountriesResponse { string country; } ) 

Request :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://namesservice.thomas_bayer.com/"><SOAP-ENV:Body><ns1:getNameInfo/></SOAP-ENV:Body></SOAP-ENV:Envelope>

Response:
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:Fault xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://www.w3.org/2003/05/soap-envelope"><faultcode>ns2:Server</faultcode><faultstring>java.lang.NullPointerException</faultstring></ns2:Fault></S:Body></S:Envelope>

If you look at the request, you’ll see we are sending an empty getNameInfo element and the web service assumes that we sent a string. Hence, the null reference exception. Looking at line 5 above, we can see that to fix this, we need to send an object with a string member so we change:

$response = $client->getNameInfo("John");

To:

$params->name = "John";
$response = $client->getNameInfo($params);

Now our call succeeds. This publicly visible web service had a simple signature. Suppose you were working (as I was) with something more complex. In my case, I had two complex parameters being passed, the second of which included an array of a complex type. Here’s what my call ended up looking like:

$user->GroupId = "TestGroup";
$user->Username = "TestUser";

$req->CompanyID = "12345";
$req->Amount = 155.20;
$req->UserDefinedFields[0]->FieldName = "Name";
$req->UserDefinedFields[0]->FieldValue = "Jane Smith";
$req->UserDefinedFields[1]->FieldName = "TrackingCode";
$req->UserDefinedFields[1]->FieldValue = "999";

$params->user = $user;
$params->purchaseReq = $req;

$response = $client->SubmitPurchaseReq($params);

If you need access URLs using SSL, then you will need to download, deploy and enable php_openssl.dll (see steps above for php_soap.dll). In addition, you will need to copy libeay32.dll to your windows\system32 directory. Libeay32.dll is in the same zip but the parent directory of the one that php_openssl.dll is in.

Hopefully, your first PHP web services experience will go more smoothly than mine.

Advertisements

13 Responses to “PHP 5 Web Service Client”

  1. Mahabub Says:

    Wow!! Last fewdays i have very tensed cause i tried to find out how to invoke a asp webservice using php. UR articles is really awesome and its help me much to do so. i tested that and working fine. thanks for postin such a nice article.Keep ti up ma frd……….

  2. A Martinez Says:

    Thanks for the tut. Come to find out I didn’t even have SOAP installed.

  3. eivind Says:

    Thanx man,

    it’s the first example I actually got to work!

  4. Ajith Says:

    Thanks a ton !!!!!!!!!!!!! I’ve not installed uniform server etc. Normal PHP and apache installations. Its working properly. Keep posting..cheers :-)

  5. Ivaylo Says:

    I have been searching for days for a solution of my problem (PHP passed null parameters while invoking Java web service). This article really helped. I would recommend it as
    a web services startup tutorial.
    Thanks for the solution!

  6. Richard van Looijen Says:

    Thanks for this great post! I finally got this combination (Java WS, PHP client) to work properly, including sending parameters. Another tip i would like to add is disabling PHP’s WSDL cache during development with soap.wsdl_cache_enabled=0 in php.ini. This cost me over an hour today to figure out. Also note that if your WSDL was already cached, restarting the webservice is not going to help, but you can safely delete the wsdl cache file(s) from /tmp (on Linux).

  7. Julien DES Says:

    Good!! You make my day with your code for tracing all stuff.
    Thank you very much.

  8. Ken Jordan Says:

    Wow, this article helped me a lot. Thanks!

  9. Abhishek Says:

    Great post , Thanks a lot ! Keep posting such wonderful post…..!

  10. Olayemi Says:

    Thanks so much for this post.Saved me many more weeks of research and fixing.

  11. travis Says:

    great post :))))))))))))))). Banged my head for days until this article. Thank You.

  12. Karthiha Says:

    Hi! Thanks a lot. Good guidance for me as i was struggle to find out a solution to use soap instead of NuSoap.

  13. webapp Says:

    Thanks A ton, it worked.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: