Building a Web Service Client using the Zend Framework

UPDATE: Interested in creating a web API? Read this post instead.

In this post I’ll demonstrate how to use the Zend Framework to quickly and easily develop a Web application that consumes a Web API as a Web service.

The Zend Framework puts heavy emphasis on Web services. This is a good thing, considering the amount of Web services out there that can help lower costs and increase the value of your site.

The various steps that are involved in creating a Web service client are as follows:

  1. Find a Web Service provider.
  2. Identify Web service endpoints.
  3. Identify query string parameters to endpoints.
  4. Identify response types.
  5. Create a proxy object for the Web service.

Find a Web Service provider

In this example, I’ll develop a Web services client to Digg, a real-world Web services provider. The Digg API has been created to let users interact programmatically with Digg. The API accepts REST requests and offers several response types. More info here: http://apidoc.digg.com/

Identify Web service endpoints

A Web services endpoint is a resource where Web services messages can be targeted. For example:

GET /stories
    All stories.
GET /stories/popular
    Popular stories.
GET /stories/topic//popular
    Popular stories from a given topic.

Let’s assume you only need to target a single endpoint, for example, fetch popular stories by topic:

http://services.digg.com/stories/topic/programming/popular/

Identify query string parameters to endpoints

You can pass additional arguments to a Web service by specifying additional parameters and then passing values for those parameters. In theory, a GET should retrieve a representation of a resource identified by a URI, but many APIs make it extremely easy to view the URI not as a resource identifier, but as a convenient means to encode parameters.

A good example of this is the Digg API that allows you to specify parameters in the query string. For example:

appkey
    The value of the application key.
sort
    How to sort returned stories.
count
    Number of stories to retrieve.
offset
    Offset in complete story list.
domain
    Partial domain of linked article.
link
    URL of linked article.

Identify Response Types

The Digg API provides several response types, each designed to be useful in a variety of programming contexts:

  • text/xml: XML response type
  • application/json: JSON response type
  • text/javascript: Javascript response type
  • application/php: Serialized PHP response type

Create a proxy object for the Web service

We are now ready to create a proxy object to extract data from the Web service. Because the Digg API accepts REST requests, we’ll create a Zend_Service_Digg class that extends Zend_Rest_Client, this allows us to take advantage of some predefined accessor and mutator methods, such as getUri() and setUri():

class Zend_Service_Digg extends Zend_Rest_Client
{
    protected $_uri = 'http://services.digg.com/';

    public function __construct()
    {
        $this->setUri($this->_uri);
        $client = self::getHttpClient();
        $client->setHeaders('Accept-Charset', 'ISO-8859-1,utf-8');
    }
...

Digg provides different response types, so we need to create a method to allow the user define the context (Codepad):

...
    protected $_responseType = 'xml';
    protected $_responseTypes = array('php', 'xml', 'json');

    public function setResponseType($responseType)
    {
        if (!in_array(strtolower($responseType), $this->_responseTypes)) {
            throw new Zend_Service_Digg_Exception('Invalid Response Type');
        }

        $this->_responseType = strtolower($responseType);
        return $this;
    }

    public function getResponseType()
    {
        return $this->_responseType;
    }
...

Now we’ll create a setter and getter method to pass and retrieve additional parameters (Codepad):

...
    protected $_params = array();

    public function setParams($params)
    {
    	// Validate mandatory parameters to endpoint
    	if (!array_key_exists('appkey', $params)) {
            throw new Zend_Service_Digg_Exception('Param appkey missing');
        }

        foreach ($params as $key => $value) {
            switch (strtolower($key)) {
                case 'type':
                    $this->setResponseType($value);
                    break;
                default:
                    $this->_params[$key] = $value;
                    break;
            }
        }

        return $this;
    }

    public function getParams()
    {
    	return $this->_params;
    }
...

All the values of the arguments must be URL encoded. Fortunately, Zend_Rest_Client takes care of this for us. Next, we’ll create a method that fetches the most popular stories by a given topic (Codepad):

...
    public function fetchPopularStoriesByTopic($topic, array $params = array())
    {
        $this->setParams($params);

        $path = sprintf('/stories/topic/%s/popular/', trim(strtolower($topic)));
        return $this->sendRequest('GET', $path);
    }
...

And finally, we create 2 methods, one that sends the request and another one that formats the response data (Codepad):

...
    public function sendRequest($requestType, $path)
    {
        $requestType = ucfirst(strtolower($requestType));
        if ($requestType !== 'Post' && $requestType !== 'Get') {
            throw new Zend_Service_Digg_Exception('Invalid request type: ' . $requestType);
        }

        try {
            $requestMethod = 'rest' . $requestType;
            $response = $this->{$requestMethod}($path, $this->getParams());
            return $this->formatResponse($response);
        } catch (Zend_Http_Client_Exception $e) {
            throw new Zend_Service_Digg_Exception($e->getMessage());
        }
    }

    public function formatResponse(Zend_Http_Response $response)
    {
        if ('json' === $this->getResponseType()) {
            return $response->getBody();
        } elseif ('php' === $this->getResponseType()) {
            return unserialize($response->getBody());
        } else {
            return new Zend_Rest_Client_Result($response->getBody());
        }
    }
}

Usage

$digg = new Zend_Service_Digg();

$params = array('type'=>'json', 'appkey'=>'http://api.test.com');
$stories = $digg->fetchPopularStoriesByTopic('programming', $params);

Response:

http://services.digg.com/stories/topic/programming/popular/?type=json&appkey=http%3A%2F%2Fapi.test.com

Zend_Service_Digg class

That’s it! Time to open your editor and start building some cool WS clients :)

9 thoughts on “Building a Web Service Client using the Zend Framework

  1. Hi! Nice tut but I have some comments. Fisrtly package shoul not be named Zend_* , lets leave zf directory untouched as is. Secondary – my personal opinion you shouldnt extend Zend_Rest_Client. Its better to have an instance of client in some protected variable. And last one idea – all responces should be checked against errors, that I’m sure most of apis throws.

  2. Hi Silverstom,

    > Firstly package should not be named Zend_*

    Why no? The idea is to encourage people to create more Zend_Service_* components.

    > my personal opinion you shouldnt extend Zend_Rest_Client

    Good point. I got the idea from Zend_Service_Twitter. I guess the logic behind this is, if Matthew did it, then it’s fine :)

    > all responses should be checked against errors

    True, but keep in mind this article only covers the basics.

    Thanks

  3. Hi, Federico. Very nice article. Waiting for howto on writing web services (server/provider).

    Btw, smth wrong with this:

    if (!isset($params[‘appkey’]) || !isset($params[‘appkey’])) {

  4. Hey guys,

    Firstly, i’m a MS developer by trade.. but i’m getting into php (see.. i do have atleast 1 redeeming quality) and i want to use php to attach to a MS.net web service. Now, i’ve tried a bunch of different scripts, and i just can’t get it to work. So, I’m modifying a 1 page grab to get xml from my service, parse it, and display it on a page..

    What do i need from zen to actually get started trying to write this.. do i need one of your librarys, or the software itself? What needs to be included in the website?

    I’m not familiar with zend, nor php.. if you have an article that has a ‘if you are n00b enough to only know what n00b means….start here’ kinda of tutorial.

    I’ve tried to find tutorials on connecting to a MS.NET web service, and i’m not really finding anything about it.

    Thanks for your help.

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 )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s