Introducing the Webservice Behavior

As webservices grow so does the need for being able to communicate with them in an easy fashion. This simple blog alone uses 2 webservices. The recent tracks at the bottom is a feed I pull from Last.fm and my spam protection is provided by Akismet. When first building my site I looked for an already built solution and found a partial solution in Felix Geisendörfer’s WebModel. However, I wanted to remove its dependancy on cURL as it is not always available, and transform it into a behavior. Transformation to a behavior was an easy process. Removing curl and manually writing all the Socket code was not an appealing prospect. Thankfully, CakePHP 1.2 has the new CakeSocket class which eases the creation of socket connections. The result is a behavior that has no extension dependancies and is a behavior for easy reuse. This is a PHP5 class, so if you are on PHP4 too bad.

Using the Webservice Behavior

Using the webservice behavior is quite easy. Simply add it to the $actsAs array for your model.

Show Plain Text
  1. $actsAs = array('webservice');

Is the most simple use of it. There are a number of setup options that you can set as well. They are mostly related to the socket connection, and in most cases don’t need much fiddling.

  • timeout The timeout for the requests defaults to 30 seconds.
  • persistent Should the connection be persistent, opened with pfsock.
  • defaultUrl The default URL to be used.
  • port The port for the connection defaults to 80.
  • protocol The protocol for connection defaults to tcp.
  • followRedirect Should redirects be followed. Defaults to false

Above are the configuration options that can be set in the model $actsAs array. They can also be set on any request through $options[‘options’]

Making Requests

Requests are made with the request() method. A simple use would look like:

Show Plain Text
  1. $this->Model->request('get');

This would make a request against the defaultUrl set in the model settings and return the content of that request. The request() method connects to the defaultUrl if no url is supplied. Several request types are supported with ‘get’, ‘post’ and ‘xml’ being the completed types. I have plans to add SOAP as well, as soon as I can wrap my head around the documentation. A second argument allows you set additional headers, data, url, and options for the connection.

Show Plain Text
  1. $this->Model->request('get', array('data' => array('q' => 'mark story'), 'url' => 'www.google.ca/search')));

The above would do a search on google for ‘mark story’.

Debugging your Requests

There is built-in capabilities to introspect on what is going on in your request calls. Using getRequestInfo() will return an array of information pertaining to the last made request. Headers for both the request and response, as well as cookies, data and connection options will be returned. I found this to be very handy in my own development, and I hope you will as well.

Bonus Round XMLRPC

As a bonus when downloading the WebserviceBehavior you get an XmlRpcMessage class as well. This is a very simple class to enable the transmission of XMLRPC requests. I haven’t done any testing with complicated payloads. But for simple requests it works quite well. When making requests with the type of xml, supplied data is automatically converted into an XMLRPC message and sent for you. The one caveat is that you need to supply a methodName as well.

Show Plain Text
  1. $data = array(
  2.     'methodName' => 'testFunc',
  3.     'data' => array(
  4.         'foo', 'bar', 1
  5.     )
  6. );
  7. $result = $this->Model->request('xml', array('data' => $data));

This will format up an XML message and send it. You can also you the XMLRPC class on its own of course. It is a full class with a usable interface. But that is another day and another article. Included with the class are some tests, they cover the typical use cases that I have come across so far, but will be expanded as I use it more, so check back for updates to the classes and tests.

As always I’d love to hear any feedback you have for this, and I hope you find them useful.

Update I’ve added in Kim’s suggestion to allow multi-dimensional array to be posted. Thanks Kim.

Update I’ve moved the webservice behavior to GitHub You can find the newest version there. The link below will be kept for historical reference.

Download WebserviceBehavior

Comments

This behaviour looks fantastic and it’s just what I’m after. I’m in the middle of building a CakePHP replacement for my wordpress blog and would love any tips on getting this to work with akismet :)

Also wanted to thank you for your Geshi helper, this is turning out to be an awesome CakePHP related blog.

anonymous user on 7/4/08

Glad you found it useful David, I’ve got plans to release my Akismet model here as well. But if you can’t wait the API documentation gives a good overview on how to do. The API call you want is ‘comment-check’

mark story on 7/4/08

Mark, really nice code! Thanks for sharing it, I´m gonna use it in my next app, that´s for sure! One detail, in your last example, I guess your variable name is called $vars, or the inverse in the request call…

anonymous user on 7/5/08

Hi Mark,

Great behavior! I have made a little enhancement to the _formatUrlData function to allow requests with multidimensional arrays and strings. Hope it’s readable :

	protected function _formatUrlData($params) {
		if (is_array($params)) {
			$this->_data = http_build_query($params, '', '&');
		} else {
			$this->_data = urlencode($params);
		}
		$this->__setInfo('data', $this->_data);
	}

anonymous user on 7/9/08

Very nice! I’m needing this im my company. I have created a datasource, but not is the best. This is better.

When you not use a database, simple make $useTable = false in model?

Some issues: – In setup (line 104), use array_merge until am. – In line 342, use isset($heads[‘Set-Cookie’]. This is better than array_key_exists. – I’m not tested in action, but the behavior not parse XMLRPC response?

Thanks for all.

anonymous user on 10/3/08

Nicely done.. Was trying it out and on sites that return bad cookie parts, the setCookie sometimes echoes a Notice: Undefined offset: 1 in /usr/www/dev/indv/jad/monitorize/models/behaviors/webservice.php on line 229

Basically because the part in question just doesn’t include any ‘=’

Adding an eregi('=', $part) check right before it solves it.

Thanks again!

anonymous user on 10/8/08

gthub says “site does not exist” for download. git clone does not work either because a “private key” failure. I really do not understand why people do not stay with established technologies and providers of FREE services – I NEVER had a download problem with sf.net in the last years and github just seems to make trouble every second day.

J0NES on 1/21/09

Jones: There are several guides on github about how to use it. Did you use the public clone url? Furthermore, there is no cost to use github. As for why not to use sourceforge, well for one there is no support for git. When I started looking for a place to host my code, I was also interested in learning to use git. Github was an excellent way to learn how to use git and be able to share code at the same time. I’m sorry that doesn’t float your boat. I know of many people other than myself who use github daily without trouble, so it can’t be all that bad.

mark story on 1/21/09

This looks great guys. Where is the example on “How to use” that!!! There should be minimum set of controller, model and view to describe further.

Chirayu on 4/26/10

Comments are not open at this time.