rekowski.info David Rekowski's random stuff

Using Zend_Soap_Client

2012-08-29

This article describes how to use the Zend_Soap_Client library to request a Soap Service. The client will be integrated into the Typo3 plugin we created in the Typo3 plugin extbase fluid tutorial. Nevertheless, the explanation of the integration can be easily adapted to a differing system context.

Keywords

Zend, SOAP, webservice, Typo3

1. Setup

I didn't find an easy way to download a single Zend component, so I fetched the full framework. I found out by trial and error, that Zend/Soap/Client depends on Zend/Soap/Server which in turn depends on Zend/Server/Interface. So I plucked those three folders and put it into a new lib folder in my plugin directory, namely webgrep/lib (actually I copied the plugin and named it soaptest, but let's leave it at that for now).

2. Tell PHP where to find the library

In order for PHP to find the library and especially all dependencies which are included by it, we need to expand the include path programmatically.

Warning: Generally, messing with the include path is not a good idea. Consider multiple plugins coming with their own version of Zend Components and extending the include path. This is bound to cause conflicts. Doing what we do now is only suited for educational purposes. Don't do this in production applications.

We add the following lines to the top of our controller class file AppController.php:

set_include_path(
    get_include_path() . PATH_SEPARATOR . realpath(__DIR__ . '/../../lib/')
);
require_once('Zend/Soap/Client.php');

Now we can use the Zend_Soap_Client class.

3. Using a webservice

The webservice we integrate is kindly provided by http://www.webservicex.net/. It features a currency converter. We intend to tell the user the current exchange rates for USD/EUR and vice versa.

We extend the formerly empty initializeAction() method and instantiate the soap client, which we store in the private property $soapClient. Again, in a real project, you would want to use some form of dependency injection for this in order to make it testable, if not write a separate class to separate the controller from the webservice request logic.

    private $soapClient;

    public function initializeAction() {
        $this->soapClient = new Zend_Soap_Client(
            'http://www.webservicex.net/CurrencyConvertor.asmx?WSDL'
        );
    }

Next, we write a new method that accepts a fromCurrency and a toCurrency string, queries the webservice with these options and returns the currency rate.

    private function getConversionRate($from, $to) {
        $parameters = array('FromCurrency' => $from, 'ToCurrency' => $to);
        return $this
            ->soapClient
            ->ConversionRate($parameters)
            ->ConversionRateResult;
    }

The SOAP client uses the WSDL passed at construction to determine, which methods are available, so we can use them directly. In this case, the method ConversionRate accepts the parameters FromCurrency and ToCurrency as defined in its WSDL. The resulting Object holds the property ConversionRateResult which we return.

To make it something useful, it remains to change the value assigned to the view.

    public function indexAction() {
        $this->view->assign(
            'result',
            'Conversion rate from EUR to USD is ' .
                $this->getConversionRate('EUR', 'USD') . '. ' .
                'Conversion rate from USD to EUR is ' .
                $this->getConversionRate('USD', 'EUR') . '.'
        );
    }

This is just a string stating both conversion rates. For real, much more flexible use, something like this would be more useful:

$result = array(
    array(
        'from' => 'EUR', 
        'to' => 'USD', 
        'rate' => $this->getConversionRate('EUR', 'USD')
    ),
    array(
        'from' => 'USD', 
        'to' => 'EUR', 
        'rate' => $this->getConversionRate('USD', 'EUR')
    )
);

But would require to alter the template, to iterate over the result, which we don't want to do right now.

Complete class

The complete new Classes/Controller/AppController.php file now looks like this:

<?php

set_include_path(
    get_include_path() . PATH_SEPARATOR . realpath(__DIR__ . '/../../lib/')
);
require_once('Zend/Soap/Client.php');

class Tx_Soaptest_Controller_AppController
    extends Tx_Extbase_MVC_Controller_ActionController {

    private $soapClient;

    public function initializeAction() {
        $this->soapClient = new Zend_Soap_Client(
            'http://www.webservicex.net/CurrencyConvertor.asmx?WSDL'
        );
    }

    public function indexAction() {
        $this->view->assign(
            'result',
            'Conversion rate from EUR to USD is ' .
                $this->getConversionRate('EUR', 'USD') . '. ' .
                'Conversion rate from USD to EUR is ' .
                $this->getConversionRate('USD', 'EUR') . '.'
        );
    }

    private function getConversionRate($from, $to) {
        $parameters = array('FromCurrency' => $from, 'ToCurrency' => $to);
        return $this
            ->soapClient
            ->ConversionRate($parameters)
            ->ConversionRateResult;
    }
}