Lately I've discovered RESTful-APIs for myself. I now tend to integrate them in most of my applications just in case some other service want to interact with it. The majority of my applications are based on Zend Framework 2 or 3, so it's really easy to provide a REST-API by using \Zend\Mvc\Controller\AbstractRestfulController.

Normally I write a simple php script (which will be delivered with the application) to 1. test if the REST-API works and 2. to have some demo code for developers, who want to talk to the API.

In the past I used \Zend\Http\Client to make the calls in my demo script. In my latest project however, I wanted to reduce dependencies. Having to install \Zend\Http\Client just to run my demo script was a bit of overkill. Also you don't know if the developer, who wants to use the API runs a Zend Framework based application and also I don't want to force him to use \Zend\Http\Client. Therefore I decided to write that demo script in "native" PHP and with "native" PHP I mean cURL.

Just to remind you of the basics: make a POST request to create a new resource, a DELETE request to delete a resource, a GET request to read one or more resources and a PUT request to update an existing resource.

Even though I worked a hundred times with cURL I had some trouble in making a PUT request. It turned out I didn't read the docs correctly (or better said: not at all).

Similar to the following code snippet I successfully sent a POST request (no magic at all, right):

<?php
$url = 'http://somehost/api/example-resource/';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'key_1' => 'value_1',
    'key_2' => 'value_2'
]);
$response = curl_exec($ch);
echo 'Status-Code: ' . curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo '<pre>';print_r(json_decode($response, true));echo'</pre>';
curl_close($ch);

This was the code to create a resource. The response was the created resource with all its keys and values as JSON. Now I thought: "okay let's just switch the POST request to a PUT request and everything else should be the same.". Great: my IDE was suggesting a constant CURLOPT_PUT and I was assuming it would work the same way as CURLOPT_POST works: set it to true and it will be a PUT request:

<?php
$url = 'http://somehost/api/example-resource/15/';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'key_2' => 'value_2'
]);
$response = curl_exec($ch);
echo 'Status-Code: ' . curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo '<pre>';print_r(json_decode($response, true));echo'</pre>';
curl_close($ch);

That resulted in a PUT request, but the data (provided via CURLOPT_POSTFIELDS) were never received by the server. I googled for like an hour and then headed back to the docs of curl_setopt(). It turned out that CURLOPT_PUT works quite different and is real nasty to use, as you have to write the data you want to sent to a file, and then use CURLOPT_INFILE to point cURL to the file. Sure you could solve this with a tempfile, somehow like this:

<?php
$url = 'http://somehost/api/example-resource/15/';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PUT, true);

$data = http_build_query([
    'key_2' => 'value_2'
]);
$tmpfile = tmpfile();
file_put_contents($tmpfile, $data);

curl_setopt($ch, CURLOPT_INFILE, $tmpfile);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($data));

$response = curl_exec($ch);
echo 'Status-Code: ' . curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo '<pre>';print_r(json_decode($response, true));echo'</pre>';
curl_close($ch);

However even that seemed a bit overkill for me. So I found another solution, which is as simple, as the POST request. Instead of using CURLOPT_PUT you would use CURLOPT_CUSTOMREQUEST and set it to PUT:

<?php
$url = 'http://somehost/api/example-resource/15/';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'key_2' => 'value_2'
]);
$response = curl_exec($ch);
echo 'Status-Code: ' . curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo '<pre>';print_r(json_decode($response, true));echo'</pre>';
curl_close($ch);

This is much simpler, does the same job and won't need creating a file or tempfile in between (which could lead to access-right issues on the file system, if not configured properly).

Maybe most of you guys already knew this. However I am keeping this snippet here for myself and everybody, that tends to forget :)

Cheers, Alex