Completed
Pull Request — master (#564)
by
unknown
01:25
created

Wechat::parseAccessTokenResponse()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 9.2248
c 0
b 0
f 0
cc 5
nc 4
nop 1
1
<?php
2
3
namespace OAuth\OAuth2\Service;
4
5
use OAuth\OAuth2\Token\StdOAuth2Token;
6
use OAuth\Common\Http\Exception\TokenResponseException;
7
use OAuth\Common\Http\Uri\Uri;
8
use OAuth\Common\Consumer\CredentialsInterface;
9
use OAuth\Common\Http\Client\ClientInterface;
10
use OAuth\Common\Storage\TokenStorageInterface;
11
use OAuth\Common\Http\Uri\UriInterface;
12
13
/**
14
 * Wechat service.
15
 *
16
 * @author snaill <[email protected]>
17
 * @link https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
18
 */
19
class Wechat extends AbstractService
20
{
21
    const SCOPE_SNSAPI_BASE             = 'snsapi_base';
22
    const SCOPE_SNSAPI_USERINFO         = 'snsapi_userinfo';
23
24
    public function __construct(
25
        CredentialsInterface $credentials,
26
        ClientInterface $httpClient,
27
        TokenStorageInterface $storage,
28
        $scopes = array(),
29
        UriInterface $baseApiUri = null
30
    ) {
31
        parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri);
32
33
        if (null === $baseApiUri) {
34
            $this->baseApiUri = new Uri('https://open.weixin.qq.com/connect/');
35
        }
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function getAuthorizationUri(array $additionalParameters = array())
42
    {
43
        $parameters = array_merge(
44
            $additionalParameters,
45
            array(
46
                'appid'         => $this->credentials->getConsumerId(),
47
                'redirect_uri'  => $this->credentials->getCallbackUrl(),
48
                'response_type' => 'code',
49
            )
50
        );
51
52
        $parameters['scope'] = implode(' ', $this->scopes);
53
54
        // Build the url
55
        $url = clone $this->getAuthorizationEndpoint();
56
        foreach ($parameters as $key => $val) {
57
            $url->addToQuery($key, $val);
58
        }
59
60
        return $url.'#wechat_redirect';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $url . '#wechat_redirect'; (string) is incompatible with the return type declared by the interface OAuth\Common\Service\Ser...ce::getAuthorizationUri of type OAuth\Common\Http\Uri\UriInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    public function getAuthorizationEndpoint()
67
    {
68
        return new Uri('https://open.weixin.qq.com/connect/oauth2/authorize');
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function getAccessTokenEndpoint()
75
    {
76
        return new Uri('https://api.weixin.qq.com/sns/oauth2/access_token');
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    protected function getAuthorizationMethod()
83
    {
84
        return static::AUTHORIZATION_METHOD_QUERY_STRING;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function requestAccessToken($code, $state = null)
91
    {
92
        if (null !== $state) {
93
            $this->validateAuthorizationState($state);
94
        }
95
96
        $bodyParams = array(
97
            'code'          => $code,
98
            'appid'         => $this->credentials->getConsumerId(),
99
            'secret'        => $this->credentials->getConsumerSecret(),
100
            'grant_type'    => 'authorization_code',
101
        );
102
103
        $responseBody = $this->httpClient->retrieveResponse(
104
            $this->getAccessTokenEndpoint(),
105
            $bodyParams,
106
            $this->getExtraOAuthHeaders()
107
        );
108
109
        $token = $this->parseAccessTokenResponse($responseBody);
110
        $this->storage->storeAccessToken($this->service(), $token);
111
112
        return $token;
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    protected function parseAccessTokenResponse($responseBody)
119
    {
120
        $data = json_decode($responseBody, true);
121
122
        if (null === $data || !is_array($data)) {
123
            throw new TokenResponseException('Unable to parse response.');
124
        } elseif (isset($data['errmsg'])) {
125
            throw new TokenResponseException('Error in retrieving token: "' . $data['error'] . '"');
126
        }
127
128
        $token = new StdOAuth2Token();
129
        $token->setAccessToken($data['access_token']);
130
131
        if (isset($data['refresh_token'])) {
132
            $token->setRefreshToken($data['refresh_token']);
133
            unset($data['refresh_token']);
134
        }
135
136
        unset($data['access_token']);
137
138
        $token->setExtraParams($data);
139
140
        return $token;
141
    }
142
}
143