Using SOAP and XmlRpc with PHP5 (a newbies findings)

The following is effectively an online version of a talk I gave to phpwm in July 2008.

Disclaimer!

I'm no great expert on the inner workings of these protocols....there are probably secret manuals on SOAP/XmlRpc etc I/we failed to read somewhere

This is just a documentation of what I/we as “newbies” found ...

What?

SOAP and XmlRpc protocols to allow for remote procedure calls. In other words, you could for example "expose" a service using XmlRpc for customers/the public to interact with. This allows them to hook into your system, and use it as they see fit.

If you are interested in this, you might also want to look at the SCA SDO module in PECL. Although aware of it, for some unknown reason, we didn't investigate using it.

  • SOAP – once stood for Simple Object Access Protocol
  • XMLRPC – XML Remote Procedure Call[s]

Both are Cross platform, language agnostic means of executing remote code.

SOAP is really now just called "SOAP", I think they've dropped the "Simple..." bit from the name as it can be anything but simple.

Examples of usage

  • Distributed applications
  • Web services (Amazon, MSN, Yahoo etc)
  • Proprietary applications
  • Integration between Python/Java/.Net/PHP etc

What is XMLRPC?

  • “Lightweight” version of SOAP
  • Does not support as rich data types
  • (Some of the SOAP-ites developed it)
  • Not standardised [by committee]
  • Zend_XmlRpc_(Server|Client)
  • PHP5 has an xmlrpc extension
  • Transparent to the end user

SOAP

  • Standardised (W3C)
  • Successor to XMLRPC?
  • Headers can contain authentication details
  • WSDL File....
  • Developed by Microsoft, W3C etc

Transport layer

  • Both work over HTTP[S] (Firewalls etc)
  • Both involve/use XML (Verbose? Bulky?)
  • Both work through POST requests
  • (Internet application layer as a transport layer...)

As PHP developers, the above should be nothing new or scary - it's building upon familiar concepts we use (XML, POST, HTTP)

XMLRPC – Data types supported

  • Array
  • Struct (Assoc. Array)
  • Integer
  • Boolean
  • Double
  • String
  • Base64 (useful for binary data)
  • Date/Time

Note, there is no support for Objects - as you can probably imagine it's likely to be difficult to take an object from e.g. Java and access it from PHP - what about logic held within the object etc?

SOAP – Data types supported

As per XMLRPC + any others you wish to define in your WSDL file.... (Objects etc - at least I'm assuming it allows this)

Error Reporting

Both sort-of-support Exceptions (“Faults”)

"Imaginary" Scenario

$customer has an application (and database) which they wish to give their customers access to (although they didn't word it as such).

“We need to allow $y to be able to integrate with our system to do $x”

My immediate thought was "XMLRPC / SOAP....?"

Initial discussion on the phpwm mailing list, also raised the idea of REST.

XMLRPC or SOAP or REST

Initial investigation of REST implied that I would need to write a 'view layer' which would probably be returning XML. On one hand it involves GET requests as well (so perhaps better caching can take place?) - but I really didn't see why anyone would use REST over e.g. XMLRPC/SOAP - considering the extra work required both when developing the API and when consuming it.

Structure

At a simplistic level, we split the structure into three layers - the backend logic (talking to DB, performing validation and business logic), the SOAP (or XMLRPC) service and finally and the client. Some example code follows.

App Logic

  • Not hard coded to a transport (could use “locally”)
  • Easier unit testing
  • Normal PHP5 “stuff”
  • Has appropriate authentication checks.

XMLRPC / SOAP Interface

Our initial implementation was XMLRPC only, as this seemed the easiest to use. Coming from a web development background, we wanted to use e.g. sessions to capture authentication details, and allow for persistence across requests. The Zend XmlRpc library does allow this (once you set a cookie jar, which takes about 4 lines of code). Unfortunately, it doesn't see that many other libraries in other languages do support cookies (easily?), so this approach has lost favour over time.

We had the same issue with HTTP authentication as with cookies - namely, asking an employee to write a Java (or Python) XMLRPC client that interacted with the service resulted in about a day of lost time.

Therefore, the only workable approach appears to be to make the first two parameters of each method call the username and password of the client. Ugly, but will always work.

  • Initially unsure of which was the best approach - So support them all?
  • How to do authentication? Cookies? user/pass parameters at the start of each method call ?
  • SOAP has some sort of header authentication support, but no one seems to have used/documented it for me to find...

From an XMLRPC point of view, we ended up having the following server URLs -

  • server_session_auth.php
  • server_manual_auth.php
  • server_http_auth.php

ARGH! Thankfully there is minimal duplication of code between the session and http auth variants, but because the manual auth approach requires two parameters to be added to all method calls, we had to write a proxy class to accommodate this. This is a slight maintenance annoyance, and if the API grows much more, we'll write something to generate this code for us.

We've now (effectively) dropped them all apart from “server_manual_auth.php”

Session and HTTP auth were VERY easy to write though; manual_auth is a pain; but adopting ONE approach makes support easier.

SOAP Interfaces

  • Php5's ext/soap doesn't support namespaces, so
  • Separate URL/PHP file per back end object
  • Stuck with “manual_auth” approach for this
  • Was able to reuse all of the XMLRPC proxy code :-)
  • Quick win - it took a handful of hours to write the server, a test client and get it working.

Performance

  • Kind of sucks
  • $server(s) are in Australia, we're in the UK.... Customers are everywhere.
  • We “batched” together the “addNewItem()” method into an explicit “addOneThousandNewItems()” method which helped.
  • Could “bulk” up; but HTTP/PHP POST size limitations?

Obviously you'll want to use deflate/gzip compression on calls/responses....

Sample Code....(xmlrpc server)

require_once('Zend/XmlRpc/Server.php');
require_once('ModuleXProxy.php');
$server = new Zend_XmlRpc_Server();
$server->setClass('ModuleXProxy', 'name.space');
echo $server->handle();

Note, you can set namespaces using the Zend XmlRpc client, this is great as we have many backend models, but they can all be combined into one server URL.

ModuleXProxy.php

PHPDoc comments VERY IMPORTANT (and annoyingly case sensitive!)

require_once('ModuleX.php');
class ModuleXProxy {

    public function __construct() {
        $this->real = new ModuleX();
    }

    /**
     * @param string $username
     * @param string $password
     * @param string $foo
     * @return boolean success indicator
     */
    public function doSomething($username, $password, $foo) {
      if(authenticated($username, $password)) {
       return $this->real->doSomething($foo);
     }
     throw new AuthenticationException(“....”);
}

Client...

require_once('Zend/XmlRpc/Client.php');
$xmlrpcclient = new Zend_XmlRpc_Client(“http://somewhere/server.php”)
$proxy = $xmlrpcclient->getProxy('name.space');
$result = $proxy->doSomething($username, $password, $whatever);

Compared to "local" usage of the server object (ModuleX) there is only one difference - the username/password parameters. Hence it's quite transparent and seamless.

4 lines of code.... seems easy enough to me! Might want Exception try/catch in the real world.

SOAP Server

require_once('ModuleXProxy.php');
$options = array('uri' => 'urn:http://dom.ain/soap/modulex',
                 'soap_version' => SOAP_1_2,
                );
$server = new SoapServer(null, $options);
$server->setClass('ModuleXProxy');
$server->handle();

Note – we're not using a WSDL file....

SOAP Client

$client = new SoapClient(null, 
    array(
        'location' => 'http://dom.ain/soap/path/to/server.php', 
        'uri'      => 'urn:http://dom.ain/soap/modulex', 
        'soap_version' => SOAP_1_2));

$result = $client->__soapCall('doSomething', array($user, $pass, $something)));

For some reason, the __call() function in the SoapClient object is marked as deprecated in the API; I've no idea why, as it would be far nicer an API (for clients) if it could be used. Obviously as a client you could write a wrapper which makes it transparent using the __call() magic method, but this seems needless work to me.

Short summary - Less code, but shame it doesn't natively support __call() etc

Zend Framework

  • Zend_Soap is under development.... which is why we didn't use it
  • Zend_XmlRpc namespaces - a great feature/idea.

The Zend Frameworks' XmlRpc library saved us a lot of time, and it's worked really well. Hopefully it will get faster performance in the future, although we don't really notice it as having bad performance. (The initial implementation of a caching feature, as per the documentation resulted in it taking longer to respond to requests! Bug ticket created etc.)

Technorati Tags:

Comments

streaming audio

Hey I want to share mp3s with afilliate websites. Does XMLRPC or Soap support streaming?

Errm...

Well, you could return the data in base64 encoded chunks, but you'd need to write the client to do this for you....

It would probably be easier to e.g. publish resources/urls [shoutcast streams etc] via xmlrpc or soap, which the client would then connect to.

Yeah, that sounds like a

Yeah, that sounds like a better idea! I imagine there could be all sorts of traffic problems in streaming!!!

Beware of using dates in PHP's XMLRPC library

Hi,

Just be aware that PHP's XML-RPC extension uses timestamps internally for dates, which means that it can't handle dates correctly cross-platform that occur before the epoch. (Some platforms support negative timestamps, but not all). It's the only reason why PHP's XML-RPC extension doesn't pass the XML-RPC upstream testsuite.

Best regards,
Stu

interesting - thanks

We've not tried using timestamps as such directly - if dates are returned they're within arrays, and treated as strings.

I'm not really sure why a PHP developer would want to return something as a 'date' via XMLRPC anyway; I'd presume they'd be happy with a string (like that from a database resultset, for instance)

David.

REST vs SOAP

I'm a huge fan of REST, because:
1. Writing a client to deal with it is much, much simpler - if you speak HTTP, you can interact with it
2. Authentication (http basic auth) is built right in.
3. Security a priority? REST + HTTPS + Authentication.
4. Having an Atom/RSS feed is an example of a restful news webservice - even if its read only.
5. When I've dealt with SOAP in a business environment, the typical use case has been "we need a way to transport a complex object which is represented in XML". In other words, you end up with soap methods which are like... postNewOrder(xml), getOrderStatus() -> return me an xml document ...
6. It's RDF friendly.

Where SOAP makes sense:
* Autogenerated code from WSDLs is useful.
* .NET to .NET is easy.
* Unit tests are hard to do cleanly (you can test your controller, but the transport layer is HTTP + SOAP + Your Application)

Where REST falls over:
* Writing a server is hard if you don't have a clean MVC approach
* Unit tests are still hard to do right - HTTP + Your Application.

Where SOAP has fallen over for me:
* PHP to .NET - nothing works just out of the box (at least for us) - you have to do Nasty Things
* There are too many ways to do Authentication for me to easily comprehend.
* PHP + SOAP == Magic happens :(

What's missing from both:
* A widespread easy way to discover webservices that makes sense. (RDF to describe services is maybe the best bet in my eyes)

.NET to PHP SOAP

It's interesting that you say you've had problems with e.g. .Net to PHP for SOAP.... do you have any more info on this?

How about nuSoap?

Did you consider using the nuSoap framework over the Zend Framework?

nuSoap - I did have a look

As far as I could tell, the project didn't seem to be all that active, and still targetting PHP4.

I think I tried it out - but perhaps got stuck up on the WSDL issue (perhaps the nuSoap docs didn't make it clear that a WSDL file wasn't needed).

I'm fairly sure I downloaded it, and I think I gave it a very quick try out... but can't remember much more as it was a few months ago.

Presumably you use it? Do you have anything interesting to report? (e.g. "It just works" or whatever)

nuSoap - use of...

Yes, I've used it, albeit, just tinkering.

The project is still active, and the core developer usually replies to mailing list posts within 24 hours - which was quite impressive :)

A while back I purged a load of chaff from my working SVN tree, but I just checked, and in revision 9 of http://home.spriggs.org.uk/svn/files/SoapTimes there's some examples of code I wrote with nuSoap.

Exactly *what* it does, I don't recall (it was chaff after all!) but, I do recall that I had *something* working somewhere around there :)

Regards,

Jon

I'm a bit of a REST fan, as

I'm a bit of a REST fan, as you know. If you had a library to help you write the REST interface, would that help? Is that all that's holding you back?

The important thing for me is the difference in philosophy. I like the idea of having, effectively, web-enabled objects (resources) which you act upon. XML-RPC is more functional: you call a function, pass in some arguments, and get back some response. REST is more like OOP to me: there are objects, you invoke their methods, and they send back responses. I also like how the HTTP method maps to the type of action on the resource (GET does a query, POST does a create, PUT does an update, DELETE does a delete). The Accept header of the request tells the server what type of representation to return, in some cases. The HTTP response code tells the client what happened to their request. So some of the semantics of the operations is carried by the URI, the method, the headers, response codes. With XML-RPC, I suppose you'd have endpoints corresponding to bundles of functionality (maybe) or a single function. The semantics of the operation might be in the endpoint address, might not; the type of operation is signified by arguments passed to the function. The meaning of the response is carried in the response body.

To my mind, REST seems more self-describing than XML-RPC: it leverages more of HTTP to lighten the amount of meaning you're passing in via arguments and getting back from the response body. The response body can be reserved for the representation of the resource, and much of the result of the operation (success, failure, location of newly-created resource etc.) is carried in other parts of the response (headers, code). That's where the power is for me.

On the other hand, it is less-standardised (though that makes it more flexible, maybe) and less predictable (which is why there are fewer libraries). Though, if you're worried about writing views, why not just serialise objects directly to XML or JSON?

Rest-ing

There is Zend_Rest, although I didn't seem to make much of it when I had a look.

I think my issue with e.g. REST, is that if I tell someone "Here's a REST API, this is how you need to talk to it" I need to provide more documentation - if I say "Here's an XMLRPC API, here is the phpdoc" I don't need to do much more - I don't have to explain to them that the output is json/whatever encoded, I don't have to explain what I expect as inputs - it's all abstracted away.

Perhaps I'm being a little harsh on REST....

__call() deprecated for direct use

I believe the reason for __call() being deprecated is that you aren't meant to use it directly. Elsewhere in the docs, it gives examples where you make use of __call() as a magic method, and event mentions it as being the preferred way to do it.
See description on: http://www.php.net/manual/en/function.soap-soapclient-soapcall.php

Post new comment

  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
Et tu Roboto?