PHP + Instagram API で画像取得する

自分のサイトにInstagramの最新の画像を表示するために
phpでInstagram APIを叩いて画像URLを取得できるようにする。

公式のヘルプ
https://developers.facebook.com/docs/instagram

# 準備
1. Facebookアプリを作成
    1. https://developers.facebook.com > アプリを作成 > アプリタイプ: なし
    1. 表示名とメールアドレス設定 ![スクリーンショット 2021-11-06 21.25.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6568/c457d367-300f-a1b9-33b0-ddd83597a388.png)
    1. アプリダッシュボード画面が表示される
    1. 設定 > ベーシック > プラットフォームを追加 > `Website` を選択
    1. サイトURLを入力![スクリーンショット 2021-11-06 21.35.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6568/e519ed47-cde6-eb99-0838-9b6911b1cf47.png)
1. Instagram APIの構成
    1. アプリダッシュボード画面で `Instagram Basic Display` を設定
    1. Instagram Basic Display > Basic Display > Create New App > New Instagram App IDを入力（FacebookアプリIDと同じで良さそう） > アプリを作成
    1. InstagramアプリID と Instagram App Secret が設定される ![スクリーンショット 2021-11-06 21.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6568/dc39f3c9-51fd-f095-63c3-c7209571842b.png)
    1. `クライアントOAuth設定`, `承認を取り消す`, `データの削除リクエスト` に 受け取れるURLを設定する（今回はサイトURLに設定した値と同じ）
1. Instagramテストユーザーを追加
    1. 役割 > 役割 > Instagramテスター欄のInstagramテスターを追加
    1. 写真を取得するInstagramのユーザー名を入力
    1. Instagramにログインし 設定 > アプリとウェブサイト > テスターへのご招待 に来ているアプリからの招待を承認する ![スクリーンショット 2021-11-06 21.58.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6568/4ea50206-0160-e090-3089-edd51654402f.png)
1. Instagramユーザーで認証
    1. 以下のURLにアクセス

        ```
        https://api.instagram.com/oauth/authorize
          ?client_id=(1.3 で取得したInstagramアプリID)
          &redirect_uri=(2.4 で設定したクライアントOAuth設定のパス)
          &scope=user_profile,user_media
          &response_type=code
    ```

    1. Instagramのユーザ認証画面が開くので許可する <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6568/a608b8a1-f174-3158-2770-6b99432d366e.png" width=300>
    1. 以下のURLにリダイレクトされ認証コードが返される

        ```
        (2.4 で設定したクライアントOAuth設定のパス)?code=(認証コード)
    ```

1. トークンを取得
    1. ターミナルで以下を叩く

        ```bash
curl -X POST \
  https://api.instagram.com/oauth/access_token \
  -F client_id=(1.3 で取得したInstagramアプリID) \
  -F client_secret=(1.3 で取得したInstagram App Secret） \
  -F grant_type=authorization_code \
  -F redirect_uri=(2.4 で設定したクライアントOAuth設定のパス) \
  -F code=(4.3 で取得した認証コード)
    ```

    1. 以下のjsonが返り、アクセストークンが取得できる

        ```json
{"access_token": "(access_token)", "user_id": (user_id)}
    ```

1. 今回は、短期 → 長期トークンに切り替える
    1. 参考 https://developers.facebook.com/docs/instagram-basic-display-api/guides/long-lived-access-tokens?locale=ja_JP
    1. ターミナルで以下を叩く

        ```bash
curl -i -X GET "https://graph.instagram.com/access_token
?grant_type=ig_exchange_token
&client_secret=(1.3 で取得したInstagram App Secret）
&access_token=(5.2 で取得したアクセストークン)"
    ```

    1. 以下のjsonが返り、長期アクセストークンが取得できる（60日間有効）

        ```json
{"access_token": "(access_token)","token_type":"bearer","expires_in":XXXXXX}
    ```

# PHP + Instagram API で画像取得
参考: https://developers.facebook.com/docs/instagram-basic-display-api/reference/user/media

1. 以下のURLを叩く

    ```bash
curl -i -X GET "https://graph.instagram.com/me/media?fields=id,media_type,media_url&access_token=(6.3 で取得したアクセストークン)"
```

1. 返ったjsonから画像URLが取得できることを確認

    ```json
    {
        "data":[
          {"id":"XXXXX","media_type":"IMAGE","media_url":"(画像URL)"},
          {"id":"XXXXX","media_type":"IMAGE","media_url":"(画像URL)"},
          ...
        ],
        "paging":{
            "cursors":{"before":"XXXXX","after":"XXXXX"}
        }
    }
```

2. phpで取得できるようにする

    ```php
    //アクセストークン
    $token = "(6.3 で取得したアクセストークン)";
    
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL,    'https://graph.instagram.com/me/media?fields=id,media_type,media_url,thumbnail_url&access_token=' . $token);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
    $response = curl_exec($curl);
    curl_close($curl);
    
    $result = json_decode($response, TRUE);
    print $result['data'][0]['media_url'];
```

1. アクセストークンの更新と、アクセストークンをDBに保持する処理を追加してクラス化

    ```php:class_insta.php
    class InstagramMedia {
      private $token;
      private $db;
    
      public function __construct(){
        $this->db = new DataBaseModel;
      }
    
      public function get_media_url() {
        $this->refresh_token();
        return $this->get_instagram_media_url();
      }
    
      private function get_instagram_media_url() {
        if (!$this->token) {
           return false;
        }
    
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, 'https://graph.instagram.com/me/media?fields=id,media_type,media_url,thumbnail_url&access_token=' . $this->token);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
        $response = curl_exec($curl);
        $errno = curl_errno($curl);
        curl_close($curl);
    
        if ($errno !== CURLE_OK) {
           return false;
        }
    
        $result = json_decode($response, TRUE);
        return $result['data'][0]['media_url'];
      }
    
      private function refresh_token() {
        $token = $this->get_token();
    
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, 'https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=' . $token);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
        $response = curl_exec($curl);
        $errno = curl_errno($curl);
        curl_close($curl);
    
        if ($errno !== CURLE_OK) {
           return false;
        }
    
        $result = json_decode($response, true);
        print_r($result);
        $token = $result['access_token'];
    
        $this->token = $token;
        $this->update_token($token);
      }
    
      private function get_token() {
        $result = $this->db->find('access_token','token', "app = 'instagram'");
        return $result['token'];
      }
    
      private function update_token($token) {
        $this->db->upd('access_token', array('token'=>$token), "app = 'instagram'");
      }
    }
```

1. 呼び出して使えるようにする

    ```php:get_media_url.php
    require_once dirname(__FILE__) . '/config.php';
    require_once dirname(__FILE__) . '/class_db.php';
    require_once dirname(__FILE__) . '/class_insta.php';
    
    $insta = new InstagramMedia;
    print $insta->get_media_url();
```

完了。
最終的なソースコードはこちら
https://github.com/background-color/php_instagram_media_sandbox