How do I make a simple PHP API handler for Digital Ocean?

May 10, 2017 1.7k views
API PHP Ubuntu
hozza
By:
hozza

Hi all,

I've having some trouble understanding the API, in the past with other API's I've just used a simple PHP with curl script to action a specific task on a service. I don't need a full library and the Digital Ocean has more dependencies than I care to count...

For some reason the DO API is not working with a simple script like this while others do, I must be missing something?

function basic_api_handle($key, $method, $URI, $data) {

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Authorization: Bearer '.$key,
        'Content-Type: application/json')
    );

    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
    curl_setopt($ch, CURLOPT_URL, $URI);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));

    $result = curl_exec($ch);
    if($result === false) error_log("API ERROR: Connection failure: $URI", 0);

    curl_close($ch);

    return json_decode($result, true);
}

var_dump(basic_api_handle($api_key, 'POST', 'https://api.digitalocean.com/v2/domains', array('name' => 'my-domain.tld', 'ip_address' => '1.2.3.4')));

This results in** "unprocessable_entity" and **"Name can't be blank" - it suggests to me the data is not being sent correctly? although, as far as I can tell, it is...

I've also posted this on Stack Overflow but it may be to specific (Digital Ocean) to get any decent answers. I have setup a bounty for a solution too :) http://stackoverflow.com/questions/43619504/how-do-i-make-a-simple-php-api-handler/43854037

2 Answers
jtittle1 May 10, 2017
Accepted Answer

@hozza

A very simple way to do this, without curl would be using file_get_contents and streams.

<?php
function doapi( $key, $method, $uri, array $data = [] )
{
    /**
     * DigitalOcean API URI
     */
    $api = 'https://api.digitalocean.com/v2';

    /**
     * Merge DigitalOcean API URI and Endpoint URI
     *
     * i.e if $uri is set to 'domains', then $api ends up as
     *     $api = 'https://api.digitalocean.com/v2/domains'
     */
    $uri = $api . DIRECTORY_SEPARATOR . $uri;

    /**
     * Define Authorization and Content-Type Header.
     */
    $headers = "Authorization: Bearer $key \r\n" .
               "Content-Type: application/json";

    /**
     * If $data array is not empty, assume we're passing data, so we'll encode
     * it and pass it to 'content'. If $data is empty, assume we're not passing
     * data, so we won't sent 'content'.
     */
    if ( ! empty( $data ) )
    {
        $data = [
                    'http'          => [
                        'method'    =>  strtoupper( $method ),
                        'header'    =>  $headers,
                        'content'   =>  json_encode( $data )
                    ]
                ];
    }
    else
    {
        $data = [
                    'http'          => [
                        'method'    =>  strtoupper( $method ),
                        'header'    =>  $headers
                    ]
                ];
    }

    /**
     * Create Stream Context
     * http://php.net/manual/en/function.stream-context-create.php
     */
    $context = stream_context_create( $data );

    /**
     * Send Request and Store to $response.
     */
    $response = file_get_contents( $uri, false, $context );

    /**
     * Return as decoded JSON (i.e. an array)
     */
    return json_decode( $response, true );
}

/**
 * Example Usage
 */

var_dump(doapi(
    'do-api-key',
    'get',
    'domains'
));

An example is at the bottom of the file.

var_dump(doapi(
    'do-api-key',
    'get',
    'domains'
));

Which just dumps the output of the response. You could assign it to a variable, like so:

$response = doapi( 'do-api-key', 'get', 'domains' );

You could then filter through the response as needed. The above is very basic and doesn't have any sort of error handling built-in, so you'd need to make sure you're passing exactly what is required.

Looking at the initial function definition:

function doapi( $key, $method, $uri, array $data = [] )

You'll pass:

$key = DigitalOcean API Key

$method = Upper or Lowercase Method (get, post, delete, etc). It'll be converted to uppercase.

$uri = The endpoint your calling, without a / (i.e. domains, droplets, etc).

$data = An array of data that isn't JSON encoded (the function will encode it for you).

...

For $uri, you could pass endpoints with a slash between them, i.e. domains/action (where action is an endpoint off domains`), though no front or trailing slash should be added.

...

I just tested this locally using one of my API keys and it works. Hopefully that'll help :-).

@hozza

No problem at all, happy to help :-). I've not been on SO in a while -- I probably need to get back on there at some point.

As a side note, if you want to tinker with cURL too, here's a modified version of the original function from my first post. It only handles GET and POST requests as-is, but you can extend the function to handle PUT, DELETE, etc as needed.

<?php
function doapi( $key, $method, $uri, array $data = [] )
{
    $api        =   'https://api.digitalocean.com/v2';
    $uri        =   $api . DIRECTORY_SEPARATOR . $uri;
    $method     =   strtoupper( $method );

    $headers    =   [
                        "Authorization: Bearer $key",
                        "Content-Type: application/json"
                    ];

    $ch         =   curl_init();

    curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
    curl_setopt( $ch, CURLOPT_URL, $uri );

    if ( $method === 'POST' && ! empty( $data ) )
    {
        curl_setopt( $ch, CURLOPT_POST, 1 );
        curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $data ) );
    }

    $result     =   curl_exec( $ch );
    $result     =   json_decode( $result, true );

    curl_close( $ch );

    return $result;
}

$result = doapi(
    'do-api-key',
    'post',
    'droplets',
    [
        'name' => 'example.com',
        'region' => 'nyc3',
        'size' => '512mb',
        'image' => 'ubuntu-16-04-x64'
    ]
);

var_dump( $result );

The only difference is the code. You'd use it the same as you do the other function. Hopefully that'll help you either way -- using whichever method you prefer to use :-).

Have another answer? Share your knowledge.