Completed
Push — master ( f6e3c2...8e6d68 )
by Haridarshan
02:28
created

Instagram::isAccessTokenPresent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 5
rs 9.4285
cc 2
eloc 3
nc 2
nop 2
1
<?php
2
namespace Haridarshan\Instagram;
3
4
/*
5
* Instagram API class
6
*
7
* API Documentation: http://instagram.com/developer/
8
* Class Documentation: https://github.com/haridarshan/Instagram-php
9
*
10
* @author Haridarshan Gorana
11
* @since May 09, 2016
12
* @copyright Haridarshan Gorana
13
* @version 1.0
14
* @license: MIT
15
*/
16
class Instagram {
17
	/*
18
	* Library Version
19
	*/
20
	const VERSION = '1.0.1';
21
	
22
	/*
23
	* API End Point
24
	*/  
25
	const API_VERSION = 'v1';
26
	
27
	/*
28
	* API End Point
29
	*/  
30
	const API_HOST = 'https://api.instagram.com/';
31
		
32
	/*
33
	* Client Id
34
	* @var: string
35
	*/
36
	private $client_id;
37
	
38
	/*
39
	* Client Secret
40
	* @var: string
41
	*/
42
	private $client_secret;
43
	
44
	/*
45
	* Instagram Callback url
46
	* @var: string
47
	*/
48
	private $callback_url;
49
	
50
	/*
51
	* Oauth Access Token
52
	* @var: string
53
	*/
54
	private $access_token;
55
	
56
	/*
57
	* Instagram Available Scopes
58
	* @var: array of strings
59
	*/
60
	private $default_scopes = array("basic", "public_content", "follower_list", "comments", "relationships", "likes");
61
	
62
	/*
63
	* User's Scope
64
	* @var: array of strings
65
	*/
66
	private $scopes = array();
67
	
68
	/*
69
	* Enable secure request
70
	* @var: boolean
71
	*/ 
72
	private $secure = true;
73
	
74
	/*
75
	* Curl timeout
76
	* @var: integer|decimal|long
77
	*/
78
	private $timeout = 90;
79
	
80
	/*
81
	* Curl Connect timeout
82
	* @var: integer|decimal|long
83
	*/
84
	private $connect_timeout = 20;
85
	
86
	/*
87
	* Remaining Rate Limit
88
	* Sandbox = 500
89
	* Live = 5000
90
	*/
91
	private $x_rate_limit_remaining = 500;
92
	
93
	/*
94
	* @var GuzzleHttp\ClientInterface $http
95
	*/
96
	private $client;
97
	
98
	/*
99
	* @var GuzzleHttp\Psr7\Response $response
100
	*/
101
	private $response;
102
		
103
	/*
104
	* Default Constructor 
105
	* Instagram Configuration Data
106
	* @param array|object|string $config
107
	*/
108
	public function __construct($config) {		
109
		if (is_array($config)) {			
110
			$this->setClientId($config['ClientId']);
111
			$this->setClientSecret($config['ClientSecret']);
112
			$this->setCallbackUrl($config['Callback']);	
113
		} else {
114
			throw new \Haridarshan\Instagram\InstagramException('Invalid Instagram Configuration data');			
115
		}
116
		
117
		$this->client = new \GuzzleHttp\Client([
0 ignored issues
show
Documentation Bug introduced by
It seems like new \GuzzleHttp\Client(a...ri' => self::API_HOST)) of type object<GuzzleHttp\Client> is incompatible with the declared type object<Haridarshan\Insta...leHttp\ClientInterface> of property $client.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
118
			'base_uri' => self::API_HOST
119
		]);
120
	}
121
	
122
	/*
123
	* Make URLs for user browser navigation.
124
	*
125
	* @param string $path
126
	* @param array  $parameters
127
	*
128
	* @return string
129
	*/
130
	public function getUrl($path, array $parameters) {	
131
		
132
		if (!isset($parameters['scope'])) {
133
			throw new \Haridarshan\Instagram\InstagramException("Missing or Invalid Scope permission used");
134
		}
135
				
136
		if (count(array_diff($parameters['scope'], $this->default_scopes)) === 0) {
137
			$this->scopes = $parameters['scope']; 
138
		} else {
139
			throw new \Haridarshan\Instagram\InstagramException("Missing or Invalid Scope permission used");
140
		}
141
142
		$query = 'client_id='.$this->getClientId().'&redirect_uri='.urlencode($this->getCallbackUrl()).'&response_type=code';
143
144
		$query .= isset($this->scopes) ? '&scope='.urlencode(str_replace(",", " ", implode(",", $parameters['scope']))) : '';
145
				
146
		return sprintf('%s%s?%s', self::API_HOST, $path, $query);
147
		
148
	}
149
	
150
	/*
151
	* Get the Oauth Access Token of a user from callback code
152
	* 
153
	* @param string $path - OAuth Access Token Path
154
	* @param string $code - Oauth2 Code returned with callback url after successfull login
155
	* @param boolean $token - true will return only access token
156
	*/
157
	public function getToken($path, $code, $token = false) {
158
		$options = array(
159
			"grant_type" => "authorization_code",
160
			"client_id" => $this->getClientId(),
161
			"client_secret" => $this->getClientSecret(),
162
			"redirect_uri" => $this->getCallbackUrl(),
163
			"code" => $code
164
		);
165
			
166
		$this->execute($path, $options, 'POST');
167
		
168
		if (isset($this->response->code)) {
169
			throw new \Haridarshan\Instagram\InstagramException("return status code: ".$this->response->code." type: ".$this->response->error_type." message: ".$this->response->error_message);
170
		}
171
				
172
		$this->setAccessToken($this->response);
173
				
174
		return !$token ? $this->response : $this->response->access_token;
175
	}
176
	
177
	/*
178
	* Secure API Request by using endpoint, paramters and API secret
179
	* copy from Instagram API Documentation: https://www.instagram.com/developer/secure-api-requests/
180
	* 
181
	* @param string $endpoint
182
	* @param array|string $params
183
	*
184
	* @return string (Signature)
185
	*/
186
	protected function secureRequest($endpoint, $params) {			
187
		$signature = $endpoint;
188
		ksort($params);
189
		
190
		foreach ($params as $key => $value) {
191
			$signature .= "|$key=$value";	
192
		}
193
		return hash_hmac('sha256', $signature, $this->getClientSecret(), false);
194
	}
195
	
196
	/* 
197
	* Method to make api requests
198
	* @return mixed
199
	*/
200
	public function request($path, array $params, $method = 'GET') {
201
		$this->isRateLimitReached();
202
		
203
		$this->isAccessTokenPresent($path, $params);
204
								
205
		$this->setAccessToken($params['access_token']);	
206
		
207
		$authentication_method = '?access_token='.$this->access_token;
208
		
209
		$endpoint = self::API_VERSION.$path.(('GET' === $method) ? $authentication_method.'&'.http_build_query($params) : $authentication_method);
210
				
211
		$endpoint .= (strstr($endpoint, '?') ? '&' : '?').'sig='.$this->secureRequest($path, $params);
212
		
213
		$this->execute($endpoint, $params, $method);
214
		return $this->response;	
215
	}
216
	
217
	/*
218
	* Method to make GuzzleHttp Client Request to Instagram APIs
219
	*
220
	* @param string $endpoint
221
	* @param array|string $options in case of POST [optional]
222
	* @param string $method GET|POST
223
	*/
224
	protected function execute($endpoint, $options, $method = 'GET') {	
225
		try {	
226
			$result = $result = $this->client->request(
227
				$method, 
228
				$endpoint, 
229
				[
230
					'headers' => [
231
						'Accept'     => 'application/json'
232
					],
233
					'body' => ('GET' !== $method) ? is_array($options) ? http_build_query($options) : ltrim($options, '&') : null
234
				]
235
			);
236
		} catch (\GuzzleHttp\Exception\ClientException $e) {
237
			throw new \Haridarshan\Instagram\InstagramException($e->getMessage());
238
		}
239
		
240
		$limit = $result->getHeader('x-ratelimit-remaining');
241
		$this->x_rate_limit_remaining = $limit[0];
242
243
		$this->response = json_decode($result->getBody()->getContents());
244
	}
245
	
246
	/*
247
	* Setter: Client Id
248
	* @param string $clientId
249
	* @return void
250
	*/
251
	public function setClientId($clientId) {
252
		$this->client_id = $clientId;	
253
	}
254
	
255
	/*
256
	* Getter: Client Id
257
	* @return string
258
	*/
259
	public function getClientId() {
260
		return $this->client_id;	
261
	}
262
	
263
	/*
264
	* Setter: Client Secret
265
	* @param string $secret
266
	* @return void
267
	*/
268
	public function setClientSecret($secret) {
269
		$this->client_secret = $secret;	
270
	}
271
	
272
	/*
273
	* Getter: Client Id
274
	* @return string
275
	*/
276
	public function getClientSecret() {
277
		return $this->client_secret;	
278
	}
279
	
280
	/*
281
	* Setter: Callback Url
282
	* @param string $url
283
	* @return void
284
	*/
285
	public function setCallbackUrl($url) {
286
		$this->callback_url = $url;	
287
	}
288
	
289
	/*
290
	* Getter: Callback Url
291
	* @return string
292
	*/
293
	public function getCallbackUrl() {
294
		return $this->callback_url;	
295
	}
296
	
297
	/*
298
	* Setter: Set Curl Timeout
299
	* @param integer|decimal|long $time
300
	* @return void
301
	*/
302
	public function setTimeout($time = 90) {
303
		$this->timeout = $time;	
304
	}
305
	
306
	/*
307
	* Getter: Get Curl Timeout
308
	* @return integer|decimal|long
309
	*/
310
	public function getTimeout() {
311
		return $this->timeout;	
312
	}
313
	
314
	/*
315
	* Setter: Set Curl Timeout
316
	* @param integer|decimal|long $time
317
	* @return void
318
	*/
319
	public function setConnectTimeout($time = 20) {
320
		$this->connect_timeout = $time;	
321
	}
322
	
323
	/*
324
	* Getter: Get Curl connect timeout
325
	* @return integer|decimal|long
326
	*/
327
	public function getConnectTimeout() {
328
		return $this->connect_timeout;	
329
	}
330
	
331
	/*
332
	* Setter: Enfore Signed Request
333
	* @param boolean $secure
334
	* @return void
335
	*/
336
	public function setRequestSecure($secure) {
337
		$this->secure = $secure;	
338
	}	
339
	
340
	/*
341
	* Setter: User Access Token
342
	* @param object|string $data
343
	* @return void
344
	*/
345
	private function setAccessToken($data) {		
346
		$token = is_object($data) ? $data->access_token : $data;
347
		$this->access_token = $token;
348
	}
349
	
350
	/*
351
	* Getter: User Access Token
352
	* @return string
353
	*/
354
	public function getAccessToken() {
355
		return isset($this->access_token) ? $this->access_token : null;
356
	}
357
		
358
	/*
359
	* Get a string containing the version of the library.
360
	* @return string
361
	*/
362
	public function getLibraryVersion() {
363
		return self::VERSION;
364
	}
365
	
366
	/*
367
	* Check whether api rate limit is reached or not
368
	* throws \Haridarshan\Instagram\InstagramException
369
	*/
370
	private function isRateLimitReached() {
371
		if (!$this->x_rate_limit_remaining) {
372
			throw new \Haridarshan\Instagram\InstagramException("You have reached Instagram API Rate Limit");
373
		}
374
	}
375
	
376
	/*
377
	* Check whether access token is present or not
378
	* throws \Haridarshan\Instagram\InstagramException
379
	*/
380
	private function isAccessTokenPresent($api, array $params) {
381
		if (!isset($params['access_token'])) {
382
			throw new \Haridarshan\Instagram\InstagramException("$api - api requires an authenticated users access token.");
383
		}	
384
	}
385
}
386