Implementing SOAP on cakePHP
I have recently (last week) started using cakePHP. I choose cake because it works with AMFPHP and because I thought cake had built in support for web services (including soap). I'm not complaining, but I was somewhat disappointed to find out that support for web services is limited to routing, which I could not get to work anyway...
Requirements
So, I set off on my quest to implement soap. In brief, my requirement were:1.Serve soap
2.Automatically generate and serve wsdl
3.Handle complex types in method calls and return values
4.Work within the cakePHP framework
5.Easy to implement and easy to use
Serve Soap
Since I use php5 and because I plan to generate the wsdl separately, I choose to use the soap server/client included with php5.http://us2.php.net/manual/en/ref.soap.php
WSDL
For generating wsdl I choose to use Webservice helper from David Kingma:http://www.jool.nl/new/1,webservice_helper.html
Cake Integration
I began by trying to create a component that would grab soap requests and redirect them to a soap view. While this may work, and it would be very cake-ish, it seemed like a lot of work and not very soap-ish. I immediately ran into problems getting arguments from the soap request into the method I was calling. Soap requests generally do not use separate addresses for each method call, and they send arguments in the soap envelope, which is not parsed into the data (post or get) variable. Based on this, it seems to me that the cake view methodology and the cake component methodology do not mesh nicely with soap.As a side note; These problems could be overcome if the cake core would parse and return soap envelopes, but that seems like a task for the cake development team rather than a hack like me!
After I beat my head against the wall for a while, I realized that my first course of action was doomed to fail; I decided to try creating one Controller that would handle all soap method calls, and wsdl requests. This seems to work. This technique is very class centric, soap methods will be grouped and accessed by their controller name.
The main challenge here is to get the soap arguments out of the soap envelope so that they can be passed to the controller method. At first glance you might think you could simply use the SoapServer to call the controller method. Like this:
Download code
<?php
$server = new SoapServer($wsdlfile);
$server->setClass('MyControllerClass');
$server->handle();
?>
The problem is that this method would NOT call the controller within the cake framework. I'm no cake expert, but I don't think this would produce the expected results.Somehow we need to get the soap arguments and then call the controller method using the cake requestAction method.
Download code
<?php
$server = new SoapServer($wsdlfile);
$server->setClass('HandleSoapClass');
$server->handle();
?>
My handle soap class looks like this:Download code
<?php
class HandleSoapClass{
/** @var AppController */
static $controller;
/** @var string */
static $wsClassName;
function __call ( $func_name, $args ){
return self::$controller->requestAction('/'.substr(self::$wsClassName,0,-10).'/'.$func_name.'/soap5/', array('pass'=>$args) );
}
}?>
If you are unfamiliar with __call, please see the php site.http://us3.php.net/manual/en/language.oop5.overloading.php
This class basically passes any method call to the appropriate controller class by means of the cake requestAction method. And on the way it sends the soap arguments.
Maybe that seems obvious to you, but I thought it was pretty cool...
The other option would be be to:
- Parse the soap envelope
- create php vars or objects
- run the function
- then construct a return soap envelope
Below is an overview of the whole process.
Download code
+---soap client request wsdl from uri:mydomain.com/soap5/wsdl/ControllerName
| |
| +---soap5Controller's wsdl method requests the wsdl from WebServiceHelper
| and returns the wsdl document
|
+---soap client calls soap method from uri: mydomain.com/soap5/serve/ControllerName
|
+---soap5Controller's serve method receives the request and passes it to a
|php5 SoapServer
|
+---SoapServer passes the request to a dummy class
|
+---Dummy class parses the soap arguments and uses the cake requestAction
|method to call the real soap method
|
\---requestAction returns the results back to the dummy file and
SoapServer outputs the soap response back to the client
Comments
Comment
1 Fantastic
One comment about securty: I've noticed that a lot of the public web services and api's have a login method which returns a "session id" which is then used on every other method. This helps as the login method can be done over SSL and the rest of them will be faster done over normal HTTP. In that way the username and password are not transferred in clear text over HTTP.
Comment
2 Nice
I've also implemented some SOAP services using CakePHP. But I have slightly different way to do it.
First of all I made the wsdl files manually (using Eclipse tools). It's quite important for me to have full control over my wsdl files.
Each SOAP service (port) has it's own controller, called soap_service_name_controller and there is route to each soap service and wsdl file defined in the `routes.php`.
It's simple way to link SOAP services defined in wsdl file with cake controllers.
When I find some spare time I will put an article describing my way of implementing SOAP services.
Question
3 Code and usage instructions
Comment
4 Example request
Can you post your way of doing SOAP service on top of cakePHP? Would be intrested to see that.
Comment
5 I second that
Would love to see an article on web services within Cake.