Completed
Push — master ( 920fb9...e44c5c )
by Steven
08:41
created

LinkedIn::extractEmailFromResponse()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 12
cts 12
cp 1
rs 8.8333
c 0
b 0
f 0
cc 7
nc 3
nop 1
crap 7
1
<?php
2
3
namespace League\OAuth2\Client\Provider;
4
5
use InvalidArgumentException;
6
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
7
use League\OAuth2\Client\Token\AccessToken;
8
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
9
use Psr\Http\Message\ResponseInterface;
10
11
class LinkedIn extends AbstractProvider
12
{
13
    use BearerAuthorizationTrait;
14
15
    /**
16
     * Default scopes
17
     *
18
     * @var array
19
     */
20
    public $defaultScopes = ['r_liteprofile', 'r_emailaddress'];
21
22
    /**
23
     * Requested fields in scope, seeded with default values
24
     *
25
     * @var array
26
     * @see https://developer.linkedin.com/docs/fields/basic-profile
27
     */
28
    protected $fields = [
29
        'id', 'firstName', 'lastName', 'localizedFirstName', 'localizedLastName',
30
        'profilePicture(displayImage~:playableStreams)',
31
    ];
32
33
    /**
34
     * Constructs an OAuth 2.0 service provider.
35
     *
36
     * @param array $options An array of options to set on this provider.
37
     *     Options include `clientId`, `clientSecret`, `redirectUri`, and `state`.
38
     *     Individual providers may introduce more options, as needed.
39
     * @param array $collaborators An array of collaborators that may be used to
40
     *     override this provider's default behavior. Collaborators include
41
     *     `grantFactory`, `requestFactory`, and `httpClient`.
42
     *     Individual providers may introduce more collaborators, as needed.
43
     */
44 14
    public function __construct(array $options = [], array $collaborators = [])
45
    {
46 14
        if (isset($options['fields']) && !is_array($options['fields'])) {
47 1
            throw new InvalidArgumentException('The fields option must be an array');
48
        }
49
50 14
        parent::__construct($options, $collaborators);
51 14
    }
52
53
    /**
54
     * Get the string used to separate scopes.
55
     *
56
     * @return string
57
     */
58 3
    protected function getScopeSeparator()
59
    {
60 3
        return ' ';
61
    }
62
63
    /**
64
     * Get authorization url to begin OAuth flow
65
     *
66
     * @return string
67
     */
68 3
    public function getBaseAuthorizationUrl()
69
    {
70 3
        return 'https://www.linkedin.com/oauth/v2/authorization';
71
    }
72
73
    /**
74
     * Get access token url to retrieve token
75
     *
76
     * @return string
77
     */
78 7
    public function getBaseAccessTokenUrl(array $params)
79
    {
80 7
        return 'https://www.linkedin.com/oauth/v2/accessToken';
81
    }
82
83
    /**
84
     * Get provider url to fetch user details
85
     *
86
     * @param  AccessToken $token
87
     *
88
     * @return string
89
     */
90 3
    public function getResourceOwnerDetailsUrl(AccessToken $token)
91
    {
92 3
        $query = http_build_query([
93 3
            'projection' => '(' . implode(',', $this->fields) . ')'
94
        ]);
95
96 3
        return 'https://api.linkedin.com/v2/me?' . urldecode($query);
97
    }
98
99
    /**
100
     * Get provider url to fetch user details
101
     *
102
     * @param  AccessToken $token
103
     *
104
     * @return string
105
     */
106 3
    public function getResourceOwnerEmailUrl(AccessToken $token)
0 ignored issues
show
Unused Code introduced by
The parameter $token is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
107
    {
108 3
        $query = http_build_query([
109 3
            'q' => 'members',
110
            'projection' => '(elements*(state,primary,type,handle~))'
111
        ]);
112
113 3
        return 'https://api.linkedin.com/v2/clientAwareMemberHandles?' . urldecode($query);
114
    }
115
116
    /**
117
     * Get the default scopes used by this provider.
118
     *
119
     * This should not be a complete list of all scopes, but the minimum
120
     * required for the provider user interface!
121
     *
122
     * @return array
123
     */
124 2
    protected function getDefaultScopes()
125
    {
126 2
        return $this->defaultScopes;
127
    }
128
129
    /**
130
     * Check a provider response for errors.
131
     *
132
     * @throws IdentityProviderException
133
     * @param  ResponseInterface $response
134
     * @param  string $data Parsed response data
135
     * @return void
136
     */
137 6
    protected function checkResponse(ResponseInterface $response, $data)
138
    {
139 6
        if (isset($data['error'])) {
140 1
            throw new IdentityProviderException(
141 1
                $data['error_description'] ?: $response->getReasonPhrase(),
142 1
                $response->getStatusCode(),
143 1
                $response
0 ignored issues
show
Documentation introduced by
$response is of type object<Psr\Http\Message\ResponseInterface>, but the function expects a array|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
144
            );
145
        }
146 5
    }
147
148
    /**
149
     * Generate a user object from a successful user details request.
150
     *
151
     * @param array $response
152
     * @param AccessToken $token
153
     * @return LinkedInResourceOwner
154
     */
155 2
    protected function createResourceOwner(array $response, AccessToken $token)
156
    {
157 2
        return new LinkedInResourceOwner($response);
158
    }
159
160
    /**
161
     * Returns the requested fields in scope.
162
     *
163
     * @return array
164
     */
165 3
    public function getFields()
166
    {
167 3
        return $this->fields;
168
    }
169
170
    /**
171
     * Attempts to fetch resource owner's email address via separate API request.
172
     *
173
     * @param  AccessToken $token [description]
174
     * @return string|null
175
     * @throws IdentityProviderException
176
     */
177 2
    public function getResourceOwnerEmail(AccessToken $token)
178
    {
179 2
        $emailUrl = $this->getResourceOwnerEmailUrl($token);
180 2
        $emailRequest = $this->getAuthenticatedRequest(self::METHOD_GET, $emailUrl, $token);
181 2
        $emailResponse = $this->getParsedResponse($emailRequest);
182
183 2
        if (is_array($emailResponse)) {
184 2
            return $this->extractEmailFromResponse($emailResponse);
185
        }
186
187 1
        return null;
188
    }
189
190
    /**
191
     * Updates the requested fields in scope.
192
     *
193
     * @param  array   $fields
194
     *
195
     * @return LinkedIn
196
     */
197 1
    public function withFields(array $fields)
198
    {
199 1
        $this->fields = $fields;
200
201 1
        return $this;
202
    }
203
204
    /**
205
     * Attempts to extract the email address from a valid email api response.
206
     *
207
     * @param  array  $response
208
     * @return string|null
209
     */
210 2
    protected function extractEmailFromResponse($response = [])
211
    {
212 2
        if (is_array($response) && isset($response['elements'])) {
213 1
            $emailElements = $response['elements'];
214 1
            $emailElements = array_filter($emailElements, function ($element) {
215
                return
216 1
                    strtoupper($element['type']) === 'EMAIL'
217 1
                    && strtoupper($element['state']) === 'CONFIRMED'
218 1
                    && $element['primary'] === true
219 1
                    && isset($element['handle~']['emailAddress'])
220
                ;
221 1
            });
222 1
            $emailElements = array_pop($emailElements);
223
224 1
            return $emailElements ? $emailElements['handle~']['emailAddress'] : null;
225
        }
226
227 1
        return null;
228
    }
229
}
230