Test Failed
Push — master ( 5a4cf9...cb6d1a )
by Taosikai
10:40
created

CredentialResolver::createQRSource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 6
rs 10
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
    /**
21
     * @var Client
22
     */
23
    protected $client;
24
25
    /**
26
     * @var CookieJar
27
     */
28
    protected $cookies;
29
30
    /**
31
     * 获取ptwebqq的地址
32
     *
33
     * @var string
34
     */
35
    protected $certificationUrl;
36
37
    /**
38
     * @var callable
39
     */
40
    protected $callback;
41
42
    public function __construct(Client $client)
43
    {
44
        $this->client = $client;
45
    }
46
47
    /**
48
     * 获取授权凭据.
49
     *
50
     * @param callable $callback
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
        return $this;
60
    }
61
62
    /**
63
     * 等待授权验证.
64
     *
65
     * @return Credential
66
     */
67
    public function wait()
68
    {
69
        //查找"qrsig"参数
70
        $qrSign = $this->findQRSign();
71
        //计算ptqrtoken
72
        $ptQrToken = Utils::hash33($qrSign);
73
74
        //验证状态
75
        while (true) {
76
            //查看二维码状态
77
            $status = $this->getQrCodeStatus($ptQrToken);
78
79
            // 认证成功
80
            if (Request\VerifyQrCodeRequest::STATUS_CERTIFICATION === $status) {
81
                //授权成功跳出状态检查
82
                break;
83
            } elseif (Request\VerifyQrCodeRequest::STATUS_EXPIRED == $status) {
84
                $this->createQRSource();
85
                //查找"qrsig"参数
86
                $qrSign = $this->findQRSign();
87
                //计算ptqrtoken
88
                $ptQrToken = Utils::hash33($qrSign);
89
            }
90
            //暂停1秒
91
            usleep(1000000);
92
        }
93
94
        $ptWebQQ = $this->getPtWebQQ();
95
        $vfWebQQ = $this->getVfWebQQ($ptWebQQ);
96
        list($uin, $pSessionId) = $this->getUinAndPSessionId($ptWebQQ);
97
98
        $credential = new Credential(
99
            $ptWebQQ,
100
            $vfWebQQ,
101
            $pSessionId,
102
            $uin,
103
            Client::$clientId, //smartqq保留字段,固定值
0 ignored issues
show
Bug introduced by
The property clientId cannot be accessed from this context as it is declared private in class Slince\SmartQQ\Client.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
104
            $this->cookies
105
        );
106
107
        return $credential;
108
    }
109
110
    protected function createQRSource()
111
    {
112
        //获取二维码资源
113
        $response = $this->sendRequest(new Request\GetQrCodeRequest());
114
        call_user_func($this->callback, (string)$response->getBody());
115
    }
116
117
    /**
118
     * 从cookie中查找 "qrsig" 参数
119
     *
120
     * @return string
121
     */
122
    protected function findQRSign()
123
    {
124
        foreach ($this->getCookies() as $cookie) {
125
            if (0 === strcasecmp($cookie->getName(), 'qrsig')) {
126
                return $cookie->getValue();
127
            }
128
        }
129
        throw new RuntimeException('Can not find parameter [qrsig]');
130
    }
131
132
    /**
133
     * 验证二维码状态
134
     *
135
     * @param int $ptQrToken qr token
136
     *
137
     * @return int
138
     */
139
    protected function getQrCodeStatus($ptQrToken)
140
    {
141
        $request = new Request\VerifyQrCodeRequest($ptQrToken);
142
        $response = $this->sendRequest($request);
143
        if (false !== strpos($response->getBody(), '未失效')) {
144
            $status = Request\VerifyQrCodeRequest::STATUS_UNEXPIRED;
145
        } elseif (false !== strpos($response->getBody(), '已失效')) {
146
            $status = Request\VerifyQrCodeRequest::STATUS_EXPIRED;
147
        } elseif (false !== strpos($response->getBody(), '认证中')) {
148
            $status = Request\VerifyQrCodeRequest::STATUS_ACCREDITATION;
149
        } else {
150
            $status = Request\VerifyQrCodeRequest::STATUS_CERTIFICATION;
151
            //找出认证url
152
            if (preg_match("#'(http.+)'#U", strval($response->getBody()), $matches)) {
153
                $this->certificationUrl = trim($matches[1]);
154
            } else {
155
                throw new RuntimeException('Can not find certification url');
156
            }
157
        }
158
159
        return $status;
160
    }
161
162
    /**
163
     * 获取ptwebqq的参数值
164
     *
165
     * @return string
166
     */
167
    protected function getPtWebQQ()
168
    {
169
        $request = new Request\GetPtWebQQRequest();
170
        $request->setUri($this->certificationUrl);
171
        $this->sendRequest($request);
172
        foreach ($this->getCookies() as $cookie) {
173
            if (0 === strcasecmp($cookie->getName(), 'ptwebqq')) {
174
                return $cookie->getValue();
175
            }
176
        }
177
        throw new RuntimeException('Can not find parameter [ptwebqq]');
178
    }
179
180
    /**
181
     * @param string $ptWebQQ
182
     *
183
     * @return string
184
     */
185
    protected function getVfWebQQ($ptWebQQ)
186
    {
187
        $request = new Request\GetVfWebQQRequest($ptWebQQ);
188
        $response = $this->sendRequest($request);
189
190
        return Request\GetVfWebQQRequest::parseResponse($response);
191
    }
192
193
    /**
194
     * 获取pessionid和uin.
195
     *
196
     * @param string $ptWebQQ
197
     *
198
     * @return array
199
     */
200
    protected function getUinAndPSessionId($ptWebQQ)
201
    {
202
        $request = new Request\GetUinAndPsessionidRequest([
203
            'ptwebqq' => $ptWebQQ,
204
            'clientid' => Client::$clientId,
0 ignored issues
show
Bug introduced by
The property clientId cannot be accessed from this context as it is declared private in class Slince\SmartQQ\Client.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
205
            'psessionid' => '',
206
            'status' => 'online',
207
        ]);
208
        $response = $this->sendRequest($request);
209
210
        return Request\GetUinAndPsessionidRequest::parseResponse($response);
211
    }
212
213
    protected function sendRequest(Request\RequestInterface $request)
214
    {
215
        return $this->client->sendRequest($request, [
216
            'cookies' => $this->cookies //使用当前cookies
217
        ]);
218
    }
219
220
    protected function getCookies()
221
    {
222
        return $this->cookies;
223
    }
224
}