<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Gareth Jones &#187; get</title>
	<atom:link href="http://blog.garethj.com/tag/get/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.garethj.com</link>
	<description></description>
	<lastBuildDate>Tue, 07 Sep 2010 17:37:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Building a RESTful Web application with PHP</title>
		<link>http://blog.garethj.com/2009/02/building-a-restful-web-application-with-php/</link>
		<comments>http://blog.garethj.com/2009/02/building-a-restful-web-application-with-php/#comments</comments>
		<pubDate>Tue, 17 Feb 2009 16:52:22 +0000</pubDate>
		<dc:creator>gareth</dc:creator>
				<category><![CDATA[techy solutions]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[client applications]]></category>
		<category><![CDATA[delete]]></category>
		<category><![CDATA[format]]></category>
		<category><![CDATA[get]]></category>
		<category><![CDATA[header]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[post]]></category>
		<category><![CDATA[put]]></category>
		<category><![CDATA[request]]></category>
		<category><![CDATA[response]]></category>
		<category><![CDATA[rest]]></category>
		<category><![CDATA[REST Service]]></category>
		<category><![CDATA[rewriting]]></category>
		<category><![CDATA[status]]></category>
		<category><![CDATA[status code]]></category>
		<category><![CDATA[url]]></category>
		<category><![CDATA[Web application]]></category>
		<category><![CDATA[Web interfaces]]></category>

		<guid isPermaLink="false">http://blog.garethj.com/?p=127</guid>
		<description><![CDATA[Recently I&#8217;ve been putting together a Web application for a research project. I decided it was about time I really looked properly into REST so my Web interfaces are better structured. I won&#8217;t go into all the benefits here, you &#8230; <a href="http://blog.garethj.com/2009/02/building-a-restful-web-application-with-php/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Recently I&#8217;ve been putting together a Web application for a research project. I decided it was about time I really looked properly into <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST</a> so my Web interfaces are better structured. I won&#8217;t go into all the benefits here, you can <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer#Claimed_benefits">read for yourself</a>. Suffice to say it seems like a good approach to take.</p>
<p>This is quite a long article and you might only be interested in some of it so here are the sections:</p>
<ul>
<li><a href="/2009/02/17/building-a-restful-web-application-with-php/#requestData">Getting access to request data</a></li>
<li><a href="/2009/02/17/building-a-restful-web-application-with-php/#returningResponses">Returning appropriate responses</a></li>
<li><a href="/2009/02/17/building-a-restful-web-application-with-php/#dataFormats">Handling different data formats</a></li>
<li><a href="/2009/02/17/building-a-restful-web-application-with-php/#singleScript">Passing all requests to a single PHP script</a></li>
<li><a href="/2009/02/17/building-a-restful-web-application-with-php/#requestedUrl">Determining the full requested URL</a></li>
<li><a href="/2009/02/17/building-a-restful-web-application-with-php/#genericService">A generic REST Service class</a></li>
</ul>
<p>If you have an suggestions for improvement, please let me know &#8211; this was a first attempt!<br />
<span id="more-127"></span></p>
<p>Some credit for this article should go to <a href="http://www.lornajane.net/">lornajane</a> for her PHP REST Server articles (<a href="http://www.lornajane.net/posts/2008/PHP-Rest-Server-part-1-of-3">Part 1</a>, <a href="http://www.lornajane.net/posts/2008/PHP-Rest-Server-part-2-of-3">Part 2</a>, <a href="http://www.lornajane.net/posts/2008/PHP-Rest-Server-part-3-of-3">Part 3</a>) which I used as a good starting point.</p>
<p><strong><a name="requestData">Getting access to request data</a></strong></p>
<p>Getting access to HTTP <a href="http://php.net/manual/en/reserved.variables.get.php">GET arguments</a> or <a href="http://php.net/manual/en/reserved.variables.post.php">POST data</a> is well known and easy. With a REST interface you are likely to support other HTTP verbs such as PUT and DELETE. Getting data from these requests is not obvious but luckily <a href="http://www.lornajane.net/">lornajane</a> had <a href="http://www.lornajane.net/posts/2008/Accessing-Incoming-PUT-Data-from-PHP">worked this out</a> for me:<br />
<code><br />
parse_str(file_get_contents('php://input'), $arguments);<br />
</code></p>
<p><strong><a name="returningResponses">Returning appropriate responses</a></strong></p>
<p>With REST you should really make use of the HTTP <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">Status Codes</a> for all requests (rather that using <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a> for everything). In PHP, this can be done with the <a href="http://php.net/header"><code>header</code></a> function in a couple of ways:<br />
<code><br />
// When setting just the status code<br />
header('HTTP/1.1 405 Method Not Allowed');<br />
// When you're setting another header at the same time<br />
header('Allow: GET, HEAD, POST, DELETE', true, 405);<br />
</code></p>
<p>I haven&#8217;t found a way of sending just the status code number (e.g. 405) without specifying the exact text (&#8216;Method Not Allowed&#8217;) but these are part of a fixed specification so it&#8217;s not a huge problem.</p>
<p>The following is a list of status codes that I&#8217;ve used and why:</p>
<ul>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a>: successful request when data is returned</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">201 Created</a>: Successful request when something is created at another URL (specified by the value returned in the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30">Location</a> header)</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">204 No Content</a>: Successful request when no data is returned</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a>: Incorrect parameters specified on request</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404 Not Found</a>: No resource at the specified URL</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6">405 Method Not Allowed</a>: when a client makes a request using an HTTP verb not supported at the requested URL (supported verbs are returned in the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7">Allow</a> header)</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7">406 Not Acceptable</a>: Requested data format not supported</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a>: An unexpected error occurred</li>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2">501 Not Implemented</a>: when a client makes a request using an unknown HTTP verb</li>
</ul>
<p><strong><a name="dataFormats">Handling different data formats</a></strong></p>
<p>As URLs are supposed to represent resources in REST, it is a feasible requirement that client applications would like to receive responses from the same URL in different formats. Clients can specify the data formats they are capable of understanding using the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">HTTP Accept header</a>. In Javacript this can be set using the <a href="http://www.w3.org/TR/XMLHttpRequest/#setrequestheader"><code>setRequestHeader</code></a> method on the <a href="http://www.w3.org/TR/XMLHttpRequest/">XMLHttpRequest object</a>. Using jQuery as my current Javascript library of choice, the code for doing this is as follows:<br />
<code><br />
$.ajax({<br />
&nbsp;&nbsp;type: method, // GET, POST, PUT, DELETE etc.<br />
&nbsp;&nbsp;url: url, // The actual URL to make the request<br />
&nbsp;&nbsp;data: data,  // Any data/parameters to send to the server<br />
&nbsp;&nbsp;beforeSend: function(xmlHttpRequest) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;xmlHttpRequest.setRequestHeader('Accept', format); // <a href="http://www.w3schools.com/media/media_mimeref.asp">MIME Type</a><br />
&nbsp;&nbsp;}<br />
});<br />
</code></p>
<p>This then needs to be handled by the server. In PHP this data can be retrieved using <a href="http://uk3.php.net/reserved.variables.server">$_SERVER['HTTP_ACCEPT']</a>. I also found a handy library for parsing this header according to <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">the specification</a> (not trivial), cleverly named &#8216;<a href="http://kevinlocke.name/programs/http_accept.php">HTTP_ACCEPT</a>&#8216;. I then wanted a way of knowing the most appropriate format from a list of formats I would support so I threw the following code together:<br />
<code><br />
$accept = new HTTP_Accept($_SERVER['HTTP_ACCEPT']);<br />
foreach ($supportedFormats as $supportedFormat) {<br />
&nbsp;&nbsp;$supportedFormatQuality = $accept->getQuality($supportedFormat);<br />
&nbsp;&nbsp;if ((!isset($bestFormat) &#038;&#038; $supportedFormatQuality > 0) ||<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $bestQuality < $supportedFormatQuality) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;$bestFormat = $supportedFormat;<br />
&nbsp;&nbsp;&nbsp;&nbsp;$bestQuality = $supportedFormatQuality;<br />
&nbsp;&nbsp;}<br />
}<br />
</code></p>
<p>The server also needs to specify the type of content it is returning; this can be done using the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header as follows:<br />
<code><br />
header("Content-Type: $format");<br />
</code></p>
<p><strong><a name="singleScript">Passing all requests to a single PHP script</a></strong></p>
<p>I needed all requests with a certain URL prefix to be handled by a single script. This was easily solved using the following URL re-writing rule in a <a href="http://httpd.apache.org/docs/trunk/howto/htaccess.html">.htaccess</a> file:<br />
<code><br />
Options +FollowSymLinks<br />
RewriteEngine on<br />
RewriteRule ^.*$ index.php<br />
</code></p>
<p>Note that I had to enable the <a href="http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html">mod_rewrite</a> module in Apache and ensure that directives could be <a href="http://httpd.apache.org/docs/2.0/mod/core.html#allowoverride">overridden</a> with the following:<br />
<code><br />
&lt;Directory /var/www/&gt;<br />
&nbsp;&nbsp;AllowOverride All<br />
&lt;/Directory&gt;<br />
</code></p>
<p><strong><a name="requestedUrl">Determining the full requested URL</a></strong></p>
<p>Another requirement of my application complicated by URL rewriting was that I needed access to the full URL. I achieved this with the following bit of code:<br />
<code><br />
$protocol = $_SERVER['HTTPS'] == 'on' ? 'https' : 'http';<br />
$location = $_SERVER['REQUEST_URI'];<br />
if ($_SERVER['QUERY_STRING']) {<br />
&nbsp;&nbsp;$location = substr($location, 0, strrpos($location, $_SERVER['QUERY_STRING']) - 1);<br />
}<br />
$url = $protocol.'://'.$_SERVER['HTTP_HOST'].$location;<br />
</code></p>
<p><strong><a name="genericService">A generic REST Service class</a></strong></p>
<p>To allow me to reuse some of the REST code I'd created, I put it into a generic REST Service class which I subclassed and replaced different methods for different means:</p>
<pre>
class RestService {

  private $supportedMethods;

  public function __construct($supportedMethods) {
    $this->supportedMethods = $supportedMethods;
  }

  public function handleRawRequest($_SERVER, $_GET, $_POST) {
    $url = $this->getFullUrl($_SERVER);
    $method = $_SERVER['REQUEST_METHOD'];
    switch ($method) {
      case 'GET':
      case 'HEAD':
        $arguments = $_GET;
        break;
      case 'POST':
        $arguments = $_POST;
        break;
      case 'PUT':
      case 'DELETE':
        parse_str(file_get_contents('php://input'), $arguments);
        break;
    }
    $accept = $_SERVER['HTTP_ACCEPT'];
    $this->handleRequest($url, $method, $arguments, $accept);
  }

  protected function getFullUrl($_SERVER) {
    $protocol = $_SERVER['HTTPS'] == 'on' ? 'https' : 'http';
    $location = $_SERVER['REQUEST_URI'];
    if ($_SERVER['QUERY_STRING']) {
      $location = substr($location, 0, strrpos($location, $_SERVER['QUERY_STRING']) - 1);
    }
    return $protocol.'://'.$_SERVER['HTTP_HOST'].$location;
  }

  public function handleRequest($url, $method, $arguments, $accept) {
    switch($method) {
      case 'GET':
        $this->performGet($url, $arguments, $accept);
        break;
      case 'HEAD':
        $this->performHead($url, $arguments, $accept);
        break;
      case 'POST':
        $this->performPost($url, $arguments, $accept);
        break;
      case 'PUT':
        $this->performPut($url, $arguments, $accept);
        break;
      case 'DELETE':
        $this->performDelete($url, $arguments, $accept);
        break;
      default:
        /* 501 (Not Implemented) for any unknown methods */
        header('Allow: ' . $this->supportedMethods, true, 501);
    }
  }

  protected function methodNotAllowedResponse() {
    /* 405 (Method Not Allowed) */
    header('Allow: ' . $this->supportedMethods, true, 405);
  }

  public function performGet($url, $arguments, $accept) {
    $this->methodNotAllowedResponse();
  }

  public function performHead($url, $arguments, $accept) {
    $this->methodNotAllowedResponse();
  }

  public function performPost($url, $arguments, $accept) {
    $this->methodNotAllowedResponse();
  }

  public function performPut($url, $arguments, $accept) {
    $this->methodNotAllowedResponse();
  }

  public function performDelete($url, $arguments, $accept) {
    $this->methodNotAllowedResponse();
  }

}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.garethj.com/2009/02/building-a-restful-web-application-with-php/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
	</channel>
</rss>
