CredentialResolver::getQrCodeStatus()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 5
nop 1
dl 0
loc 22
rs 9.2568
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the slince/smartqq package.
5
 *
6
 * (c) Slince <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Slince\SmartQQ;
13
14
use GuzzleHttp\Cookie\CookieJar;
15
use Slince\SmartQQ\Exception\RuntimeException;
16
17
class CredentialResolver
18
{
19
    /**
20
     * @var Client
21
     */
22
    protected $client;
23
24
    /**
25
     * @var CookieJar
26
     */
27
    protected $cookies;
28
29
    /**
30
     * 获取ptwebqq的地址
31
     *
32
     * @var string
33
     */
34
    protected $certificationUrl;
35
36
    /**
37
     * @var callable
38
     */
39
    protected $callback;
40
41
    public function __construct(Client $client)
42
    {
43
        $this->client = $client;
44
    }
45
46
    /**
47
     * 获取授权凭据.
48
     *
49
     * @param callable $callback
50
     *
51
     * @return $this
52
     */
53
    public function resolve($callback)
54
    {
55
        // 重置cookie
56
        $this->cookies = new CookieJar();
57
        $this->callback = $callback;
58
        $this->createQRSource();
59
60
        return $this;
61
    }
62
63
    /**
64
     * 等待授权验证.
65
     *
66
     * @return Credential
67
     */
68
    public function wait()
69
    {
70
        //查找"qrsig"参数
71
        $qrSign = $this->findQRSign();
72
        //计算ptqrtoken
73
        $ptQrToken = Utils::hash33($qrSign);
74
75
        //验证状态
76
        while (true) {
77
            //查看二维码状态
78
            $status = $this->getQrCodeStatus($ptQrToken);
79
80
            // 认证成功
81
            if (Request\VerifyQrCodeRequest::STATUS_CERTIFICATION === $status) {
82
                //授权成功跳出状态检查
83
                break;
84
            } elseif (Request\VerifyQrCodeRequest::STATUS_EXPIRED == $status) {
85
                $this->createQRSource();
86
                //查找"qrsig"参数
87
                $qrSign = $this->findQRSign();
88
                //计算ptqrtoken
89
                $ptQrToken = Utils::hash33($qrSign);
90
            }
91
            //暂停1秒
92
            usleep(1000000);
93
        }
94
95
        $ptWebQQ = $this->getPtWebQQ();
96
        $vfWebQQ = $this->getVfWebQQ($ptWebQQ);
97
        list($uin, $pSessionId) = $this->getUinAndPSessionId($ptWebQQ);
98
99
        $credential = new Credential(
100
            $ptWebQQ,
101
            $vfWebQQ,
102
            $pSessionId,
103
            $uin,
104
            Client::$clientId, //smartqq保留字段,固定值
105
            $this->cookies
106
        );
107
108
        return $credential;
109
    }
110
111
    protected function createQRSource()
112
    {
113
        //获取二维码资源
114
        $response = $this->sendRequest(new Request\GetQrCodeRequest());
115
        call_user_func($this->callback, (string) $response->getBody());
116
    }
117
118
    /**
119
     * 从cookie中查找 "qrsig" 参数.
120
     *
121
     * @return string
122
     */
123
    protected function findQRSign()
124
    {
125
        foreach ($this->getCookies() as $cookie) {
126
            if (0 === strcasecmp($cookie->getName(), 'qrsig')) {
127
                return $cookie->getValue();
128
            }
129
        }
130
        throw new RuntimeException('Can not find parameter [qrsig]');
131
    }
132
133
    /**
134
     * 验证二维码状态
135
     *
136
     * @param int $ptQrToken qr token
137
     *
138
     * @return int
139
     */
140
    protected function getQrCodeStatus($ptQrToken)
141
    {
142
        $request = new Request\VerifyQrCodeRequest($ptQrToken);
143
        $response = $this->sendRequest($request);
144
        if (false !== strpos($response->getBody(), '未失效')) {
145
            $status = Request\VerifyQrCodeRequest::STATUS_UNEXPIRED;
146
        } elseif (false !== strpos($response->getBody(), '已失效')) {
147
            $status = Request\VerifyQrCodeRequest::STATUS_EXPIRED;
148
        } elseif (false !== strpos($response->getBody(), '认证中')) {
149
            $status = Request\VerifyQrCodeRequest::STATUS_ACCREDITATION;
150
        } else {
151
            $status = Request\VerifyQrCodeRequest::STATUS_CERTIFICATION;
152
            //找出认证url
153
            if (preg_match("#'(http.+)'#U", strval($response->getBody()), $matches)) {
154
                $this->certificationUrl = trim($matches[1]);
155
            } else {
156
                throw new RuntimeException('Can not find certification url');
157
            }
158
        }
159
160
        return $status;
161
    }
162
163
    /**
164
     * 获取ptwebqq的参数值
165
     *
166
     * @return string
167
     */
168
    protected function getPtWebQQ()
169
    {
170
        $request = new Request\GetPtWebQQRequest();
171
        $request->setUri($this->certificationUrl);
172
        $this->sendRequest($request);
173
        foreach ($this->getCookies() as $cookie) {
174
            if (0 === strcasecmp($cookie->getName(), 'ptwebqq')) {
175
                return $cookie->getValue();
176
            }
177
        }
178
        throw new RuntimeException('Can not find parameter [ptwebqq]');
179
    }
180
181
    /**
182
     * @param string $ptWebQQ
183
     *
184
     * @return string
185
     */
186
    protected function getVfWebQQ($ptWebQQ)
187
    {
188
        $request = new Request\GetVfWebQQRequest($ptWebQQ);
189
        $response = $this->sendRequest($request);
190
191
        return Request\GetVfWebQQRequest::parseResponse($response);
192
    }
193
194
    /**
195
     * 获取pessionid和uin.
196
     *
197
     * @param string $ptWebQQ
198
     *
199
     * @return array
200
     */
201
    protected function getUinAndPSessionId($ptWebQQ)
202
    {
203
        $request = new Request\GetUinAndPsessionidRequest([
204
            'ptwebqq' => $ptWebQQ,
205
            'clientid' => Client::$clientId,
206
            'psessionid' => '',
207
            'status' => 'online',
208
        ]);
209
        $response = $this->sendRequest($request);
210
211
        return Request\GetUinAndPsessionidRequest::parseResponse($response);
212
    }
213
214
    protected function sendRequest(Request\RequestInterface $request)
215
    {
216
        return $this->client->sendRequest($request, [
217
            'cookies' => $this->cookies, //使用当前cookies
218
        ]);
219
    }
220
221
    protected function getCookies()
222
    {
223
        return $this->cookies;
224
    }
225
}
226