After a month of not writing much, this week it’s a blitz - this time looking at how you create SOAP servers with PEAR::SOAP.
But first...
... a couple of points addressing comments which came up in response to Guidelines for Designing Classes in PHP. This site isn’t handy with an 800×600 screen and the printable version doesn’t always fit a printer. After coming across an awesome little class HTMLSax reckon I’ve got the right tool to help output PDF with R&OS PDF. The problem is there’s HTML-like markup store in the db here but HTMLSax solves (note also that PHPShelve is cunning piece of code that allows you to persist objects with text files, based on a Python Shelve).
As to the point that it’s not worth trying to teach “HTML authors” OOP and patterns... well given that anyone who’s done any HTML has already used a pattern so “they” have a right to know (anyone who’s taught themselves HTML and PHP is capable of working out OOP / patterns IMO)
1)
PEAR::SOAP Server
OK here’s a SOAP server built with PEAR::SOAP;
class Test1 {
// Stores instance of PEAR::SOAP Server
var $soapServer;
// Constructor builds PEAR::SOAP Server
function Test1 () {
// Switch off notices to all GET
error_reporting(E_ALL ^ E_NOTICE);
// Instantiate PEAR::SOAP SOAP_Server
$this->soapServer=new SOAP_Server;
// Build the object map (using this instance) + add a namespace
$this->soapServer->addObjectMap($this,'http://www.phppatterns.com#Test1');
// Turn on the server
$this->soapServer->service($GLOBALS['HTTP_RAW_POST_DATA']);
}
function serverTimestamp() {
return time();
}
function hello($name) {
return 'Hello '.$name;
}
}
?>
In this example the Test class “owns” the PEAR::SOAP Server instance. To execute the above class;
// testserver1.php
// Include the SOAP Server
require_once('SOAP/Server.php');
// Include the test class
require_once('lib/Test1.php');
// Start the SOAP server
$test = new Test1;
?>
That’s it. PEAR::SOAP server does some introspection on the Test1 class and works out what methods are available (no need to register dispatch maps etc.).
Note the namespace used here;
$this->soapServer->addObjectMap($this,'http://www.phppatterns.com#Test1');
Multiple classes can be registered with the PEAR::SOAP server, the namespace being what distinguishes a method in one from a method in another of the same name.
Another way to do the same thing;
class Test2 {
function Test2 () {}
function serverTimestamp() {
return time();
}
function hello($name) {
return 'Hello '.$name;
}
}
?>
Now in the code that executes this class;
// testserver2.php
// Include the SOAP Server
require_once('SOAP/Server.php');
// Include the test class
require_once('lib/Test2.php');
// Start the SOAP server
$test = new Test2;
// Switch off notices to all GET
error_reporting(E_ALL ^ E_NOTICE);
// Instantiate PEAR::SOAP SOAP_Server
$soapServer=new SOAP_Server;
// Build the object map (using this instance) + add a namespace
$soapServer->addObjectMap($test,'http://www.phppatterns.com#Test2');
// Turn on the server
$soapServer->service($GLOBALS['HTTP_RAW_POST_DATA']);
?>
You can even pass an instance of PEAR::SOAP server to a class like;
class Test3 {
function Test3(&$soapServer) {
$soapServer->addObjectMap($test,'http://www.phppatterns.com#Test3');
}
}
// etc.
Credit to Shane Caraveo for making it so easy.
Note on the Dispatch Map
(Added 12th Apr 2003) One further note on the dispatch map is with the above examples, PEAR::SOAP is gathering every method in the object you pass it - there may be situations where you don’t what this, in which case you need to define the dispatch map yourself like;
$test = new Test4;
$test->dispatch_map['serverTimestamp'] =
array(
'out' => array('timestamp'=>'string')
);
$test->dispatch_map['hello'] =
array(
'in' => array('name'=>'string'),
'out' => array('greeting'=>'string')
);
// Instantiate the SOAP server
$soapServer=new SOAP_Server;
$soapServer->addObjectMap($test,'http://www.phppatterns.com#Test4');
You might also define the dispatch map inside the constructor of your own class.
2)
PEAR::SOAP Clients the hard way
If you were impressed by WSDL and PEAR::SOAP now for the bad news - unless you’re fluent in WSDL, you have to hand code the proxy code you saw generated there (as people did it in the “old days” with XML-RPC).
At some point I’ll do my best to explain WSDL, perhaps with a rant on REST as well as venting steam about how a standard meant for web services is better suited to strongly typed languages (at least on the server side). Which languages made dynamic web sites really happen? Could it Perl, JavaScript, VBScript and PHP? Not much strong typing there... And it could have been MIME RPC.
Anyway, there’s hope for PHP that at some point in the not so distant future we’ll be able to generate WSDL documents directly from a PHP class, either with the Tokenizer extension (the PHP Documentor team have already begun to take advantage of it) or perhaps the type hinting coming with PHP 5. Shane also mentions some kind of plan for something looking like IDL in the PEAR::SOAP “todo” comments but not sure how far that’s going.
Today you either have to manually code clients for PEAR::SOAP (or your preferred implementation) or learn WSDL, at which point you may be interested in Generating PHP SOAP Servers from WSDL with XSLT. Another option which I haven’t checked out yet is PEAR::SOAP is also supposed to be able to create Servers from WSDL documents in what looks like a similar way to Clients.
Building a client by hand for a small service is pretty simple though, for example;
class TestClient {
var $client;
var $nameSpace;
function TestClient ($url,$nameSpace) {
$this->client = new SOAP_Client($url);
$this->nameSpace= $nameSpace;
}
function serverTimestamp() {
$params=array();
return $this->client->call('serverTimestamp',$params,
$this->nameSpace);
}
function hello($name) {
$params=array($name);
return $this->client->call('hello',$params,
$this->nameSpace);
}
}
?>
Here I’ve gone for owning the PEAR::SOAP Client - you might considering extending it though in the same way as the proxy code, giving you access to the underlying SOAP methods or even passing it around between classes like a database connection (a SOAP Client a database class have much in common).
To put the above code into action;
// testclient.php
// Include the SOAP Server
require_once('SOAP/Client.php');
// Include the test class
require_once('lib/TestClient.php');
// Edit these to match the server
$url='http://localhost/phppatterns/ws/testserver1.php';
$namespace='http://www.phppatterns.com#Test1';
// Start the SOAP server
$testClient = new TestClient($url,$namespace);
echo ( $testClient->serverTimeStamp().'
' );
echo ( $testClient->hello('World!').'
' );
?>
So far so good. Note that I haven’t implemented any error handling here - if something goes wrong PEAR::SOAP returns an object of SOAP_Fault which you can check for like
PEAR::isError($response);
Security and Authentication
Security, in regard to transfering data over the wire and making sure no is “listening in” is best handled by SSL. Authentication could be built into the application itself, should you need it to control access to certain SOAP methods, passing a username/password as arguments but in general you’re better off with HTTP Basic Authentication (best with SSL) - check out the HTTP Authentication section in the manual, to protect the server.
On the client side, PEAR has an excellent collection of network related classes so dealing with HTTP authentication is no problem. Basically just;
$proxy=array('proxy_user'=>'someone','proxy_pass'=>'secret');
$client = new SOAP_Client($url,false,false,$proxy);
// or
$wsdl = new SOAP_WSDL($url,$proxy);
It’s actually PEAR::HTTP Request that gets passed the $proxy array eventually - check out it’s constructor to see all the values you can use.
Which reminds me; you can send SOAP over other protocols such as SMTP (email) and FTP should you so desire. PEAR::SOAP supports SMPT right now (I believe).
News Flash: PEAR API Documentation
Note on Interop: RPC/Encoded vs Document / Literal
The .NET tools encourage developers to use Document / Literal style SOAP and there’s been people stuggling to get talking to PEAR::SOAP as a result. I could say “That’s what you get for using a GUI to help you fail to understand what you’re doing” but I wont.
Basically (IMO - who really knows?) document literal style means in the body of the SOAP message you’re sending a “normal” XML document with no definitions of what data types the tags are (no encoding). Here’s where this get’s amusing: http://www.webservicex.net/usweather.asmx/GetWeatherReport?ZipCode=10005 - that’s document / literal SOAP available via the GET method generated by .NET. Now slap me for being stupid but where is there any reference to SOAP in that? This is good because it’s REST (more an that another time) but what was the whole point of SOAP in the first place? Hadn’t we done all this ages ago with WDDX (I guess that’s technically Document/Encoded which is a cross between the two)?
The WSDL document for the service above is at http://www.webservicex.net/usweather.asmx?WSDL
RPC/Encoded style is like the good ‘ol days of XML-RPC - the SOAP encoding specifications identify the data types you use in your SOAP server responses.
In terms of practical difference, RPC/Encoded is a better choice for Intranets / system integration and data exchange between small groups of web servers. Document/Literal (i.e. plain old boring XML + WSDL to help you find it) has more potential to be a true messaging mechanism for web services - i.e. you could use Google to find that this of service as opposed to a UDDI server.
PEAR::SOAP defaults to RPC/Encoded so be warned when you have confused people trying to access your RPC/Encoded service with .NET or if you try to use PEAR::SOAP Client to access a Doc/Lit service. You can pass an array of options to the SOAP_Client::call() method using the argument seen before to pass the namespace e.g (from Shane’s presentation - see end);
$options=array(
'namespace' => $baseurl.'/livecitycams.wsdl',
'soapaction' => $baseurl.'/livecitycams/listlivecitycams',
'style' => 'document',
'use' => 'literal');
$params=array()
$result = $client->call("ListLiveCityCams",$params,$options);
I haven’t played with this much but it seems that the SOAP_WSDL class knows how to handle document / literal services although no guarantees.
On the server side, I looks like PEAR::SOAP Server can generate Document Literal services but more I can’t tell you (I use this stuff on Intranets where RPC / Encoded is just fine).
Further Reading
2)
Heard of
XUL yet? Mozilla have turned Gecko (the core) into a runtime just like Java and .NET have runtimes. You can use it to make complete
GUI applications using markup like
HTML along with
CSS and JavaScript and you can also launch
XUL apps straight from a website to anything running Gecko (e.g. Mozilla / Netscape) - i.e extemely fast download compared to Applets / Flash. And it’s library XPCom has support for
SOAP and
XML-
RPC... check out
http://www.xulplanet.com for more...
develop/pear_soap_server_quick_start.txt · Last modified: 2005/10/15 21:47
Comments [0]