Completed
Push — master ( e44c5c...9d06c6 )
by Steven
03:52
created

LinkedIn::extractEmailFromResponse()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 9.3888
c 0
b 0
f 0
cc 5
nc 3
nop 1
crap 5
1
<?php
2
3
namespace League\OAuth2\Client\Provider;
4
5
use Exception;
6
use InvalidArgumentException;
7
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
8
use League\OAuth2\Client\Token\AccessToken;
9
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
10
use Psr\Http\Message\ResponseInterface;
11
12
class LinkedIn extends AbstractProvider
13
{
14
    use BearerAuthorizationTrait;
15
16
    /**
17
     * Default scopes
18
     *
19
     * @var array
20
     */
21
    public $defaultScopes = ['r_liteprofile', 'r_emailaddress'];
22
23
    /**
24
     * Requested fields in scope, seeded with default values
25
     *
26
     * @var array
27
     * @see https://developer.linkedin.com/docs/fields/basic-profile
28
     */
29
    protected $fields = [
30
        'id', 'firstName', 'lastName', 'localizedFirstName', 'localizedLastName',
31
        'profilePicture(displayImage~:playableStreams)',
32
    ];
33
34
    /**
35
     * Constructs an OAuth 2.0 service provider.
36
     *
37
     * @param array $options An array of options to set on this provider.
38
     *     Options include `clientId`, `clientSecret`, `redirectUri`, and `state`.
39
     *     Individual providers may introduce more options, as needed.
40
     * @param array $collaborators An array of collaborators that may be used to
41
     *     override this provider's default behavior. Collaborators include
42
     *     `grantFactory`, `requestFactory`, and `httpClient`.
43
     *     Individual providers may introduce more collaborators, as needed.
44
     */
45 14
    public function __construct(array $options = [], array $collaborators = [])
46
    {
47 14
        if (isset($options['fields']) && !is_array($options['fields'])) {
48 1
            throw new InvalidArgumentException('The fields option must be an array');
49
        }
50
51 14
        parent::__construct($options, $collaborators);
52 14
    }
53
54
    /**
55
     * Get the string used to separate scopes.
56
     *
57
     * @return string
58
     */
59 3
    protected function getScopeSeparator()
60
    {
61 3
        return ' ';
62
    }
63
64
    /**
65
     * Get authorization url to begin OAuth flow
66
     *
67
     * @return string
68
     */
69 3
    public function getBaseAuthorizationUrl()
70
    {
71 3
        return 'https://www.linkedin.com/oauth/v2/authorization';
72
    }
73
74
    /**
75
     * Get access token url to retrieve token
76
     *
77
     * @return string
78
     */
79 7
    public function getBaseAccessTokenUrl(array $params)
80
    {
81 7
        return 'https://www.linkedin.com/oauth/v2/accessToken';
82
    }
83
84
    /**
85
     * Get provider url to fetch user details
86
     *
87
     * @param  AccessToken $token
88
     *
89
     * @return string
90
     */
91 3
    public function getResourceOwnerDetailsUrl(AccessToken $token)
92
    {
93 3
        $query = http_build_query([
94 3
            'projection' => '(' . implode(',', $this->fields) . ')'
95
        ]);
96
97 3
        return 'https://api.linkedin.com/v2/me?' . urldecode($query);
98
    }
99
100
    /**
101
     * Get provider url to fetch user details
102
     *
103
     * @param  AccessToken $token
104
     *
105
     * @return string
106
     */
107 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...
108
    {
109 3
        $query = http_build_query([
110 3
            'q' => 'members',
111
            'projection' => '(elements*(state,primary,type,handle~))'
112
        ]);
113
114 3
        return 'https://api.linkedin.com/v2/clientAwareMemberHandles?' . urldecode($query);
115
    }
116
117
    /**
118
     * Get the default scopes used by this provider.
119
     *
120
     * This should not be a complete list of all scopes, but the minimum
121
     * required for the provider user interface!
122
     *
123
     * @return array
124
     */
125 2
    protected function getDefaultScopes()
126
    {
127 2
        return $this->defaultScopes;
128
    }
129
130
    /**
131
     * Check a provider response for errors.
132
     *
133
     * @throws IdentityProviderException
134
     * @param  ResponseInterface $response
135
     * @param  string $data Parsed response data
136
     * @return void
137
     */
138 6
    protected function checkResponse(ResponseInterface $response, $data)
139
    {
140 6
        if (isset($data['error'])) {
141 1
            throw new IdentityProviderException(
142 1
                $data['error_description'] ?: $response->getReasonPhrase(),
143 1
                $response->getStatusCode(),
144 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...
145
            );
146
        }
147 5
    }
148
149
    /**
150
     * Generate a user object from a successful user details request.
151
     *
152
     * @param array $response
153
     * @param AccessToken $token
154
     * @return LinkedInResourceOwner
155
     */
156 2
    protected function createResourceOwner(array $response, AccessToken $token)
157
    {
158 2
        return new LinkedInResourceOwner($response);
159
    }
160
161
    /**
162
     * Returns the requested fields in scope.
163
     *
164
     * @return array
165
     */
166 3
    public function getFields()
167
    {
168 3
        return $this->fields;
169
    }
170
171
    /**
172
     * Attempts to fetch resource owner's email address via separate API request.
173
     *
174
     * @param  AccessToken $token [description]
175
     * @return string|null
176
     * @throws IdentityProviderException
177
     */
178 2
    public function getResourceOwnerEmail(AccessToken $token)
179
    {
180 2
        $emailUrl = $this->getResourceOwnerEmailUrl($token);
181 2
        $emailRequest = $this->getAuthenticatedRequest(self::METHOD_GET, $emailUrl, $token);
182 2
        $emailResponse = $this->getParsedResponse($emailRequest);
183
184 2
        return $this->extractEmailFromResponse($emailResponse);
0 ignored issues
show
Bug introduced by
It seems like $emailResponse defined by $this->getParsedResponse($emailRequest) on line 182 can also be of type null or string; however, League\OAuth2\Client\Pro...ractEmailFromResponse() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
185
    }
186
187
    /**
188
     * Updates the requested fields in scope.
189
     *
190
     * @param  array   $fields
191
     *
192
     * @return LinkedIn
193
     */
194 1
    public function withFields(array $fields)
195
    {
196 1
        $this->fields = $fields;
197
198 1
        return $this;
199
    }
200
201
    /**
202
     * Attempts to extract the email address from a valid email api response.
203
     *
204
     * @param  array  $response
205
     * @return string|null
206
     */
207
    protected function extractEmailFromResponse($response = [])
208
    {
209
        try {
210 2
            $confirmedEmails = array_filter($response['elements'], function ($element) {
211
                return
212 1
                    strtoupper($element['type']) === 'EMAIL'
213 1
                    && strtoupper($element['state']) === 'CONFIRMED'
214 1
                    && $element['primary'] === true
215 1
                    && isset($element['handle~']['emailAddress'])
216
                ;
217 2
            });
218
219 1
            return $confirmedEmails[0]['handle~']['emailAddress'];
220 1
        } catch (Exception $e) {
221 1
            return null;
222
        }
223
    }
224
}
225