├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── composer.lock └── src ├── InstagramBasicDisplay.php └── InstagramBasicDisplayException.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | /vendor/ 3 | 4 | # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control 5 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 6 | # composer.lock 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 espresso.dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *As the Instagram Basic Display API will be deprecated on December 4, 2024 [https://developers.facebook.com/docs/instagram-basic-display-api/](https://developers.facebook.com/docs/instagram-basic-display-api/) this package is no longer maintained. Please use the [Instagram API PHP](https://github.com/espresso-dev/instagram-php) package instead.* 2 | 3 | # Instagram Basic Display PHP API 4 | 5 | A simple PHP wrapper for the Instagram Basic Display API. Based on the [Instagram-PHP-API](https://github.com/cosenary/Instagram-PHP-API) by [Christian Metz](http://metzweb.net) 6 | 7 | [](https://packagist.org/packages/espresso-dev/instagram-basic-display-php) 8 | [](https://packagist.org/packages/espresso-dev/instagram-basic-display-php) 9 | [](https://packagist.org/packages/espresso-dev/instagram-basic-display-php) 10 | 11 | > [Composer](#installation) package available. 12 | 13 | ## Requirements 14 | 15 | - PHP 5.6 or higher 16 | - cURL 17 | - Facebook Developer Account 18 | - Facebook App 19 | 20 | ## Get started 21 | 22 | To use the [Instagram Basic Display API](https://developers.facebook.com/docs/instagram-basic-display-api), you will need to register a Facebook app and configure Instagram Basic Display. Follow the [getting started guide](https://developers.facebook.com/docs/instagram-basic-display-api/getting-started). 23 | 24 | ### Installation 25 | 26 | I strongly advice using [Composer](https://getcomposer.org) to keep updates as smooth as possible. 27 | 28 | ``` 29 | $ composer require espresso-dev/instagram-basic-display-php 30 | ``` 31 | 32 | ### Initialize the class 33 | 34 | ```php 35 | use EspressoDev\InstagramBasicDisplay\InstagramBasicDisplay; 36 | 37 | $instagram = new InstagramBasicDisplay([ 38 | 'appId' => 'YOUR_APP_ID', 39 | 'appSecret' => 'YOUR_APP_SECRET', 40 | 'redirectUri' => 'YOUR_APP_REDIRECT_URI' 41 | ]); 42 | 43 | echo "Login with Instagram"; 44 | ``` 45 | 46 | ### Authenticate user (OAuth2) 47 | 48 | ```php 49 | // Get the OAuth callback code 50 | $code = $_GET['code']; 51 | 52 | // Get the short lived access token (valid for 1 hour) 53 | $token = $instagram->getOAuthToken($code, true); 54 | 55 | // Exchange this token for a long lived token (valid for 60 days) 56 | $token = $instagram->getLongLivedToken($token, true); 57 | 58 | echo 'Your token is: ' . $token; 59 | ``` 60 | 61 | ### Get user profile 62 | 63 | ```php 64 | // Set user access token 65 | $instagram->setAccessToken($token); 66 | 67 | // Get the users profile 68 | $profile = $instagram->getUserProfile(); 69 | 70 | echo '
'; 71 | print_r($profile); 72 | echo ''; 73 | ``` 74 | 75 | **All methods return the API data as `json_decode()` - so you can directly access the data.** 76 | 77 | ## Available methods 78 | 79 | ### Setup Instagram 80 | 81 | `new Instagram(/ );` 82 | 83 | `array` if you want to perform oAuth: 84 | 85 | ```php 86 | new InstagramBasicDisplay([ 87 | 'appId' => 'YOUR_APP_ID', 88 | 'appSecret' => 'YOUR_APP_SECRET', 89 | 'redirectUri' => 'YOUR_APP_REDIRECT_URI' 90 | ]); 91 | ``` 92 | 93 | `string` once you have a token and just want to return *read-only* data: 94 | 95 | ```php 96 | new InstagramBasicDisplay('ACCESS_TOKEN'); 97 | ``` 98 | 99 | ### Get login URL 100 | 101 | `getLoginUrl( , )` 102 | 103 | ```php 104 | getLoginUrl( 105 | array( 106 | 'user_profile', 107 | 'user_media' 108 | ), 109 | 'state' 110 | ); 111 | ``` 112 | 113 | ### Get OAuth token (Short lived valid for 1 hour) 114 | 115 | `getOAuthToken($code, / )` 116 | 117 | `true` : Returns only the OAuth token 118 | `false` *[default]* : Returns OAuth token and profile data of the authenticated user 119 | 120 | ### Exchange the OAuth token for a Long lived token (valid for 60 days) 121 | 122 | `getLongLivedToken($token, / )` 123 | 124 | `true` : Returns only the OAuth token 125 | `false` *[default]* : Returns OAuth token and profile data of the authenticated user 126 | 127 | ### Refresh access token for another 60 days before it expires 128 | 129 | `refreshToken($token, / )` 130 | 131 | `true` : Returns only the OAuth token 132 | `false` *[default]* : Returns OAuth token and expiry data of the token 133 | 134 | ### Set / Get access token 135 | 136 | - Set the access token, for further method calls: `setAccessToken($token)` 137 | - Get the access token, if you want to store it for later usage: `getAccessToken()` 138 | 139 | ### User methods 140 | 141 | **Authenticated methods** 142 | 143 | - `getUserProfile()` 144 | - `getUserMedia(<$id>, <$limit>)` 145 | - if an `$id` isn't defined or equals `'me'`, it returns the media of the logged in user 146 | 147 | ### Media methods 148 | 149 | **Authenticated methods** 150 | 151 | - `getMedia($id)` 152 | - `getMediaChildren()` 153 | 154 | 155 | ## Pagination 156 | 157 | The `getUserMedia` endpoint has a maximum range of results, so increasing the `limit` parameter above the limit of 99 won't help.You can use pagination to return more results for this endpoint. 158 | 159 | Pass an object into the `pagination()` method and receive your next dataset: 160 | 161 | ```php 162 | $media = $instagram->getUserMedia(); 163 | 164 | $moreMedia = $instagram->pagination($media); 165 | ``` 166 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "espresso-dev/instagram-basic-display-php", 3 | "description": "A simple PHP class for accessing the Instagram Basic Display API", 4 | "keywords": ["instagram", "api"], 5 | "homepage": "https://github.com/espresso-dev/instagram-basic-display-php", 6 | "type": "library", 7 | "require": { 8 | "php": ">=5.6", 9 | "ext-curl": "*", 10 | "ext-json": "*" 11 | }, 12 | "autoload": { 13 | "psr-4": { 14 | "EspressoDev\\InstagramBasicDisplay\\": "src/" 15 | } 16 | }, 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "Herman Schutte", 21 | "email": "herman@espresso.dev" 22 | } 23 | ], 24 | "minimum-stability": "dev" 25 | } 26 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "ca16009b9b2cfa015991077776e95f47", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "dev", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=5.6", 17 | "ext-curl": "*" 18 | }, 19 | "platform-dev": [], 20 | "plugin-api-version": "1.1.0" 21 | } 22 | -------------------------------------------------------------------------------- /src/InstagramBasicDisplay.php: -------------------------------------------------------------------------------- 1 | setAppId($config['appId']); 80 | $this->setAppSecret($config['appSecret']); 81 | $this->setRedirectUri($config['redirectUri']); 82 | 83 | if (isset($config['timeout'])) { 84 | $this->setTimeout($config['timeout']); 85 | } 86 | 87 | if (isset($config['connectTimeout'])) { 88 | $this->setConnectTimeout($config['connectTimeout']); 89 | } 90 | } elseif (is_string($config)) { 91 | // For read-only 92 | $this->setAccessToken($config); 93 | } else { 94 | throw new InstagramBasicDisplayException('Error: __construct() - Configuration data is missing.'); 95 | } 96 | } 97 | 98 | /** 99 | * @param string[] $scopes 100 | * @param string $state 101 | * @return string 102 | * @throws InstagramBasicDisplayException 103 | */ 104 | public function getLoginUrl($scopes = ['user_profile', 'user_media'], $state = '') 105 | { 106 | if (is_array($scopes) && count(array_intersect($scopes, $this->_scopes)) === count($scopes)) { 107 | return self::API_OAUTH_URL . '?client_id=' . $this->getAppId() . '&redirect_uri=' . urlencode($this->getRedirectUri()) . '&scope=' . implode(',', 108 | $scopes) . '&response_type=code' . ($state != '' ? '&state=' . $state : ''); 109 | } 110 | 111 | throw new InstagramBasicDisplayException("Error: getLoginUrl() - The parameter isn't an array or invalid scope permissions used."); 112 | } 113 | 114 | /** 115 | * @param int $id 116 | * @return object 117 | * @throws InstagramBasicDisplayException 118 | */ 119 | public function getUserProfile($id = 0) 120 | { 121 | if ($id === 0) { 122 | $id = 'me'; 123 | } 124 | 125 | return $this->_makeCall($id, ['fields' => $this->_userFields]); 126 | } 127 | 128 | /** 129 | * @param string $id 130 | * @param int $limit 131 | * @param string|null $before 132 | * @param string|null $after 133 | * @return object 134 | * @throws InstagramBasicDisplayException 135 | */ 136 | public function getUserMedia($id = 'me', $limit = 0, $before = null, $after = null) 137 | { 138 | $params = [ 139 | 'fields' => $this->_mediaFields 140 | ]; 141 | 142 | if ($limit > 0) { 143 | $params['limit'] = $limit; 144 | } 145 | if (isset($before)) { 146 | $params['before'] = $before; 147 | } 148 | if (isset($after)) { 149 | $params['after'] = $after; 150 | } 151 | 152 | return $this->_makeCall($id . '/media', $params); 153 | } 154 | 155 | /** 156 | * @param string $id 157 | * @return object 158 | * @throws InstagramBasicDisplayException 159 | */ 160 | public function getMedia($id) 161 | { 162 | return $this->_makeCall($id, ['fields' => $this->_mediaFields]); 163 | } 164 | 165 | /** 166 | * @param string $id 167 | * @return object 168 | * @throws InstagramBasicDisplayException 169 | */ 170 | public function getMediaChildren($id) 171 | { 172 | return $this->_makeCall($id . '/children', ['fields' => $this->_mediaChildrenFields]); 173 | } 174 | 175 | /** 176 | * @param object $obj 177 | * @return object|null 178 | * @throws InstagramBasicDisplayException 179 | */ 180 | public function pagination($obj) 181 | { 182 | if (is_object($obj) && !is_null($obj->paging)) { 183 | if (!isset($obj->paging->next)) { 184 | return null; 185 | } 186 | 187 | $apiCall = explode('?', $obj->paging->next); 188 | 189 | if (count($apiCall) < 2) { 190 | return null; 191 | } 192 | 193 | $function = str_replace(self::API_URL, '', $apiCall[0]); 194 | parse_str($apiCall[1], $params); 195 | 196 | // No need to include access token as this will be handled by _makeCall 197 | unset($params['access_token']); 198 | 199 | return $this->_makeCall($function, $params); 200 | } 201 | 202 | throw new InstagramBasicDisplayException("Error: pagination() | This method doesn't support pagination."); 203 | } 204 | 205 | /** 206 | * @param string $code 207 | * @param bool $tokenOnly 208 | * @return object|string 209 | * @throws InstagramBasicDisplayException 210 | */ 211 | public function getOAuthToken($code, $tokenOnly = false) 212 | { 213 | $apiData = array( 214 | 'client_id' => $this->getAppId(), 215 | 'client_secret' => $this->getAppSecret(), 216 | 'grant_type' => 'authorization_code', 217 | 'redirect_uri' => $this->getRedirectUri(), 218 | 'code' => $code 219 | ); 220 | 221 | $result = $this->_makeOAuthCall(self::API_OAUTH_TOKEN_URL, $apiData); 222 | 223 | return !$tokenOnly ? $result : $result->access_token; 224 | } 225 | 226 | /** 227 | * @param string $token 228 | * @param bool $tokenOnly 229 | * @return object|string 230 | * @throws InstagramBasicDisplayException 231 | */ 232 | public function getLongLivedToken($token, $tokenOnly = false) 233 | { 234 | $apiData = array( 235 | 'client_secret' => $this->getAppSecret(), 236 | 'grant_type' => 'ig_exchange_token', 237 | 'access_token' => $token 238 | ); 239 | 240 | $result = $this->_makeOAuthCall(self::API_TOKEN_EXCHANGE_URL, $apiData, 'GET'); 241 | 242 | return !$tokenOnly ? $result : $result->access_token; 243 | } 244 | 245 | /** 246 | * @param string $token 247 | * @param bool $tokenOnly 248 | * @return object|string 249 | * @throws InstagramBasicDisplayException 250 | */ 251 | public function refreshToken($token, $tokenOnly = false) 252 | { 253 | $apiData = array( 254 | 'grant_type' => 'ig_refresh_token', 255 | 'access_token' => $token 256 | ); 257 | 258 | $result = $this->_makeOAuthCall(self::API_TOKEN_REFRESH_URL, $apiData, 'GET'); 259 | 260 | return !$tokenOnly ? $result : $result->access_token; 261 | } 262 | 263 | /** 264 | * @param string $function 265 | * @param string[]|null $params 266 | * @param string $method 267 | * @return object 268 | * @throws InstagramBasicDisplayException 269 | */ 270 | protected function _makeCall($function, $params = null, $method = 'GET') 271 | { 272 | if (!isset($this->_accesstoken)) { 273 | throw new InstagramBasicDisplayException("Error: _makeCall() | $function - This method requires an authenticated users access token."); 274 | } 275 | 276 | $authMethod = '?access_token=' . $this->getAccessToken(); 277 | 278 | $paramString = null; 279 | 280 | if (isset($params) && is_array($params)) { 281 | $paramString = '&' . http_build_query($params); 282 | } 283 | 284 | $apiCall = self::API_URL . $function . $authMethod . (('GET' === $method) ? $paramString : null); 285 | 286 | $headerData = array('Accept: application/json'); 287 | 288 | $ch = curl_init(); 289 | curl_setopt($ch, CURLOPT_URL, $apiCall); 290 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData); 291 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->_connectTimeout); 292 | curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->_timeout); 293 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 294 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 295 | curl_setopt($ch, CURLOPT_HEADER, true); 296 | 297 | $jsonData = curl_exec($ch); 298 | 299 | if (!$jsonData) { 300 | throw new InstagramBasicDisplayException('Error: _makeCall() - cURL error: ' . curl_error($ch), curl_errno($ch)); 301 | } 302 | 303 | list($headerContent, $jsonData) = explode("\r\n\r\n", $jsonData, 2); 304 | 305 | curl_close($ch); 306 | 307 | return json_decode($jsonData); 308 | } 309 | 310 | /** 311 | * @param string $apiHost 312 | * @param string[] $params 313 | * @param string $method 314 | * @return object 315 | * @throws InstagramBasicDisplayException 316 | */ 317 | private function _makeOAuthCall($apiHost, $params, $method = 'POST') 318 | { 319 | $paramString = null; 320 | 321 | if (isset($params) && is_array($params)) { 322 | $paramString = '?' . http_build_query($params); 323 | } 324 | 325 | $apiCall = $apiHost . (('GET' === $method) ? $paramString : null); 326 | 327 | $ch = curl_init(); 328 | curl_setopt($ch, CURLOPT_URL, $apiCall); 329 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json')); 330 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 331 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 332 | curl_setopt($ch, CURLOPT_TIMEOUT_MS, $this->_timeout); 333 | 334 | if ($method === 'POST') { 335 | curl_setopt($ch, CURLOPT_POST, count($params)); 336 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); 337 | } 338 | 339 | $jsonData = curl_exec($ch); 340 | 341 | if (!$jsonData) { 342 | throw new InstagramBasicDisplayException('Error: _makeOAuthCall() - cURL error: ' . curl_error($ch)); 343 | } 344 | 345 | curl_close($ch); 346 | 347 | return json_decode($jsonData); 348 | } 349 | 350 | /** 351 | * @param string $token 352 | */ 353 | public function setAccessToken($token) 354 | { 355 | $this->_accesstoken = $token; 356 | } 357 | 358 | /** 359 | * @return string 360 | */ 361 | public function getAccessToken() 362 | { 363 | return $this->_accesstoken; 364 | } 365 | 366 | /** 367 | * @param string $appId 368 | */ 369 | public function setAppId($appId) 370 | { 371 | $this->_appId = $appId; 372 | } 373 | 374 | /** 375 | * @return string 376 | */ 377 | public function getAppId() 378 | { 379 | return $this->_appId; 380 | } 381 | 382 | /** 383 | * @param string $appSecret 384 | */ 385 | public function setAppSecret($appSecret) 386 | { 387 | $this->_appSecret = $appSecret; 388 | } 389 | 390 | /** 391 | * @return string 392 | */ 393 | public function getAppSecret() 394 | { 395 | return $this->_appSecret; 396 | } 397 | 398 | /** 399 | * @param string $redirectUri 400 | */ 401 | public function setRedirectUri($redirectUri) 402 | { 403 | $this->_redirectUri = $redirectUri; 404 | } 405 | 406 | /** 407 | * @return string 408 | */ 409 | public function getRedirectUri() 410 | { 411 | return $this->_redirectUri; 412 | } 413 | 414 | /** 415 | * @param string $fields 416 | */ 417 | public function setUserFields($fields) 418 | { 419 | $this->_userFields = $fields; 420 | } 421 | 422 | /** 423 | * @param string $fields 424 | */ 425 | public function setMediaFields($fields) 426 | { 427 | $this->_mediaFields = $fields; 428 | } 429 | 430 | /** 431 | * @param string $fields 432 | */ 433 | public function setMediaChildrenFields($fields) 434 | { 435 | $this->_mediaChildrenFields = $fields; 436 | } 437 | 438 | /** 439 | * @param int $timeout 440 | */ 441 | public function setTimeout($timeout) 442 | { 443 | $this->_timeout = $timeout; 444 | } 445 | 446 | /** 447 | * @param int $connectTimeout 448 | */ 449 | public function setConnectTimeout($connectTimeout) 450 | { 451 | $this->_connectTimeout = $connectTimeout; 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /src/InstagramBasicDisplayException.php: -------------------------------------------------------------------------------- 1 |