Completed
Pull Request — master (#73)
by Tobias
02:38
created

LinkedIn::api()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 13
Bugs 2 Features 0
Metric Value
c 13
b 2
f 0
dl 0
loc 27
rs 8.5806
cc 4
eloc 14
nc 4
nop 3
1
<?php
2
3
namespace Happyr\LinkedIn;
4
5
use Happyr\LinkedIn\Exception\LoginError;
6
use Happyr\LinkedIn\Http\GlobalVariableGetter;
7
use Happyr\LinkedIn\Http\RequestManager;
8
use Happyr\LinkedIn\Http\ResponseConverter;
9
use Happyr\LinkedIn\Http\UrlGenerator;
10
use Happyr\LinkedIn\Http\UrlGeneratorInterface;
11
use Happyr\LinkedIn\Storage\DataStorageInterface;
12
use Http\Client\HttpClient;
13
use Psr\Http\Message\ResponseInterface;
14
15
/**
16
 * Class LinkedIn lets you talk to LinkedIn api.
17
 *
18
 * When a new user arrives and want to authenticate here is whats happens:
19
 * 1. You redirect him to whatever url getLoginUrl() returns.
20
 * 2. The user logs in on www.linkedin.com and authorize your application.
21
 * 3. The user returns to your site with a *code* in the the $_REQUEST.
22
 * 4. You call isAuthenticated() or getAccessToken()
23
 * 5. We don't got an access token (only a *code*). So getAccessToken() calls fetchNewAccessToken()
24
 * 6. fetchNewAccessToken() gets the *code* from the $_REQUEST and calls getAccessTokenFromCode()
25
 * 7. getAccessTokenFromCode() makes a request to www.linkedin.com and exchanges the *code* for an access token
26
 * 8. When you have the access token you should store it in a database and/or query the API.
27
 * 9. When you make a second request to the API we have the access token in memory, so we don't go through all these
28
 *    authentication steps again.
29
 *
30
 * @author Tobias Nyholm
31
 */
32
class LinkedIn
33
{
34
    /**
35
     * The OAuth access token received in exchange for a valid authorization
36
     * code.  null means the access token has yet to be determined.
37
     *
38
     * @var AccessToken
39
     */
40
    protected $accessToken = null;
41
42
    /**
43
     * @var string format
44
     */
45
    private $format;
46
47
    /**
48
     * @var string responseFormat
49
     */
50
    private $responseDataType;
51
52
    /**
53
     * @var ResponseInterface
54
     */
55
    private $lastResponse;
56
57
    /**
58
     * @var RequestManager
59
     */
60
    private $requestManager;
61
62
    /**
63
     * @var Authenticator
64
     */
65
    private $authenticator;
66
67
    /**
68
     * @var UrlGeneratorInterface
69
     */
70
    private $urlGenerator;
71
72
    /**
73
     * Constructor.
74
     *
75
     * @param string $appId
76
     * @param string $appSecret
77
     * @param string $format           'json', 'xml'
78
     * @param string $responseDataType 'array', 'string', 'simple_xml' 'psr7', 'stream'
79
     */
80
    public function __construct($appId, $appSecret, $format = 'json', $responseDataType = 'array')
81
    {
82
        $this->format = $format;
83
        $this->responseDataType = $responseDataType;
84
85
        $this->requestManager = new RequestManager();
86
        $this->authenticator = new Authenticator($this->requestManager, $appId, $appSecret);
87
    }
88
89
    /**
90
     * Is the current user authenticated?
91
     *
92
     * @return bool
93
     */
94
    public function isAuthenticated()
95
    {
96
        $accessToken = $this->getAccessToken();
97
        if ($accessToken === null) {
98
            return false;
99
        }
100
101
        $user = $this->api('GET', '/v1/people/~:(id,firstName,lastName)', array('format' => 'json', 'response_data_type' => 'array'));
102
103
        return !empty($user['id']);
104
    }
105
106
    /**
107
     * Make an API call. Read about what calls that are possible here: https://developer.linkedin.com/docs/rest-api.
108
     *
109
     * Example:
110
     * $linkedIn->api('GET', '/v1/people/~:(id,firstName,lastName,headline)');
111
     *
112
     * The options:
113
     * - body: the body of the request
114
     * - format: the format you are using to send the request
115
     * - headers: array with headers to use
116
     * - response_data_type: the data type to get back
117
     * - query: query parameters to the request
118
     *
119
     * @param string $method   This is the HTTP verb
120
     * @param string $resource everything after the domain in the URL.
121
     * @param array  $options  See the readme for option description.
122
     *
123
     * @return mixed this depends on the response_data_type parameter.
124
     */
125
    public function api($method, $resource, array $options = array())
126
    {
127
        // Add access token to the headers
128
        $options['headers']['Authorization'] = sprintf('Bearer %s', (string) $this->getAccessToken());
129
130
        // Do logic and adjustments to the options
131
        $requestFormat = $this->filterRequestOption($options);
132
133
        // Generate an url
134
        $url = $this->getUrlGenerator()->getUrl(
135
            'api',
136
            $resource,
137
            isset($options['query']) ? $options['query'] : array()
138
        );
139
140
        $body = isset($options['body']) ? $options['body'] : null;
141
        $this->lastResponse = $this->getRequestManager()->sendRequest($method, $url, $options['headers'], $body);
142
143
        //Get the response data format
144
        if (isset($options['response_data_type'])) {
145
            $responseDataType = $options['response_data_type'];
146
        } else {
147
            $responseDataType = $this->getResponseDataType();
148
        }
149
150
        return ResponseConverter::convert($this->lastResponse, $requestFormat, $responseDataType);
151
    }
152
153
    /**
154
     * Modify and filter the request options. Make sure we use the correct query parameters and headers.
155
     *
156
     * @param array &$options
157
     *
158
     * @return string the request format to use
159
     */
160
    protected function filterRequestOption(array &$options)
161
    {
162
        if (isset($options['json'])) {
163
            $options['format'] = 'json';
164
            $options['body'] = json_encode($options['json']);
165
        } elseif (!isset($options['format'])) {
166
            // Make sure we always have a format
167
            $options['format'] = $this->getFormat();
168
        }
169
170
        // Set correct headers for this format
171
        switch ($options['format']) {
172
            case 'xml':
173
                $options['headers']['Content-Type'] = 'text/xml';
174
                break;
175
            case 'json':
176
                $options['headers']['Content-Type'] = 'application/json';
177
                $options['headers']['x-li-format'] = 'json';
178
                $options['query']['format'] = 'json';
179
                break;
180
            default:
181
                // Do nothing
182
        }
183
184
        return $options['format'];
185
    }
186
187
    /**
188
     * Get a login URL where the user can put his/hers LinkedIn credentials and authorize the application.
189
     *
190
     * The options:
191
     * - redirect_uri: the url to go to after a successful login
192
     * - scope: comma (or space) separated list of requested extended permissions
193
     *
194
     * @param array $options Provide custom parameters
195
     *
196
     * @return string The URL for the login flow
197
     */
198
    public function getLoginUrl($options = array())
199
    {
200
        return $this->getAuthenticator()->getLoginUrl($this->getUrlGenerator(), $options);
201
    }
202
203
    /**
204
     * See docs for LinkedIn::api().
205
     *
206
     * @param string $resource
207
     * @param array  $options
208
     *
209
     * @return mixed
210
     */
211
    public function get($resource, array $options = array())
212
    {
213
        return $this->api('GET', $resource, $options);
214
    }
215
216
    /**
217
     * See docs for LinkedIn::api().
218
     *
219
     * @param string $resource
220
     * @param array  $options
221
     *
222
     * @return mixed
223
     */
224
    public function post($resource, array $options = array())
225
    {
226
        return $this->api('POST', $resource, $options);
227
    }
228
229
    /**
230
     * Clear the data storage. This will forget everything about the user and authentication process.
231
     *
232
     * @return $this
233
     */
234
    public function clearStorage()
235
    {
236
        $this->getAuthenticator()->clearStorage();
237
238
        return $this;
239
    }
240
241
    /**
242
     * If the user has canceled the login we will return with an error.
243
     *
244
     * @return bool
245
     */
246
    public function hasError()
247
    {
248
        return GlobalVariableGetter::has('error');
249
    }
250
251
    /**
252
     * Returns a LoginError or null.
253
     *
254
     * @return LoginError|null
255
     */
256
    public function getError()
257
    {
258
        if ($this->hasError()) {
259
            return new LoginError(GlobalVariableGetter::get('error'), GlobalVariableGetter::get('error_description'));
260
        }
261
    }
262
263
    /**
264
     * Get the default format to use when sending requests.
265
     *
266
     * @return string
267
     */
268
    public function getFormat()
269
    {
270
        return $this->format;
271
    }
272
273
    /**
274
     * Set the default format to use when sending requests.
275
     *
276
     * @param string $format
277
     *
278
     * @return $this
279
     */
280
    public function setFormat($format)
281
    {
282
        $this->format = $format;
283
284
        return $this;
285
    }
286
287
    /**
288
     * Get the default data type to be returned as a response.
289
     *
290
     * @return string
291
     */
292
    public function getResponseDataType()
293
    {
294
        return $this->responseDataType;
295
    }
296
297
    /**
298
     * Set the default data type to be returned as a response.
299
     *
300
     * @param string $responseDataType
301
     *
302
     * @return $this
303
     */
304
    public function setResponseDataType($responseDataType)
305
    {
306
        $this->responseDataType = $responseDataType;
307
308
        return $this;
309
    }
310
311
    /**
312
     * Get the last response. This will always return a PSR-7 response no matter of the data type used.
313
     *
314
     * @return ResponseInterface|null
315
     */
316
    public function getLastResponse()
317
    {
318
        return $this->lastResponse;
319
    }
320
321
    /**
322
     * Returns an access token. If we do not have one in memory, try to fetch one from a *code* in the $_REQUEST.
323
     *
324
     * @return AccessToken|null The access token of null if the access token is not found
325
     */
326
    public function getAccessToken()
327
    {
328
        if ($this->accessToken === null) {
329
            if (null !== $newAccessToken = $this->getAuthenticator()->fetchNewAccessToken($this->getUrlGenerator())) {
330
                $this->setAccessToken($newAccessToken);
331
            }
332
        }
333
334
        // return the new access token or null if none found
335
        return $this->accessToken;
336
    }
337
338
    /**
339
     * If you have stored a previous access token in a storage (database) you could set it here. After setting an
340
     * access token you have to make sure to verify it is still valid by running LinkedIn::isAuthenticated.
341
     *
342
     * @param string|AccessToken $accessToken
343
     *
344
     * @return $this
345
     */
346
    public function setAccessToken($accessToken)
347
    {
348
        if (!($accessToken instanceof AccessToken)) {
349
            $accessToken = new AccessToken($accessToken);
350
        }
351
352
        $this->accessToken = $accessToken;
353
354
        return $this;
355
    }
356
357
    /**
358
     * Set a URL generator.
359
     *
360
     * @param UrlGeneratorInterface $urlGenerator
361
     *
362
     * @return $this
363
     */
364
    public function setUrlGenerator(UrlGeneratorInterface $urlGenerator)
365
    {
366
        $this->urlGenerator = $urlGenerator;
367
368
        return $this;
369
    }
370
371
    /**
372
     * @return UrlGeneratorInterface
373
     */
374
    protected function getUrlGenerator()
375
    {
376
        if ($this->urlGenerator === null) {
377
            $this->urlGenerator = new UrlGenerator();
378
        }
379
380
        return $this->urlGenerator;
381
    }
382
383
    /**
384
     * Set a data storage.
385
     *
386
     * @param DataStorageInterface $storage
387
     *
388
     * @return $this
389
     */
390
    public function setStorage(DataStorageInterface $storage)
391
    {
392
        $this->getAuthenticator()->setStorage($storage);
393
394
        return $this;
395
    }
396
397
    /**
398
     * Set a http client.
399
     *
400
     * @param HttpClient $client
401
     *
402
     * @return $this
403
     */
404
    public function setHttpClient(HttpClient $client)
405
    {
406
        $this->getRequestManager()->setHttpClient($client);
407
408
        return $this;
409
    }
410
411
    /**
412
     * @return RequestManager
413
     */
414
    protected function getRequestManager()
415
    {
416
        return $this->requestManager;
417
    }
418
419
    /**
420
     * @return Authenticator
421
     */
422
    protected function getAuthenticator()
423
    {
424
        return $this->authenticator;
425
    }
426
}
427