Electrotek

From programming to day dreaming

SOAP web service with Zend Framework

Warning: this stuff is written more than a year ago and might not fully reflect current situation. It’s not production ready (performance is not addressed here – think cache). This tutorial is no longer supported. Use at your own risk.

SOAP definition from Wikipedia:

SOAP is a protocol for exchanging XML-based messages over computer networks, normally using HTTP/HTTPS.

I was surprised that Zend Framework (1.5), at the time of writing, still has no component for SOAP. Actually such component exists, but it’s still in incubator, so if you want to use it, you have to get current Zend Framework version from SVN (svn checkout http://framework.zend.com/svn/framework/trunk) or to download the latest snapshot.

What I really needed was WSDL generator because standard PHP SOAP extension does not support WSDL generation.

Zend_Soap component has a very handy helper, Zend_Soap_AutoDiscover, which will generate WSDL file using PHPDoc (PHP has no static typing, so API docs are the only way to get the required info, this accomplished using reflection functionality of PHP).

Note: At the time of writing Zend_Soap_AutoDiscover fails to create a valid WSDL if the methods of your object have default parameters. So make sure you don’t have them… If you must have them, you can use Zend_Soap_Wsdl to manually create WSDL. Edit: this issue is now fixed.

To use Zend_Soap_AutoDiscover you have to document all params, their types and return types. Lets say, we want a service to log some messages. For actual logging we have a class called Writer, which has a write method, that just writes given string to a file (the params are described in PHPDoc):

<?php
 
class Writer {
 
    private static $fileName = 'message.txt';
 
    /**
     * Write to a file
     *
     * @param string $string
     * @return boolean
     */
    public function write($string) {
        try {
            $file = new File(self::$fileName);
            $file->lock();
            $result = $file->write($string);
            $file->unlock();
            $file->close();
            return $result;
        } catch (Exception $e) {
            return false;
        }
    }
}
 
?>

And here’s a little action, which renders generated WSDL for this class using Zend_Soap_AutoDiscover:

...
/**
 * Action that renders WSDL
 * Controller: IndexController
 */
public function wsdlAction() {
    $wsdl = new Zend_Soap_AutoDiscover();
    $wsdl->setClass('Writer');
    $wsdl->handle();
    exit;
}
...

Now if you go to http://yourhost/index/wsdl you should see a properly generated WSDL of your web service. Please note, under default conditions, Zend_Soap_AutoDiscover will register URL of your index.php file as your SOAP service URL.

Lets expose our Writer and it’s write method – for that, create a SOAP server using the URL of WSDL. I don’t see any reasons to use Zend_Soap_Server for this example, so I’m using PHP SOAP extension’s functionality:

...
/**
 * SOAP server action
 * Controller: IndexController
 */
public function indexAction() {
    $server = new SoapServer('http://yourhost/index/wsdl');
    $server->setClass('Writer');
    $server->handle();
    exit;
}
...

After the server is ready, you can test your service with SoapClient. If message is successfully logged you should get true in the response:

...
$client = new SoapClient('http://yourhost/index/wsdl');
$response = $client->write($message);
var_dump($response); // true or false
...

That’s it. Now you should be able to easily create the WSDL for your object, set up a SOAP server and to test it with a SOAP client.

28 Responses to SOAP web service with Zend Framework

  1. Steve 2008-07-06 at 04:20:10

    Looks like a great example. If your ZF setup conforms to MVC, where would you play the Writer class in this instance? Is it in the same file as your IndexController.php?

  2. Viktoras Agejevas 2008-07-06 at 09:31:52

    Don’t know if I understood the question correctly. If you are talking about the actual location of the file, then no, it’s in directory “lib”, because it’s just some util.

  3. yuki 2008-09-10 at 08:29:31

    Hi Viktoras, I am getting the following error when trying to expose the Writer and it’s write method using PHP SoapServer() function, any idea?

    SOAP-ENV:Server
    Bad Request. Can’t find HTTP_RAW_POST_DATA

  4. Viktoras Agejevas 2008-09-10 at 10:16:11

    yuki,

    have no idea, but this might be related to this bug:
    http://bugs.php.net/bug.php?id=41293

    It affects PHP 5.2.2, what version do you use?

  5. Junkmyfunk 2008-09-23 at 12:46:15

    Now that this component has been included in the main trunk with Zend Framework 1.6, I thought I’d try setting up a SOAP server using it. However, I have hit a problem with registering the functions or classes to use with the server:

    $server->addFunction(‘newRequest’);

    throws an exception saying “Invalid function specified”, even though it’s defined just below, in the same file.

    The setClass() method won’t work either – it says “Class ‘xxxx’ does not exist”. I have put an Api folder in my library folder with a file called Request.php but “Api_Request” doesn’t get found. I tested with “Zend_Acl” and it finds the file Acl.php in the library/Zend folder.

    Any ideas would be much appreciated – it’s driving me nuts.

  6. Junkmyfunk 2008-09-23 at 14:07:11

    Nevermind – turns out you actually have to include the class file at the top of the IndexController and then just put it’s name in the setClass parameter. There’s another 2 hours of my life down the drain.

  7. Fordnox 2008-10-15 at 12:12:19

    Correct me if I’m wrong but, I had a problem with Zend_Soap_AutoDiscover
    Function setClass() sets
    $uri = Zend_Uri::factory(‘http://’ . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME']);
    which is not always correct, it is the url which all webservice actions are directed.
    I am making webservice as a module, and url it creates is always wrong. so i changed it to:
    $uri = Zend_Uri::factory(Zend_Registry::get(‘config’)->module->api->wsdl->listen);

    and it works great

  8. Pingback: PHP Soapclient « Mcloide’s resources library

  9. E L Elza 2008-10-31 at 18:50:37

    Thanks for the tutorial!

  10. Fordnox 2008-11-13 at 17:52:42

    Server function, This function returns simple php array.

    /**
    * @desc Get Product info by product_id
    * @param int $product_id
    * @return array
    */
    public function getProduct($product_id)
    {
    $result = array(
    “product_id”=>$product_id,
    “title”=>’Product Title’
    );
    return $result;
    }

    Client gets such response from SoapServer

    $client->getProduct(2);

    product_id
    2

    title
    Product Title

    I want this result to be:

    2
    Product Title

    How do i do that ?

  11. Fordnox 2008-11-13 at 18:16:00

    Sorry for the previuos comment, but can somebody help me on problem, that i have explained over here http://www.fordnox.com/blog/2008/11/zend-frameword-soap-server/

  12. D0uDa 2008-11-26 at 00:56:48

    Nice, thanks.

  13. imehesz 2008-12-10 at 15:52:59

    hello,

    i followed this article to create my SOAP server (i’m using the latest ZF 1.7.1)

    I created the a SoapModel where i’m storing the Soap functions. If I point my browser to the soap controller (http://MYSERVER/soap/) i get a nicely formed XML document stating that:

    SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://MYSERVER/soap/wsdl’

    So, I’m checking the given WSDL link:

    http://MYSERVER/soap/wsdl

    and I get a:

    Invalid URI supplied

    I’ve read about a bug in 1.6 but it’s supposed to be fixed by now. So maybe I am missing something :(

    Any ideas?

    Thanks for the help
    –iM

  14. marlon990 2008-12-18 at 02:24:04

    before then , ex me my bad english but i want to help about the Zend_Soap:

    When you declarate a new Zoad_Soap_Server you need to declarate an this form:

    $sServer = new Zend_Soap_Server(‘../webservices/calculadora.wsdl’, array(‘actor’=>’urn:BasicAPI’, ‘soap_version’ => SOAP_1_2));

    first you need to define when state your archive .wsdl, and the client is same:

    $sClient = new Zend_Soap_Client(‘http://localhost/suburban/webservices/calculadora.wsdl’);

    and your file .wsdl

    EN PHP –>

    Note:
    If you used Zend_Layout in the action for you serve need to disable

    public function wsdlAction(){
    $this->_helper->layout->disableLayout();

    ini_set(“soap.wsdl_cache_enabled”, “0″);
    $Server = new Zend_Soap_Server(‘../webservices/calculadora.wsdl’, array(‘actor’=>’urn:BasicAPI’, ‘soap_version’ => SOAP_1_2));
    $sServer->setClass(“Calculadora”);
    $sServer->handle();

    }

  15. Roy 2009-03-13 at 20:18:10

    Hi, I was trying to follow the instruction above. I got the wsdl when I point my browser to “http://mysite.com/index/wsdl”

    But when I try my client “http://mysite.com/index/test”

    It just run forever, and then (after 3 minutes ) shows my test.phtml file with nothing else.

    Below is the code ( wsdlurl is fake ), please let me know if you see any problem??

    Thanks

    setClass(‘Greeter’);
    $wsdl->handle();
    exit;
    }

    public function indexAction() {
    //$server = new Zend_Soap_Server($this->wsdlUrl);
    $server = new SoapServer($this->wsdlUrl);
    $server->setClass(‘Greeter’);
    $server->handle();
    exit;
    }

    public function testAction() {
    try{
    //$client = new Zend_Soap_Client($this->wsdlUrl);
    $client = new SoapClient($this->wsdlUrl);
    $response = $client->greet(“Roy”);
    var_dump($response);
    } catch (Exception $e) {
    print_r($e);
    }
    }
    }
    ?>

  16. Pingback: Use soap on Zend framework | Jonathan Wijaya Loe

  17. Raffy 2009-08-25 at 15:38:32

    For the sake of best practise and performance, cache the rendered WSDL file if your webserver has a medium to heavy request rate.

    Or use a separate script to render the WSDL once using output buffering and then storing the file.

    Since your WSDL file won’t change much in a production environment, it shouldn’t be necessary to use the autodiscovery function over and over. I also doubt it’s meant for usage as described, though it’s a good tutorial.

  18. Software Developer 2009-08-28 at 11:58:06

    Seems good work but the service is a bit too simple to be a good example for me.

    Insted of exit in index Action, you could just disable the layout and rendering by.
    Which is a bit more useful.

    $this->_helper->viewRenderer->setNoRender();
    $this->_helper->layout->disableLayout();

  19. Amila 2010-01-04 at 09:50:18

    Im trying to implement the service in a separate ZF project & client in a separate ZF project.

    I have designed my class in the same manner as explained above.

    class IndexController extends Zend_Controller_Action
    {

    public function indexAction()
    {
    $wsdl=new SoapServer(‘http://localhost/db/public/index/wsdl/’);
    $wsdl->setClass(‘admin’);
    $wsdl->handle();
    exit();
    }

    public function wsdlAction(){
    $wsdl=new Zend_Soap_AutoDiscover();
    $wsdl->setClass(‘admin’);
    $wsdl->handle();
    exit();
    }
    }

    http://localhost/db/public/index/wsdl/ does generate the WSDL wihtout any errors.

    But when i call it from the client i get a soap fault.

    $client=new SoapClient(‘http://localhost/slcdb/public/index/wsdl/’); $response=$client->GetAdminInfo(‘Administrator’);
    var_dump($response);
    exit();

    Error::

    SoapFault exception: [VersionMismatch] Wrong Version in C:\Program Files\Apache Software Foundation\Apache2.2\www\client\application\controllers\IndexController.php:27 Stack trace:

    #0 [internal function]: SoapClient->__call(‘GetAdminInfo’, Array)
    #1 C:\Program Files\Apache Software Foundation\Apache2.2\www\client\application\controllers\IndexController.php(27): SoapClient->GetAdminInfo(‘Administrator’)
    #2 C:\ZendFramework-1.9.4\library\Zend\Controller\Action.php(513): IndexController->indexAction()
    …..

    What could be the cause of it?

    • Viktoras Agejevas 2010-01-04 at 10:44:34

      Hi Amila,

      I think that wrong version if WSDL might be cached. To fix it you can try one of things:

      1. Delete it by hand, by default WSDL cache is located in /tmp directory.
      2. Set ini_set(“soap.wsdl_cache_enabled”, “0″); in you program.

      • Amila 2010-01-05 at 11:43:38

        hi,

        i checked the /tmp folder; its empty

        i did check using ini_set(“soap.wsdl_cache_enabled”, “0″);

        still i get the same error

      • Michal 2010-01-06 at 00:22:06

        I’m also getting the same error in the client. ZF 1.9.4 + Zend_Soap_AutoDiscover. WSDL generation works fine.

        public function clientAction() {
        $client = new Zend_Soap_Client(“http://parcel-ws.localhost.com/webservice/wsdl”);
        $string = $client->hello();
        var_dump($string);
        exit;
        }

      • Viktoras Agejevas 2010-01-06 at 10:06:01

        Sorry, probably can’t help here. It’s been about two years since I last used PHP. ZF also probably changed a lot since v1.5.

  20. Elwin 2010-01-14 at 02:09:04

    Pfff sorry for the spam, but this is the actually working code the index and wsdl actions have to be merged to 1 indexAction with a check for the get ?wsdl.

    controllers/IndexController.php
    
    class IndexController extends Zend_Controller_Action
    {
    
        public function init()
        {
            /* Initialize action controller here */
        	$this->_helper->viewRenderer->setNoRender();
    		ini_set("soap.wsdl_cache_enabled",0);
        	require_once APPLICATION_PATH . '/soap/Remote.php';
        }
    
    	/**
    	 * SOAP server action
    	 * Controller: IndexController
    	 */
    	public function indexAction() {
    		if( isset( $_GET['wsdl'] ) ) {
    			$autodiscover = new Zend_Soap_AutoDiscover();
    			$autodiscover->setClass( 'Remote' );
    			$autodiscover->handle();
    		} else {
    			$server = new Zend_Soap_Server("http://localhost:8080/Soap/public/index/?wsdl");
    			$server->setClass('Remote');
    			$server->handle();
    		}
    	}
    }
    
    controllers/ClientController.php
    
    class ClientController extends Zend_Controller_Action
    {
    
        public function init() {
        	$this->_helper->viewRenderer->setNoRender();
    		ini_set("soap.wsdl_cache_enabled", 0);
        }
        
        public function indexAction() {
    		$client = new SoapClient('http://localhost:8080/Soap/public/index/?wsdl');
    		$response = $client->__call( "validate", array("blah") );
    		var_dump($response); // true or false
        }
    }
    
    soap/Remote.php
    
    class Remote {
    
        /**
         * Validate session
         *
         * @param string $string
         * @return boolean
         */
        public function validate( $string ) {
            try {
                return true;
            } catch (Exception $e) {
                return false;
            }
        }
    }
    
    • Emperor 2010-03-07 at 01:19:30

      You are so stupid man. Your example no working the same error and where you read that funking bullshit about this merge in one action and ?wsdl.

  21. Riki Risnandar 2010-09-02 at 20:40:47

    nice tutorial using soap with zend framework .. thanks

  22. plutov 2011-09-23 at 08:00:19

    You can find some additional information about zend_soap and web-services here http://plutov.by/post/web_service_soap

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 )

Connecting to %s

Follow

Get every new post delivered to your Inbox.