close
Gen X Design | Ian Selby Gen X Design | Ian Selby
Search
Making RESTful Requests in PHP

APIs have become a very commonplace part of many popular web sites and services… especially REST APIs. I’ve already discussed how you can roll your own REST API for your PHP apps, but I’ve also received countless requests to go over how to actually make RESTful requests. That’s exactly what we’ll take a look at in this article. Before we dive in, however, I want to make sure you’ve got a basic understanding of how REST APIs work, so if you’re a bit shaky or unfamiliar with the concept, head on over to my previous article on REST and read up (you don’t need to go over the implementation stuff if you don’t want, just read the first part of the article). Seriously, do it… this article is written with the assumption you’re familiar with the concepts of REST.

Anyway, without any further delay, let’s take a look at what we’re going to cover…

The Basics of a RESTful Request

Every REST request consists of essentially the same basic parts:

  • The URL – This is the URL we’ll be making a request against (often referred to as a resource).
  • The Verb – GET, POST, PUT, or DELETE… there are some others out there, but these are the 4 most common ones.
  • The Params – The parameters we’ll be supplying to the API, often referred to as the request body.
  • Credentials – Username and password… we’ll cover HTTP Digest auth credentials.

And, of course, we’ll have a few pieces for our response as well:

  • The Response Body – The actual response body the API gave us.
  • The Response Status Code – The HTTP status code the API responded with.
  • Other Response Info – We’ll also have some other interesting info in the response.

Nothing really unexpected here, everything’s probably looking like what you’d expect: a request and a response.

So, on to the answer to the most important question: “How the heck do you actually make the request?!?!?”

REST & PHP – Curl to the Rescue

You may have already guessed we’d be using curl (or suspected it perhaps)… and if you didn’t, well, that’s what we’ll be using :) Anyway, if you’ve ever worked with Curl in PHP, you’re probably aware that making GET and POST requests are pretty straight-forward, but the PUT and DELETE requests are a little less obvious. Luckily, they’re pretty easy once you know how to do them, the problem is that they’re very poorly documented. We’ll go over them soon, but let’s get started with a little code. We’re going to be building a class that we can use to make requests and deal with their responses, so why don’t we build up a bit of a base class, and then start filling things in:

class RestRequest
{
	protected $url;
	protected $verb;
	protected $requestBody;
	protected $requestLength;
	protected $username;
	protected $password;
	protected $acceptType;
	protected $responseBody;
	protected $responseInfo;

	public function __construct ($url = null, $verb = 'GET', $requestBody = null)
	{
		$this->url				= $url;
		$this->verb				= $verb;
		$this->requestBody		= $requestBody;
		$this->requestLength		= 0;
		$this->username			= null;
		$this->password			= null;
		$this->acceptType		= 'application/json';
		$this->responseBody		= null;
		$this->responseInfo		= null;

		if ($this->requestBody !== null)
		{
			$this->buildPostBody();
		}
	}

	public function flush ()
	{
		$this->requestBody		= null;
		$this->requestLength		= 0;
		$this->verb				= 'GET';
		$this->responseBody		= null;
		$this->responseInfo		= null;
	}

	public function execute ()
	{

	}

	public function buildPostBody ($data = null)
	{

	}

	protected function executeGet ($ch)
	{		

	}

	protected function executePost ($ch)
	{

	}

	protected function executePut ($ch)
	{

	}

	protected function executeDelete ($ch)
	{

	}

	protected function doExecute (&$curlHandle)
	{

	}

	protected function setCurlOpts (&$curlHandle)
	{

	}

	protected function setAuth (&$curlHandle)
	{

	}
}

So, what we’ve got here is essentially all of the functions we’ll need to make this whole thing work right. We’ll cover each of the function individually, but let’s take a look at the class members:

  • $url – The URL we’ll be requesting against
  • $verb – The type of request we’ll be making (what verb to use)
  • $requestBody – The request body we’ll send with PUT and POST requests
  • $requestLength – An internally used variable for PUT requests (more on this later)
  • $username – The username to use for this request
  • $password – I’ll let you guess ;)
  • $acceptType – What kind of content we’ll accept as a response (not all APIs use this to determine the response format, but we’ll be robust)
  • $responseBody – The body of our response
  • $responseInfo – All the other goodness from our response (status code, etc.)

Not too rough, right? And if you take a look at the constructor, you’ll see things are pretty straight-forward as well. We’ve also got a flush function. This allows us to use the same object to make multiple requests by only clearing out certain member variables (notice username / password aren’t touched). You may have also noticed we’re missing getters / setters… this is simply to keep the code a little shorter. The final class will have them.

Making Requests – The Prep Work

Let’s go ahead and fill in some of the functions that do prep work for us, and then we’ll dig into making the requests. Anyway, let’s take a look at these “init” functions..

buildPostBody
This function will take an array and prepare it for being posted (or put as well):

	public function buildPostBody ($data = null)
	{
		$data = ($data !== null) ? $data : $this->requestBody;

		if (!is_array($data))
		{
			throw new InvalidArgumentException('Invalid data input for postBody.  Array expected');
		}

		$data = http_build_query($data, '', '&');
		$this->requestBody = $data;
	}

Notice how we will throw exceptions if we receive anything other than an array? This is important, as we don’t want our data to be malformed (or an error to be thrown by http_build_query). Also, you may be thinking that I’m using an exception type that isn’t defined anywhere… well, it is (if you’re using PHP 5). I won’t go off on a tangent, but check out the PHP SPL for more info. All you need to know is that the InvalidArgumentException is already defined for you :)

setCurlOpts
This function will take care of all the curl options common to all our requests:

	protected function setCurlOpts (&$curlHandle)
	{
		curl_setopt($curlHandle, CURLOPT_TIMEOUT, 10);
		curl_setopt($curlHandle, CURLOPT_URL, $this->url);
		curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($curlHandle, CURLOPT_HTTPHEADER, array ('Accept: ' . $this->acceptType));
	}

setAuth
If we’ve got a username and password set on the class, we’ll set up the auth options on the curl request with this function:

	protected function setAuth (&$curlHandle)
	{
		if ($this->username !== null && $this->password !== null)
		{
			curl_setopt($curlHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
			curl_setopt($curlHandle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
		}
	}

I mentioned earlier that we’re only going to cover HTTP Digest authentication in this class, but you could adapt this function to suit your needs however you wanted depending on what the API(s) you’re consuming require.

Making the REST Requests

We’re about ready to cover how to make the individual verb requests, but we need to do just a little bit more work filling in the common functionality. We’ll go ahead and create our doExecute and execute functions now:

	public function execute ()
	{
		$ch = curl_init();
		$this->setAuth($ch);

		try
		{
			switch (strtoupper($this->verb))
			{
				case 'GET':
					$this->executeGet($ch);
					break;
				case 'POST':
					$this->executePost($ch);
					break;
				case 'PUT':
					$this->executePut($ch);
					break;
				case 'DELETE':
					$this->executeDelete($ch);
					break;
				default:
					throw new InvalidArgumentException('Current verb (' . $this->verb . ') is an invalid REST verb.');
			}
		}
		catch (InvalidArgumentException $e)
		{
			curl_close($ch);
			throw $e;
		}
		catch (Exception $e)
		{
			curl_close($ch);
			throw $e;
		}

	}

	protected function doExecute (&$curlHandle)
	{
		$this->setCurlOpts($curlHandle);
		$this->responseBody = curl_exec($curlHandle);
		$this->responseInfo	= curl_getinfo($curlHandle);

		curl_close($curlHandle);
	}

Looks like a lot of code, but it’s actually pretty simple stuff.

First, looking at execute, you’ll see that we set up a curl handle variable ($ch), and then run the setAuth function. Since we pass $ch around by reference, the functions will directly manipulate the variable without any need to return it. Now, I could make the $ch a member variable, but for the sake of this example, I wanted to show how everything gets passed around a little more explicitly. Anyway, the next thing we do is normalize the verb, and run through our switch statement to determine what function will get executed. If we don’t find a matching case, we throw an exception. We also wrap the whole thing in a try / catch block so we can properly catch exceptions, close our curl handle, then throw the exception again (presumably for some other error handler or try / catch block elsewhere in the code).

Moving on to the doExecute function, you’ll see all we really do here is set all the common curl options with setCurlOpts, execute the request, and get the response body and info. We’ll take a look at those in a bit, but let’s get to the individual request functions.

GET Requests

These requests are about as easy as they get. Since your params will be a part of the URL, there isn’t much to do outside of actually making the request. So our code is nice and easy:

	protected function executeGet ($ch)
	{
		$this->doExecute($ch);
	}

‘Nuff said :)

POST Requests

These too are pretty easy to accomplish, and POSTing with Curl is well-documented. Nonetheless, here’s what our function looks like:

	protected function executePost ($ch)
	{
		if (!is_string($this->requestBody))
		{
			$this->buildPostBody();
		}

		curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody);
		curl_setopt($ch, CURLOPT_POST, 1);

		$this->doExecute($ch);
	}

All we really do here is make sure the request body is a string, and if it isn’t that means we need to prepare it… so we do. Then we tell Curl to make a POST request with the provided POST body. That’s all there is to it! Moving on…

PUT Requests in PHP

Ah, PUT requests… These are the big mystery. Admittedly, there are a few articles out there covering how to do them, but they’re not super-easy to come across. I did, however, manage to find a few and here’s what I came up with:

	protected function executePut ($ch)
	{
		if (!is_string($this->requestBody))
		{
			$this->buildPostBody();
		}

		$this->requestLength = strlen($this->requestBody);

		$fh = fopen('php://memory', 'rw');
		fwrite($fh, $this->requestBody);
		rewind($fh);

		curl_setopt($ch, CURLOPT_INFILE, $fh);
		curl_setopt($ch, CURLOPT_INFILESIZE, $this->requestLength);
		curl_setopt($ch, CURLOPT_PUT, true);

		$this->doExecute($ch);

		fclose($fh);
	}

A little funky, right? Well, this boils down to the way PUT requests are technically supposed to work. RESTful APIs don’t quite use a PUT the way they were originally intended, which is for file uploads. As such, we need to stream the data to the web server. So we open an internal php memory resource, write our request body to it, and then pass the handle for that memory resource to curl. We also calculate the size of it (in bytes), so that our full body will be received on the API side of things. I suppose that doesn’t necessarily need to make any sense to you, as long as you trust that it works ;)

DELETE Requests in PHP

Deletes, when done as they’re intended (no post body), are easy. Remember, you’re not really supposed to send any body for a delete request, as you’re merely sending a command to a resource (i.e. /api/user/1). Here’s the code:

	protected function executeDelete ($ch)
	{
		curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');

		$this->doExecute($ch);
	}

Now, if you need to send a body for some reason, you can do it by adding the same stuff you use for a PUT request, but swap out the CURLOPT_PUT line for the existing CURLOPT_CUSTOMREQUEST line that we’ve got in this function. I don’t advocate this, but it works like a champ if you need it.

The Response

Now that we’ve got a fully functioning REST requester, let’s take a look at a response. So, making a request against an imaginary API looks something like:

$request = new RestRequest('http://example.com/api/user/1', 'GET');
$request->execute();

echo '<pre>' . print_r($request, true) . '</pre>';

Which gives us output similar to:

RestRequest Object
(
    [url:protected] => http://example.com/api/user/1
    [verb:protected] => GET
    [requestBody:protected] =>
    [requestLength:protected] => 0
    [username:protected] =>
    [password:protected] =>
    [acceptType:protected] => application/json
    [responseBody:protected] => [RESPONSE BODY HERE]
    [responseInfo:protected] => Array
        (
            [url] => http://example.com/api/user/1
            [content_type] => application/json
            [http_code] => 200
            [header_size] => 232
            [request_size] => 192
            [filetime] => -1
            [ssl_verify_result] => 0
            [redirect_count] => 0
            [total_time] => 0.015693
            [namelookup_time] => 0.0004
            [connect_time] => 0.000571
            [pretransfer_time] => 0.000619
            [size_upload] => 0
            [size_download] => 4276
            [speed_download] => 272478
            [speed_upload] => 0
            [download_content_length] => 4276
            [upload_content_length] => 0
            [starttransfer_time] => 0.015655
            [redirect_time] => 0
        )
)

Take a look at the responseInfo… we’ve got our status code, and all sorts of other good stuff. And, believe it or not, we’re done!

Wrapping Up

Of course, as with all my samples, there’s a lot left for you to do. You’ll probably want to do some processing of the response body and info (such as extracting the status code), or make a few things more robust, but this is all you should need to get started. Hopefully, after this article and the previous one, you’ve got a good idea of how REST APIs work from the server-side, to the client-side, and you’re ready to jump on the REST bandwagon. If you’re not yet convinced… go play with some SOAP APIs, that ought to change your mind ;)

Finally, you can grab the final copy of the class, complete with getters / setters at the link below:
RestRequest.inc.php – Final Source Code

arrow43 Comments

  1. 70 mos, 3 wks ago

    Nice Article.
    Have you worked on CakePHP 1.2. Its Routing section does not work for me.
    Any help on that please?

  2. 70 mos, 3 wks ago

    Thanks!

    I haven’t had a chance to play with CakePHP, sorry

  3. 70 mos, 3 wks ago

    Hi i am using it like this.
    require (’RestRequest.inc.php’);
    $request = new RestRequest(’http://twitter.com/statuses/friends.xml’, ‘GET’);
    $request->setUsername (’muh_atta’);
    $request->setPassword (xxxxx’);
    $request->execute();

    but its says
    [http_code] => 401
    What i m doing wrong?

  4. 70 mos, 3 wks ago

    A little spelling mistake here CUROPT_USERPWD.
    curl_setopt($curlHandle, CURLOPT_USERPWD… In set setAuth.

  5. 70 mos, 3 wks ago

    When i comment out
    curl_setopt($curlHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
    It worked well with Twitter.

  6. 70 mos, 3 wks ago

    @Atta, Good catch, I fixed the typo.

    Also, RE: your issue with twitter… they probably don’t do digest auth, so by commenting that out you’re doing basic auth I think. At any rate, I’m glad you got it working!

  7. 70 mos, 3 wks ago

    Great post. For those of you using frameworks – You should also check out the Symfony sfWebBrowser plugin – this basically does all the code you provided, and is quite easy to use.

  8. 70 mos, 3 wks ago

    http://github.com/speedmax/activeresource-php/tree

    Implementation of ActiveResource php class to consume restful rails resource

  9. 70 mos, 3 wks ago

    Cool stuff, thanks for the link!

  10. 70 mos, 2 wks ago

    great 2 articles! thanks a lot. i was really looking forward to this one.

  11. arcade
    70 mos, 2 wks ago

    really hrelpfull stuff!! i love ya.

  12. Megha Hasan Shilpi
    70 mos, 1 wk ago

    thanks a lot for this helpful topics. But is there any way sent xml through array. I want to send xml.Will you please help me out please please please.

  13. 70 mos, 1 wk ago

    If you want to send plain ol’ content with a specific verb, you can merge the PUT and DELETE methods a bit. For example, let $method be one of the verbs:

    $fh = fopen(’php://memory’, ‘rw’);
    fwrite($fh, $this->requestBody);
    rewind($fh);

    curl_setopt($ch, CURLOPT_INFILE, $fh);
    curl_setopt($ch, CURLOPT_INFILESIZE, $this->requestLength);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);

    $this->doExecute($ch);

    fclose($fh);

    You’ll also need to make sure you calculate the request length properly (if you’re doing XML, I think strlen will work just fine). That ought to be enough to get you started :)

  14. 70 mos ago

    Just got this working. Thanks ever so much for taking the time to publish it.

  15. 68 mos, 3 wks ago

    Thanks a lot! Excellent tutorial :) A small request. For the sake of clarity, can you combine the files of the previous example (setting up the REST service) and this one (making REST requests) into a single archive?

  16. Anita
    68 mos, 2 wks ago

    excellent article… You have helped me a lot. Thanks a lot :)

  17. 67 mos, 3 wks ago

    The POST didn’t work until i urlencoded the json data.
    $action = array(’username’=>’hans’);
    $data = array(”data” => urlencode(json_encode($action)));

  18. 67 mos ago

    Luckily found it just hours before sitting down to write our REST API (for our booking software).

    Thank you!!

  19. epilif
    66 mos, 3 wks ago

    And class RestUtils? where is it?

  20. King Ahmad
    66 mos, 2 wks ago

    The best and the only practical one that can be found in Internet.
    I used it in our project and works as charm.
    Coffee on me !

  21. Andy
    66 mos, 1 wk ago

    I got this error:

    curl_setopt() [function.curl-setopt]: cannot represent a stream of type MEMORY as a STDIO FILE

    Do i have to enable something?

  22. blinky
    64 mos, 1 wk ago

    i’m new to the REST game, can you show an example of using the PUT & POST requests? not usre if i’m using them right.

  23. James
    64 mos, 1 wk ago

    Two things with the PUT example:
    1. use php://temp instead of php://memory as the latter is limited by memory, the former uses available memory, and then a temporary file if needed (therefore more reliable)
    2. the flag for the file handle should be “r+”, not “rw” (I think…)

  24. LILI
    62 mos, 2 wks ago

    Hi, very cool. Could you also post the result php file of the “Create a REST API with PHP” article like you did with this one? Thanks

  25. newbieman
    62 mos, 2 wks ago

    OK, a seriously newbie question. I’m just not getting it. If you are sending curl off on a mission to ‘http://somedomain.com/api/somecommand/1 ‘ what’s to stop the web server from issuing a 404-Page not found (as mine is doing)? I mean, there’s no document there. Where is the request being intercepted? I haven’t found any REST tutorials that spell out this basic issue.

    Question 2: Why are there two completely different RestRequest classes in Part 1 and Part 2 of this article?

    As you can see, I’m not getting the basic concept, but maybe asking the question will jog something loose cognitively.

  26. 62 mos, 2 wks ago

    Question 1: Check out Apache’s mod_rewrite… that’s the secret sauce (and why you don’t find it in any REST-related articles, it’s an Apache thing). There are lots of mod rewrite tutorials out there that can help get you up to speed. The other half of the equation here is that these URLs follow an MVC pattern of sorts (most modern frameworks use a combination of mod-rewrite and url routes. The best place to start for this is any of the major PHP frameworks or Rails… they all follow the same paradigm).

    Question 2: Mostly because while the articles cover similar topics, it’s not intended that the code within each would be used together. If you were to do that, you’d have to tweak some class names. Part 1 is about creating a RESTful API, and part 2 is about making RESTful requests.

    Hope that helps :)

  27. newbieman
    62 mos, 1 wk ago

    OH, I see. Thanks for the heads up. Can’t believe in the cookbook tutorials I saw that no one at least mentioned mod_rewrite. Probably they do somewhere. Does transferring state by means of URL mean that something like ‘http://www.mysite.com/api/controller?command=dosomething’ (or its equivalent as a POST, and assuming the adjustments you mention in the article re: PUT data) would also be RESTful and could be used in lieu of mod_rewrite?

  28. 62 mos, 1 wk ago

    I like your example. You wrote it very nicely and its easy to understand. Using framework is good to develop quickly but understanding real implementation is essential for knowledge.

  29. Elidjon
    60 mos, 3 wks ago

    Thank you for sharing with us this useful material but don’t you think using not CURL would have been better? If not, can you tell me why please? I’m confused weather to use CURL or not. Thank you again!

  30. Tom Van Schoor
    60 mos, 2 wks ago

    Very nice article and I agree that it is very easy. Nice work!

    I don’t agree on the SOAP part though :) I have used some very user friendly SOUP API. The Addemar API (mass email SAAS) for instance beats any RESTful API in simplicity and usability. With the new SoapClient class in PHP, connecting and using it is child’s play.

    What I do agree on is the fact that creating a REST API is much easier, but that has it’s reasons… You know what they say, with great power comes great responsibility… And that is why SOAP is more complicated, it has more power and allows to completely customize your API, but that power requires you to put time and research into the development and to provide good documentation for the clients as each SOAP API has it’s own custom functions.

    But to create fast, portable and generic API’s SOAP cant beat REST, that much I agree on :)

    Cheers for the article!

  31. 60 mos ago

    Dude, great articles, this one and the first one in the series of course! Just out of curiosity, where is your framework ? :)

  32. Windy!
    57 mos, 3 wks ago

    Can someone tell me how to run this code??

  33. test
    57 mos, 1 wk ago

    I once see this kind of REST SERVER example:
    The client should submit request to E.G. http://xxx.com/restserver.do, and it also use HTTP method to inform CURD type.
    Nevertheless, it should submit the method name in parameter to inform rest server what to do.
    It seems just link SOAP, do you think it is a real example of REST? Because, what you inform give me a impression that REST should with good manner of URI design. E.G.
    http://xxx.com/book/111.

    Could give a breif explaination about it? I am confused with when and why to use but not how to use.

  34. Hardik
    56 mos, 1 wk ago

    Thanks a lot for this article. Its really very informative documentation.

  35. 56 mos, 1 wk ago

    I’m currently working on making a Content Management System(CMS), and I very much appreciate this! :) The reason I ever even looked at an API, is because when I allow users of the CMS to check for updates – each CMS installed generates it’s own “API key” (not actually an API key, more or less used as an identification key) – and when they go to check for updates, it makes sure the key is valid, and if it’s not – I print it back to the user that their key is invalid. I do this as a way to stop people from abusing the CMS; it also limits them to 1-10 checks per day (depending on if the administrator is checking it, or a sub-administrator). Does this sound like a good practice, or no?

  36. DASER
    56 mos, 1 wk ago

    you have a wonderfull post here, it really helped me, thank you bro

  37. mike
    55 mos ago

    In a tutorial you need to explain everything first. What is curl? What is $data? It is not clear and much more detailed fleshing out should be added.

    Snippets are never good in a tutorial, b/c people need full working code to figure out what’s going on. People get hung up at different spots, so the only way to let people chew on stuff is to post a full sample.

  38. TY Ang
    54 mos, 2 wks ago

    Hi, Thanks for the great article. I’m new to this REST. Perhaps you may giving some example on request by PUT and POST. TQ very much for sharing.

  39. TY Ang
    54 mos, 2 wks ago

    Base on this example, if i would like to post a registration to REST API to create a new a/c, How do i send POST request? How do i set my form to $requestBody in this case?

  40. 52 mos ago

    Really helpful insights..

  41. 49 mos, 4 wks ago

    Thanks for sharing… :)
    very helpfull

  42. chazzz
    49 mos, 3 wks ago

    Hi

    thanks for this tutorial. I’m trying to get a partners’ XML feed with through REST.

    He just gave me the URL and a key number.
    He told me to put the key number straight in an HTTP header.

    I don’t understand what he means. Does anybody know where I shouldput this key number in the code given above ?

    Many thanks

  43. chazzz
    49 mos, 3 wks ago

    Ok I understood how to do this by including the key in the CURLOPT_HTTPHEADER array !

    But now… I’m getting content to appear but I don-t know how to parse it using Simple_xml ? Any idea

Leave A Comment