1
\$\begingroup\$

Here's an example script I wrote for Instagram (Facebook)'s Basic Display API. I did this for myself because I haven't used Instagram's API since their Legacy API was disabled, and I needed to familiarize myself for upcoming projects. It's super easy to understand and is meant for learning purposes. It covers every function (I think). I would love a critique.

define('client_id', 'your client id here');
define('client_secret', 'your client secret here');
define('redirect_uri', 'your Redirect URI here');

if (isset($_GET['code'])) {
    try{
        #gets code
        $code = $_GET['code'];
        echo '<pre>'.$code.'</pre>';

        #gets short lived access token
        $authorize = get_short_lived_access_token($code);
        echo '<pre>'.$authorize.'</pre>';
        $result = json_decode($authorize);
        $short_lived_access_token = $result->access_token;
        $user_id = $result->user_id;

        #exchanges short lived access token for long lived access token
        $access_token = get_long_lived_access_token($short_lived_access_token, $user_id);
        $result = json_decode($access_token);
        echo '<pre>'.$access_token.'</pre>';
        $long_lived_access_token = $result->access_token;

        #gets user data
        $user = get_user_data($long_lived_access_token, $user_id);
        echo '<pre>'.$user.'</pre>';

        #gets a list of all media
        $media = get_user_media_id($long_lived_access_token, $user_id);
        echo '<pre>'.$media.'</pre>';

        #gets each media entry
        $media = json_decode($media);
        $i = 0;
        foreach($media->data as $media_data){
            $media_id = $media_data->id;
            $media_child = get_user_media_data($long_lived_access_token, $user_id, $media_id);
            echo '<pre>'.$media_child.'</pre>';
            $media_child = json_decode($media_child);
            echo '<img src="'.$media_child->media_url.'"><br><br>';
            if (++$i == 5) break;
        }

        #refreshes access token
        $refresh = refresh_access_token($long_lived_access_token);
        echo '<pre>'.$refresh.'</pre>';

    }catch (Exception $e){
    echo json_encode(array('response'=>'error','message'=>$e->getMessage()));
    }
}else{
    echo 'instagram not connected<br>';
}

echo '<a href="https://api.instagram.com/oauth/authorize?client_id='.client_id.'&redirect_uri='.redirect_uri.'&scope=user_profile,user_media&response_type=code" target="_blank">connect your instagram</a>';

function get_short_lived_access_token($code){
    $url = 'https://api.instagram.com/oauth/access_token';
    $data = array(
                'client_id'     => client_id,
                'client_secret' => client_secret,
                'grant_type'    => 'authorization_code',
                'redirect_uri'  => redirect_uri,
                'code'          => $code
            );
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POST, true);    
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

function get_long_lived_access_token($access_token, $user_id){
    $url = 'https://graph.instagram.com/access_token/?';
    $data = array(
                'client_secret' => client_secret,
                'access_token'  => $access_token,
                'grant_type'    => 'ig_exchange_token'
            );
    $string = http_build_query($data);
    $ch = curl_init($url.$string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

function get_user_data($access_token, $user_id){
    $url = 'https://graph.instagram.com/'.$user_id.'/?';
    $data = array(
                'access_token'  => $access_token,
                'fields'        => 'username,account_type,media_count'
            );
    $string = http_build_query($data);
    $ch = curl_init($url.$string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $result = curl_exec($ch);
    curl_close($ch);
    return ($result);
}

function get_user_media_id($access_token, $user_id){
    $url = 'https://graph.instagram.com/'.$user_id.'/media/?';
    $data = array(
                'access_token'  => $access_token,
                'fields'        => 'id,timestamp'
            );
    $string = http_build_query($data);
    $ch = curl_init($url.$string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $result = curl_exec($ch);
    curl_close($ch);
    return ($result);
}

function get_user_media_data($access_token, $user_id, $media_id){
    $url = 'https://graph.instagram.com/'.$media_id.'/?';
    $data = array(
                'access_token'  => $access_token,
                'fields'        => 'caption,id,media_type,media_url,permalink,thumbnail_url,timestamp'
            );
    $string = http_build_query($data);
    $ch = curl_init($url.$string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $result = curl_exec($ch);
    curl_close($ch);
    return ($result);
}

function refresh_access_token($access_token){
    $url = 'https://graph.instagram.com/refresh_access_token/?';
    $data = array(
                'access_token'  => $access_token,
                'grant_type'    => 'ig_refresh_token'
            );
    $string = http_build_query($data);
    $ch = curl_init($url.$string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $result = curl_exec($ch);
    curl_close($ch);
    return ($result);
}
\$\endgroup\$
1
\$\begingroup\$

Overall feedback

It might be wise to put all the functions and constants into a class for the sake of encapsulation. That way the constants can be namespaced. Perhaps it would be wise to have a constant for the base URLs - for example:

const API_URL = 'https://api.instagram.com';
const GRAPH_URL = 'https://graph.instagram.com';

There is a bit of redundancy between the functions. It might be wise to have a function that handles the cURL functions - perhaps accepting a URL or path that can be appended to a base URL (e.g. defined using const as https://graph.instagram.com/) if the URL does contain a TLD, a parameter for the data, and an optional parameter for method - which would handle CURLOPT_POST.

It is advisable to follow recommended standards - e.g. the PHP Standards Recommendations - especially PSR-1 and PSR-12.

The try/catch looks like something Your Common Sense critiqued in an earlier review as being "a cargo cult code that makes no sense". Do any of the functions called within the try block throw exceptions? I could understand it being used if the calls to json_decode() passed JSON_THROW_ON_ERROR as the $flags parameter...

Targeted feedback

Constant format

As is a common convention with many Object oriented languages, PSR-1 recommends constants be declared in all caps:

Class constants MUST be declared in all upper case with underscore separators. 1

Thus it would be simpler to spot CLIENT_ID as a constant compared to client_id, and similar for the other two constants.

Array format

There isn't anything wrong with using array() but as of the time of writing, PHP has Active support for versions 8.0 and 7.4, and since PHP 5.4 arrays can be declared with short array syntax (PHP 5.4) - i.e. [].

Return early

After the three define lines there is this code:

if (isset($_GET['code'])) {
    try{
        #gets code

and at the end of that block is this:

}else{
    echo 'instagram not connected<br>';
}

If the code was put into a function and the logic is flipped so that the scenario where the query string doesn't contain a code is handled first, the subsequent indentation levels can be decreased:

if (!isset($_GET['code'])) {
    return 'instagram not connected<br>';
}
try {
    #gets code

This can improve readability as anyone reading the code would have less of a need to scroll horizontally.

Iteration counter with foreach

Instead of this foreach that manually maintains $i:

   $i = 0;
   foreach($media->data as $media_data){

with the last step:

        if (++$i == 5) break;

Use this format:

foreach ($media->data as $i => $media_data) {

And the last step:

    if ($i === 5) break;

While it isn't wrong to update $i manually, it can be handled by the iteration.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.