First impressions on Zend_Soap and a basic implementation

The Zend Framework release candidate for version 1.6 includes a new component for SOAP operations. Zend_Soap_Server/Client extend the PHP functionality of the SOAPClient and SOAPServer objects, which by itself is trivial. The more important functionality it ads to the package is the AutoDiscovery Component.

Generally you can use SOAP in the so called "non-wsdl" mode, that is if you specify the correct options like the soap service location and uri, you don't need any description of the service for the clients. This is only useful if you're using the SOAP Service internally since you then know about all the available functions and methods. If you want to offer the Service for external users you want to use the WSDL-mode: Generate a WSDL that describes the services available methods, their parameters and return types is an important task.

Using Zend_Soap_AutoDiscover you can generate your WSDL file automatically by reflecting on the given Service Class methods. This works as follows. We setup a simple service:

class HelloWorldService
{
    /**
     * @return string
     */
    public function helloWorld()
    {
        return "Hello World!";
    }
 
    /**
     * @return array
     */
    public function getFruits()
    {
        return array('apple', 'orange', 'banana');
    }
}

It is important that you specify the Doc Comments @param and @return otherwise the AutoDiscovery of the correct parameter and return types cannot be resolved. We will now setup a simple SOAP Server access point, that will also generate our WSDL file for description of this HelloWorld service:

require_once "HelloWorldService.php";
require_once "Zend/Soap/Server.php";
require_once "Zend/Soap/AutoDiscover.php";
 
if(isset($_GET['wsdl'])) {
    $autodiscover = new Zend_Soap_AutoDiscover();
    $autodiscover->setClass('HelloWorldService');
    $autodiscover->handle();
} else {
 
    $soap = new Zend_Soap_Server("http://localhost/soapserver.php?wsdl"); // this current file here
    $soap->setClass('HelloWorldService');
    $soap->handle();
}

Now we have our SOAP Service up and running and any client can access the HelloWorldService class from a remote or local location with just this simple lines:

require_once "Zend/Exception.php";
require_once "Zend/Soap/Client.php";
 
try {
    $client = new Zend_Soap_Client("http://localhost/soapserver.php?wsdl"); // Servers WSDL Location
    $string =  $client->helloWorld();
    $fruits = $client->getFruits();
 
    var_dump($string);
    var_dump($fruits);
} catch(Zend_Exception $e) {
    echo $e->getMessage();
}

This is easy. Additionally Zend_Soap offers a WSDL class to generate your own WSDL file based on your special preferences, which is a nice feature. I guess only some people can write a correct WSDL XML specification file on their own from scratch, so using a powerful helper is reasonable.

The usage of Zend_Soap is quite simple and straightforward as is PHP5's internal SOAP Service and might therefore gain widespread use. I have never tested PEAR's WSDL Autodiscovery, so i cannot draw comparissons.

Share This Post

  • Share on Twitter
  • Facebook
  • Share on deli.cio.us
  • Share on Digg
  • Share on reddit
  • Share on StumbleUpon

Comments


kumar
Aug, 12. 2008

Good article on SOAP. Wondering whether we can see the WSDL file created by the autodiscovery. Any ideas !



beberlei
Aug, 12. 2008

Hello!

Thanks for the comment, you can see the WSDL file content, i created a simple
script for testing this example, it uses DOMDocument such that the XML is
printed with intendation. Otherwise you could only print $contents, it has no
linebreaks though:

Hello!

Thanks for the comment, you can see the WSDL file content, i created a simple
script for testing this example, it uses DOMDocument such that the XML is
printed with intendation. Otherwise you could only print $contents, it has no
linebreaks though:

<?php

require_once "HelloWorldService.php";
require_once "Zend/Soap/AutoDiscover.php";

ob_start();

$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setClass('HelloWorldService');
$autodiscover->handle();

$contents = ob_get_contents();
ob_end_clean();

$dom = new DOMDocument();
$dom->loadXML($contents);
$dom->formatOutput = true;
echo $dom->saveXML();

?>



kumar
Aug, 21. 2008

Fantastic ! It works.



jan666
Sep, 16. 2008

This was very usefull but I couldn't get it to work in 1.6.0 until this morning when 1.6.1 was available to download.
Thanks for a great intro though, one of the few dealing with the 1.6 version of ZF soap.



robert_
Dec, 23. 2008

Hi.

I tried your solution, and I'm still getting, "Fatal error: Uncaught SoapFault exception: [VersionMismatch] Wrong Version in ..."

I need assistance as soon as possible.

thanks,
- Robert



nifty101
Feb, 06. 2009

Hi,

Can you please explain how all of it fits in an MVC model? I do not want to hardcode URL locations of WSDL. I understand "HelloWorldService" class goes into models directory. But where will the code for autodiscovery of wsdl go?

Thanks



nifty101
Feb, 06. 2009

Also, can you please explain how to setup autodiscovery for multiple services?

Thanks



rodrigo
Feb, 17. 2009

Hi,

Finally, a Zend Soap example that actually works. Thanks for that. Although, when i try to add a new function to the HelloWorldService, I got a Soap Exception: SoapFault exception: [Sender] Function ("getName") is not a valid method for this service. Can you help on this? Thank you.



willian
Mar, 11. 2009

hi,
i'm trying to use zend_soap but i cant do it work. i read your article and i just copy and paste it on my local server i it work. but you dont use zend_controller_action and the other stuffs. so i tried to use your code with it. it doesnt work. it gives a "Wrong Version" error. i tried many times and find i weird thing: if if put like this:

/**
* Renders WSDL
*/
public function wsdlAction() {
require_once APPLICATION_PATH."soap/ClassAPI.php";
$wsdl = new Zend_Soap_AutoDiscover();
$wsdl-setClass('ClassAPI');
$wsdl-handle();
exit;
}
it doesnt work (give me "Wrong Version" error), but if put like this:

/**
* Renders WSDL
*/
public function wsdlAction() {
if(isset($_GET["wsdl"])) {
require_once APPLICATION_PATH."soap/ClassAPI.php";
$wsdl = new Zend_Soap_AutoDiscover();
$wsdl-setClass('ClassAPI');
$wsdl-handle();
exit;
}
}
it doesnt work to, always return NULL values but dont give exceptions errors.

i dont know whats wrong.
do you already tried to do put this code inside a controller_action class? if yes could you send to me on my email to me analize it.

thank you

ps: im sorry for my english
pps: i already read http://electrotek.wordpress.com/2008/04/19/soap-web-service-with-zend-framework/ and it doesnt work to



thagoob
Mar, 26. 2009

Whenever you'll add methods to this example's class, you may want to check your Apache/PHP temp directory, since the WSDL is cached there. Delete it and *THEN* add new methods... it will work!



thagoob
Mar, 26. 2009

As an addition to my former post:
You can disable this feature by setting
soap.wsdl_cache_enabled=0
in your php.ini



gnozu
Mar, 31. 2009

Hey willian,
I had exactly the same problem as you.
If you look at the Zend API for Zend_Soap_Autodiscover you'll see that it says:

"As the SOAP Endpoint Uri is uses the default 'http://' .$_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'], but this can be changed with the setUri() function or the Constructor parameter of Zend_Soap_AutoDiscover class. The endpoint has to provide a Zend_Soap_Server that listens to requests."

ie using an MVC approach ZF thinks your webservice url is different from what it actually is. So, you have to call:
new Zend_Soap_AutoDiscover(true,url_of_your_webservice)

This isn't documented anywhere at all in the API. I had to look up these params in the class file itself. Yuk! But it did the trick for me.

Good luck!



subrata
Sep, 03. 2009

Benjamin - Thanks for the SOAP intro, indeed! I am a PHP newbie.

My experience below, if it helps anybody...

I started with Zend_Soap_Server/Client with my Zend MVC bootstrapped way; I could not get it to work, so far (suspecting rewrite feature in .htaccess!). I then used (standard PHP) SoapServer & SoapClient (non-MVC, server defined under the web root), and used Zend_Auto_Discover to generate the WSDL. server & client uses this WSDL from PHP stdout. It worked ! Next, I got it working from a MVC client.

Thanks again, have a great one.



jaistealth
Oct, 28. 2009

Fatal error: Uncaught SoapFault exception: [VersionMismatch] Wrong Version in C:\xampp\htdocs\hw\ZeroWebserviceClient.php:4 Stack trace: #0 [internal function]: SoapClient-__call('sayHelloWorld', Array) #1 C:\xampp\htdocs\hw\ZeroWebserviceClient.php(4): SoapClient-sayHelloWorld('User') #2 {main} thrown in C:\xampp\htdocs\hw\ZeroWebserviceClient.php on line 4



sagar
May, 20. 2010

Great it works thank you



Jignesh
Aug, 18. 2010

Hello when i try this example in zend framework then it will give "Wrong Version" exception.
so what should be the problems. can any one guide me here is my code

soapController.php

require_once realpath(APPLICATION_PATH . '/../library/').'/SoapDiscovery.class.php';
require_once realpath(APPLICATION_PATH . '/../library/').'/Soaptest.php';

class SoapController extends Zend_Controller_Action
{
private $_WSDL_URI = 'http://localhost/soap/public/soap/index?wsdl';

public function init()
{
}

public function indexAction()
{
$this->_helper->viewRenderer->setNoRender();

if(!isset($_GET['wsdl'])) {
//return the WSDL
$this->hadleWSDL();
} else {
//handle SOAP request
$this->handleSOAP();
}
}

private function hadleWSDL() {
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setClass('Soaptest');
$autodiscover->handle();
}

private function handleSOAP() {
$soap = new Zend_Soap_Server($this->_WSDL_URI);
//print_r($soap);
$soap->setClass('Soaptest');
$soap->handle();
}

public function clientAction() {
try{

$client = new SoapClient('http://localhost/soap/public/soap/index');
echo '<pre>';
print_r($client->math_add(11,33));
// print_r($client->getname());
echo "hello";
exit;
/*$this->view->add_result = $client->math_add(11, 55);
$this->view->logical_not_result = $client->logical_not(true);
$this->view->sort_result = $client->simple_sort( array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"));*/
}catch(Exception $e)
{
echo "hellofsdfad";
echo '<pre>';print_r($e->getMessage());
}


}

}

HelloWorld.php


//$conn = mysql_connect('localhost','root','');
/**
* @return string
*/
public function helloWorld($name = '')
{

return "Hello World ". $name."!";
}

/**
* @return array
*/
public function getFruits()
{
return array('apple', 'orange', 'banana');
}

/**
* @return array
*/
public function getName($name = '')
{
$names = array('jignesh','sandip','mayank','nilkanth');
$data = array($name);
$result = array_intersect($names,$data);
if(count($result)){
return $result;
}else{
return array('No Match Found');
}

//return "Jignesh";
}

/**
* @return int
*/
public function getMathAdd($a, $b)
{

return $a + $b;
}
}

please help me out..


Write a Comment