Completed
Push — master ( 05d0a6...f721c8 )
by
unknown
02:20 queued 12s
created

Instagram::maintainToken()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.2222
c 0
b 0
f 0
cc 6
nc 7
nop 0
1
<?php
2
/*!
3
* Hybridauth
4
* https://hybridauth.github.io | https://github.com/hybridauth/hybridauth
5
*  (c) 2020 Hybridauth authors | https://hybridauth.github.io/license.html
6
*/
7
8
namespace Hybridauth\Provider;
9
10
use Hybridauth\Adapter\OAuth2;
11
use Hybridauth\Data\Collection;
12
use Hybridauth\Exception\UnexpectedApiResponseException;
13
use Hybridauth\User;
14
15
/**
16
 * Instagram OAuth2 provider adapter via Instagram Basic Display API.
17
 */
18
class Instagram extends OAuth2
19
{
20
    /**
21
     * {@inheritdoc}
22
     */
23
    protected $scope = 'user_profile,user_media';
24
25
    /**
26
     * {@inheritdoc}
27
     */
28
    protected $apiBaseUrl = 'https://graph.instagram.com';
29
30
    /**
31
     * {@inheritdoc}
32
     */
33
    protected $authorizeUrl = 'https://api.instagram.com/oauth/authorize';
34
35
    /**
36
     * {@inheritdoc}
37
     */
38
    protected $accessTokenUrl = 'https://api.instagram.com/oauth/access_token';
39
40
    /**
41
     * {@inheritdoc}
42
     */
43
    protected $apiDocumentation = 'https://developers.facebook.com/docs/instagram-basic-display-api';
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    protected function initialize()
49
    {
50
        parent::initialize();
51
52
        // The Instagram API requires an access_token from authenticated users
53
        // for each endpoint.
54
        $accessToken = $this->getStoredData($this->accessTokenName);
55
        $this->apiRequestParameters[$this->accessTokenName] = $accessToken;
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    protected function validateAccessTokenExchange($response)
62
    {
63
        $collection = parent::validateAccessTokenExchange($response);
64
65
        if (!$collection->exists('expires_in')) {
66
            // Instagram tokens always expire in an hour, but this is implicit not explicit
67
68
            $expires_in = 60 * 60;
69
70
            $expires_at = time() + $expires_in;
71
72
            $this->storeData('expires_in', $expires_in);
73
            $this->storeData('expires_at', $expires_at);
74
        }
75
76
        return $collection;
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function maintainToken()
83
    {
84
        if (!$this->isConnected()) {
85
            return;
86
        }
87
88
        // Handle token exchange prior to the standard handler for an API request
89
        $exchange_by_expiry_days = $this->config->get('exchange_by_expiry_days') ?: 45;
90
        if ($exchange_by_expiry_days !== null) {
91
            $projected_timestamp = time() + 60 * 60 * 24 * $exchange_by_expiry_days;
92
            if (!$this->hasAccessTokenExpired() && $this->hasAccessTokenExpired($projected_timestamp)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->hasAccessTokenExpired() of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
93
                $this->exchangeAccessToken();
94
            }
95
        }
96
    }
97
98
    /**
99
     * Exchange the Access Token with one that expires further in the future.
100
     *
101
     * @return string Raw Provider API response
102
     * @throws \Hybridauth\Exception\HttpClientFailureException
103
     * @throws \Hybridauth\Exception\HttpRequestFailedException
104
     * @throws InvalidAccessTokenException
105
     */
106
    public function exchangeAccessToken()
107
    {
108
        $exchangeTokenParameters = [
109
            'grant_type'        => 'ig_exchange_token',
110
            'client_secret'     => $this->clientSecret,
111
            'access_token'      => $this->getStoredData('access_token'),
112
        ];
113
114
        $response = $this->httpClient->request(
115
            'https://graph.instagram.com/access_token',
116
            'GET',
117
            $exchangeTokenParameters
118
        );
119
120
        $this->validateApiResponse('Unable to exchange the access token');
121
122
        $this->validateAccessTokenExchange($response);
123
124
        return $response;
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function getUserProfile()
131
    {
132
        $response = $this->apiRequest('me', 'GET', [
133
            'fields' => 'id,username,account_type,media_count',
134
        ]);
135
136
        $data = new Collection($response);
137
        if (!$data->exists('id')) {
138
            throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
139
        }
140
141
        $userProfile = new User\Profile();
142
        $userProfile->identifier = $data->get('id');
143
        $userProfile->displayName = $data->get('username');
144
        $userProfile->profileURL = "https://instagram.com/{$userProfile->displayName}";
145
        $userProfile->data = [
146
            'account_type' => $data->get('account_type'),
147
            'media_count' => $data->get('media_count'),
148
        ];
149
150
        return $userProfile;
151
    }
152
153
    /**
154
     * Fetch user medias.
155
     *
156
     * @param int $limit Number of elements per page.
157
     * @param string $pageId Current pager ID.
158
     * @param array|null $fields Fields to fetch per media.
159
     *
160
     * @return \Hybridauth\Data\Collection
161
     *
162
     * @throws \Hybridauth\Exception\HttpClientFailureException
163
     * @throws \Hybridauth\Exception\HttpRequestFailedException
164
     * @throws \Hybridauth\Exception\InvalidAccessTokenException
165
     * @throws \Hybridauth\Exception\UnexpectedApiResponseException
166
     */
167
    public function getUserMedia($limit = 12, $pageId = null, array $fields = null)
168
    {
169
        if (empty($fields)) {
170
            $fields = [
171
                'id',
172
                'caption',
173
                'media_type',
174
                'media_url',
175
                'thumbnail_url',
176
                'permalink',
177
                'timestamp',
178
                'username',
179
            ];
180
        }
181
182
        $params = [
183
            'fields' => implode(',', $fields),
184
            'limit' => $limit,
185
        ];
186
        if ($pageId !== null) {
187
            $params['after'] = $pageId;
188
        }
189
190
        $response = $this->apiRequest('me/media', 'GET', $params);
191
192
        $data = new Collection($response);
193
        if (!$data->exists('data')) {
194
            throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
195
        }
196
197
        return $data;
198
    }
199
200
    /**
201
     * Fetches a single user's media.
202
     *
203
     * @param string $mediaId Media ID.
204
     * @param array|null $fields Fields to fetch per media.
205
     *
206
     * @return \Hybridauth\Data\Collection
207
     *
208
     * @throws \Hybridauth\Exception\HttpClientFailureException
209
     * @throws \Hybridauth\Exception\HttpRequestFailedException
210
     * @throws \Hybridauth\Exception\InvalidAccessTokenException
211
     * @throws \Hybridauth\Exception\UnexpectedApiResponseException
212
     */
213
    public function getMedia($mediaId, array $fields = null)
214
    {
215
        if (empty($fields)) {
216
            $fields = [
217
                'id',
218
                'caption',
219
                'media_type',
220
                'media_url',
221
                'thumbnail_url',
222
                'permalink',
223
                'timestamp',
224
                'username',
225
            ];
226
        }
227
228
        $response = $this->apiRequest($mediaId, 'GET', [
229
            'fields' => implode(',', $fields),
230
        ]);
231
232
        $data = new Collection($response);
233
        if (!$data->exists('id')) {
234
            throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
235
        }
236
237
        return $data;
238
    }
239
}
240