hozza
By:
hozza

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

May 10, 2017 574 views
API PHP Ubuntu

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.