|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Rucaptcha; |
|
4
|
|
|
|
|
5
|
|
|
use GuzzleHttp\Exception\GuzzleException; |
|
6
|
|
|
use GuzzleHttp\RequestOptions; |
|
7
|
|
|
use Rucaptcha\Exception\ErrorResponseException; |
|
8
|
|
|
use Rucaptcha\Exception\InvalidArgumentException; |
|
9
|
|
|
use Rucaptcha\Exception\RuntimeException; |
|
10
|
|
|
use SimpleXMLElement; |
|
11
|
|
|
|
|
12
|
|
|
/** |
|
13
|
|
|
* Class Client |
|
14
|
|
|
* |
|
15
|
|
|
* @package Rucaptcha |
|
16
|
|
|
* @author Dmitry Gladyshev <[email protected]> |
|
17
|
|
|
*/ |
|
18
|
|
|
class Client extends GenericClient |
|
19
|
|
|
{ |
|
20
|
|
|
/* json status codes */ |
|
21
|
|
|
const STATUS_CODE_CAPCHA_NOT_READY = 0; |
|
22
|
|
|
const STATUS_CODE_OK = 1; |
|
23
|
|
|
|
|
24
|
|
|
/* status codes */ |
|
25
|
|
|
const STATUS_OK_REPORT_RECORDED = 'OK_REPORT_RECORDED'; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* @var int |
|
29
|
|
|
*/ |
|
30
|
|
|
protected $recaptchaRTimeout = 15; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* @var string |
|
34
|
|
|
*/ |
|
35
|
|
|
protected $serverBaseUri = 'http://rucaptcha.com'; |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* Your application ID in Rucaptcha catalog. |
|
39
|
|
|
* The value `1013` is ID of this library. Set in false if you want to turn off sending any ID. |
|
40
|
|
|
* |
|
41
|
|
|
* @see https://rucaptcha.com/software/view/php-api-client |
|
42
|
|
|
* @var string |
|
43
|
|
|
*/ |
|
44
|
|
|
protected $softId = '1013'; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @inheritdoc |
|
48
|
|
|
*/ |
|
49
|
|
|
public function sendCaptcha($content, array $extra = []) |
|
50
|
|
|
{ |
|
51
|
|
|
if ($this->softId && !isset($extra[Extra::SOFT_ID])) { |
|
52
|
|
|
$extra[Extra::SOFT_ID] = $this->softId; |
|
53
|
|
|
} |
|
54
|
|
|
return parent::sendCaptcha($content, $extra); |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Bulk captcha result. |
|
59
|
|
|
* |
|
60
|
|
|
* @param int[] $captchaIds # Captcha task Ids array |
|
61
|
|
|
* @return string[] # Array $captchaId => $captchaText or false if is not ready |
|
62
|
|
|
* @throws GuzzleException |
|
63
|
|
|
*/ |
|
64
|
|
|
public function getCaptchaResultBulk(array $captchaIds) |
|
65
|
|
|
{ |
|
66
|
|
|
$response = $this->getHttpClient()->request('GET', '/res.php?' . http_build_query([ |
|
67
|
|
|
'key' => $this->apiKey, |
|
68
|
|
|
'action' => 'get', |
|
69
|
|
|
'ids' => join(',', $captchaIds) |
|
70
|
|
|
])); |
|
71
|
|
|
|
|
72
|
|
|
$captchaTexts = $response->getBody()->__toString(); |
|
73
|
|
|
|
|
74
|
|
|
$this->getLogger()->info("Got bulk response: `{$captchaTexts}`."); |
|
75
|
|
|
|
|
76
|
|
|
$captchaTexts = explode("|", $captchaTexts); |
|
77
|
|
|
|
|
78
|
|
|
$result = []; |
|
79
|
|
|
|
|
80
|
|
|
foreach ($captchaTexts as $index => $captchaText) { |
|
81
|
|
|
$captchaText = html_entity_decode(trim($captchaText)); |
|
82
|
|
|
$result[$captchaIds[$index]] = |
|
83
|
|
|
($captchaText == self::STATUS_CAPTCHA_NOT_READY) ? false : $captchaText; |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
return $result; |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
|
|
/** |
|
90
|
|
|
* Returns balance of account. |
|
91
|
|
|
* |
|
92
|
|
|
* @return string |
|
93
|
|
|
* @throws GuzzleException |
|
94
|
|
|
*/ |
|
95
|
|
|
public function getBalance() |
|
96
|
|
|
{ |
|
97
|
|
|
$response = $this |
|
98
|
|
|
->getHttpClient() |
|
99
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=getbalance"); |
|
100
|
|
|
|
|
101
|
|
|
return $response->getBody()->__toString(); |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* Alias of $this->reportBad(); |
|
106
|
|
|
* |
|
107
|
|
|
* @param string $captchaId |
|
108
|
|
|
* @return bool |
|
109
|
|
|
* @throws ErrorResponseException |
|
110
|
|
|
* @throws GuzzleException |
|
111
|
|
|
*/ |
|
112
|
|
|
public function badCaptcha($captchaId) |
|
113
|
|
|
{ |
|
114
|
|
|
return $this->reportBad($captchaId); |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
/** |
|
118
|
|
|
* Alias of $this->reportGood(); |
|
119
|
|
|
* |
|
120
|
|
|
* @param string $captchaId |
|
121
|
|
|
* @return bool |
|
122
|
|
|
* @throws ErrorResponseException |
|
123
|
|
|
* @throws GuzzleException |
|
124
|
|
|
*/ |
|
125
|
|
|
public function goodCaptcha($captchaId) |
|
126
|
|
|
{ |
|
127
|
|
|
return $this->reportGood($captchaId); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* Report of wrong recognition. |
|
132
|
|
|
* |
|
133
|
|
|
* @param string $captchaId |
|
134
|
|
|
* @return bool |
|
135
|
|
|
* @throws ErrorResponseException |
|
136
|
|
|
* @throws GuzzleException |
|
137
|
|
|
*/ |
|
138
|
|
|
public function reportBad($captchaId) |
|
139
|
|
|
{ |
|
140
|
|
|
$response = $this |
|
141
|
|
|
->getHttpClient() |
|
142
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=reportbad&id={$captchaId}"); |
|
143
|
|
|
|
|
144
|
|
|
$responseText = $response->getBody()->__toString(); |
|
145
|
|
|
|
|
146
|
|
|
if ($responseText === self::STATUS_OK_REPORT_RECORDED) { |
|
147
|
|
|
return true; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
throw new ErrorResponseException( |
|
151
|
|
|
$this->getErrorMessage($responseText) ?: $responseText, |
|
152
|
|
|
$this->getErrorCode($responseText) ?: 0 |
|
153
|
|
|
); |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
|
|
157
|
|
|
/** |
|
158
|
|
|
* Reports rucaptcha for good recognition. |
|
159
|
|
|
* |
|
160
|
|
|
* @param $captchaId |
|
161
|
|
|
* @return bool |
|
162
|
|
|
* @throws ErrorResponseException |
|
163
|
|
|
* @throws GuzzleException |
|
164
|
|
|
*/ |
|
165
|
|
|
public function reportGood($captchaId) |
|
166
|
|
|
{ |
|
167
|
|
|
$response = $this |
|
168
|
|
|
->getHttpClient() |
|
169
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=reportgood&id={$captchaId}"); |
|
170
|
|
|
|
|
171
|
|
|
$responseText = $response->getBody()->__toString(); |
|
172
|
|
|
|
|
173
|
|
|
if ($responseText === self::STATUS_OK_REPORT_RECORDED) { |
|
174
|
|
|
return true; |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
throw new ErrorResponseException( |
|
178
|
|
|
$this->getErrorMessage($responseText) ?: $responseText, |
|
179
|
|
|
$this->getErrorCode($responseText) ?: 0 |
|
180
|
|
|
); |
|
181
|
|
|
} |
|
182
|
|
|
|
|
183
|
|
|
/** |
|
184
|
|
|
* Returns server health data. |
|
185
|
|
|
* |
|
186
|
|
|
* @param string|string[] $paramsList # List of metrics to be returned |
|
187
|
|
|
* @return string[]|string # Array of load metrics $metric => $value formatted |
|
188
|
|
|
* @throws GuzzleException |
|
189
|
|
|
*/ |
|
190
|
|
|
public function getLoad($paramsList = ['waiting', 'load', 'minbid', 'averageRecognitionTime']) |
|
191
|
|
|
{ |
|
192
|
|
|
$parser = $this->getLoadXml(); |
|
193
|
|
|
|
|
194
|
|
|
if (is_string($paramsList)) { |
|
195
|
|
|
return $parser->$paramsList->__toString(); |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
$statusData = []; |
|
199
|
|
|
|
|
200
|
|
|
foreach ($paramsList as $item) { |
|
201
|
|
|
$statusData[$item] = $parser->$item->__toString(); |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
return $statusData; |
|
|
|
|
|
|
205
|
|
|
} |
|
206
|
|
|
|
|
207
|
|
|
/** |
|
208
|
|
|
* Returns load data as XML. |
|
209
|
|
|
* |
|
210
|
|
|
* @return SimpleXMLElement |
|
211
|
|
|
* @throws GuzzleException |
|
212
|
|
|
*/ |
|
213
|
|
|
public function getLoadXml() |
|
214
|
|
|
{ |
|
215
|
|
|
$response = $this |
|
216
|
|
|
->getHttpClient() |
|
217
|
|
|
->request('GET', "/load.php"); |
|
218
|
|
|
|
|
219
|
|
|
return new SimpleXMLElement($response->getBody()->__toString()); |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
/** |
|
223
|
|
|
* @param string $captchaId # Captcha task ID |
|
224
|
|
|
* @return array | false # Solved captcha and cost array or false if captcha is not ready |
|
225
|
|
|
* @throws ErrorResponseException |
|
226
|
|
|
* @throws GuzzleException |
|
227
|
|
|
*/ |
|
228
|
|
|
public function getCaptchaResultWithCost($captchaId) |
|
229
|
|
|
{ |
|
230
|
|
|
$response = $this |
|
231
|
|
|
->getHttpClient() |
|
232
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=get2&id={$captchaId}"); |
|
233
|
|
|
|
|
234
|
|
|
$responseText = $response->getBody()->__toString(); |
|
235
|
|
|
|
|
236
|
|
|
if ($responseText === self::STATUS_CAPTCHA_NOT_READY) { |
|
237
|
|
|
return false; |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
if (strpos($responseText, 'OK|') !== false) { |
|
241
|
|
|
$this->getLogger()->info("Got OK response: `{$responseText}`."); |
|
242
|
|
|
$data = explode('|', $responseText); |
|
243
|
|
|
return [ |
|
244
|
|
|
'captcha' => html_entity_decode(trim($data[1])), |
|
245
|
|
|
'cost' => html_entity_decode(trim($data[2])), |
|
246
|
|
|
]; |
|
247
|
|
|
} |
|
248
|
|
|
|
|
249
|
|
|
throw new ErrorResponseException( |
|
250
|
|
|
$this->getErrorMessage($responseText) ?: $responseText, |
|
251
|
|
|
$this->getErrorCode($responseText) ?: 0 |
|
252
|
|
|
); |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
/** |
|
256
|
|
|
* Add pingback url to rucaptcha whitelist. |
|
257
|
|
|
* |
|
258
|
|
|
* @param string $url |
|
259
|
|
|
* @return bool # true if added and exception if fail |
|
260
|
|
|
* @throws ErrorResponseException |
|
261
|
|
|
* @throws GuzzleException |
|
262
|
|
|
*/ |
|
263
|
|
|
public function addPingback($url) |
|
264
|
|
|
{ |
|
265
|
|
|
$response = $this |
|
266
|
|
|
->getHttpClient() |
|
267
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=add_pingback&addr={$url}"); |
|
268
|
|
|
|
|
269
|
|
|
$responseText = $response->getBody()->__toString(); |
|
270
|
|
|
|
|
271
|
|
|
if ($responseText === self::STATUS_OK) { |
|
272
|
|
|
return true; |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
throw new ErrorResponseException( |
|
276
|
|
|
$this->getErrorMessage($responseText) ?: $responseText, |
|
277
|
|
|
$this->getErrorCode($responseText) ?: 0 |
|
278
|
|
|
); |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
/** |
|
282
|
|
|
* Returns pingback whitelist items. |
|
283
|
|
|
* |
|
284
|
|
|
* @return string[] # List of urls |
|
285
|
|
|
* @throws ErrorResponseException |
|
286
|
|
|
* @throws GuzzleException |
|
287
|
|
|
*/ |
|
288
|
|
|
public function getPingbacks() |
|
289
|
|
|
{ |
|
290
|
|
|
$response = $this |
|
291
|
|
|
->getHttpClient() |
|
292
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=get_pingback"); |
|
293
|
|
|
|
|
294
|
|
|
$responseText = $response->getBody()->__toString(); |
|
295
|
|
|
|
|
296
|
|
|
if (strpos($responseText, 'OK|') !== false) { |
|
297
|
|
|
$data = explode('|', $responseText); |
|
298
|
|
|
unset($data[0]); |
|
299
|
|
|
return empty($data[1]) ? [] : array_values($data); |
|
300
|
|
|
} |
|
301
|
|
|
|
|
302
|
|
|
throw new ErrorResponseException( |
|
303
|
|
|
$this->getErrorMessage($responseText) ?: $responseText, |
|
304
|
|
|
$this->getErrorCode($responseText) ?: 0 |
|
305
|
|
|
); |
|
306
|
|
|
} |
|
307
|
|
|
|
|
308
|
|
|
/** |
|
309
|
|
|
* Remove pingback url from whitelist. |
|
310
|
|
|
* |
|
311
|
|
|
* @param string $uri |
|
312
|
|
|
* @return bool |
|
313
|
|
|
* @throws ErrorResponseException |
|
314
|
|
|
* @throws GuzzleException |
|
315
|
|
|
*/ |
|
316
|
|
|
public function deletePingback($uri) |
|
317
|
|
|
{ |
|
318
|
|
|
$response = $this |
|
319
|
|
|
->getHttpClient() |
|
320
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=del_pingback&addr={$uri}"); |
|
321
|
|
|
|
|
322
|
|
|
$responseText = $response->getBody()->__toString(); |
|
323
|
|
|
|
|
324
|
|
|
if ($responseText === self::STATUS_OK) { |
|
325
|
|
|
return true; |
|
326
|
|
|
} |
|
327
|
|
|
throw new ErrorResponseException( |
|
328
|
|
|
$this->getErrorMessage($responseText) ?: $responseText, |
|
329
|
|
|
$this->getErrorCode($responseText) ?: 0 |
|
330
|
|
|
); |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
/** |
|
334
|
|
|
* Truncate pingback whitelist. |
|
335
|
|
|
* |
|
336
|
|
|
* @return bool |
|
337
|
|
|
* @throws ErrorResponseException |
|
338
|
|
|
* @throws GuzzleException |
|
339
|
|
|
*/ |
|
340
|
|
|
public function deleteAllPingbacks() |
|
341
|
|
|
{ |
|
342
|
|
|
return $this->deletePingback('all'); |
|
343
|
|
|
} |
|
344
|
|
|
|
|
345
|
|
|
/* Recaptcha v2 */ |
|
346
|
|
|
|
|
347
|
|
|
/** |
|
348
|
|
|
* Sent recaptcha v2 |
|
349
|
|
|
* |
|
350
|
|
|
* @param string $googleKey |
|
351
|
|
|
* @param string $pageUrl |
|
352
|
|
|
* @param array $extra |
|
353
|
|
|
* @return string |
|
354
|
|
|
* @throws ErrorResponseException |
|
355
|
|
|
* @throws GuzzleException |
|
356
|
|
|
*/ |
|
357
|
|
|
public function sendRecaptchaV2($googleKey, $pageUrl, $extra = []) |
|
358
|
|
|
{ |
|
359
|
|
|
$this->getLogger()->info("Try send google key (recaptcha) on {$this->serverBaseUri}/in.php"); |
|
360
|
|
|
|
|
361
|
|
|
if ($this->softId && !isset($extra[Extra::SOFT_ID])) { |
|
362
|
|
|
$extra[Extra::SOFT_ID] = $this->softId; |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
|
|
$response = $this->getHttpClient()->request('POST', "/in.php", [ |
|
366
|
|
|
RequestOptions::QUERY => array_merge($extra, [ |
|
367
|
|
|
'method' => 'userrecaptcha', |
|
368
|
|
|
'key' => $this->apiKey, |
|
369
|
|
|
'googlekey' => $googleKey, |
|
370
|
|
|
'pageurl' => $pageUrl |
|
371
|
|
|
]) |
|
372
|
|
|
]); |
|
373
|
|
|
|
|
374
|
|
|
$responseText = $response->getBody()->__toString(); |
|
375
|
|
|
|
|
376
|
|
|
if (strpos($responseText, 'OK|') !== false) { |
|
377
|
|
|
$this->lastCaptchaId = explode("|", $responseText)[1]; |
|
378
|
|
|
$this->getLogger()->info("Sending success. Got captcha id `{$this->lastCaptchaId}`."); |
|
379
|
|
|
return $this->lastCaptchaId; |
|
380
|
|
|
} |
|
381
|
|
|
|
|
382
|
|
|
throw new ErrorResponseException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`."); |
|
383
|
|
|
} |
|
384
|
|
|
|
|
385
|
|
|
/** |
|
386
|
|
|
* Alias for bc |
|
387
|
|
|
* @param $googleKey |
|
388
|
|
|
* @param $pageUrl |
|
389
|
|
|
* @param array $extra |
|
390
|
|
|
* @return string |
|
391
|
|
|
* @throws ErrorResponseException |
|
392
|
|
|
* @throws GuzzleException |
|
393
|
|
|
* @deprecated |
|
394
|
|
|
*/ |
|
395
|
|
|
public function sendRecapthaV2($googleKey, $pageUrl, $extra = []) |
|
396
|
|
|
{ |
|
397
|
|
|
return $this->sendRecaptchaV2($googleKey, $pageUrl, $extra); |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
|
|
/** |
|
401
|
|
|
* Recaptcha V2 recognition. |
|
402
|
|
|
* |
|
403
|
|
|
* @param string $googleKey |
|
404
|
|
|
* @param string $pageUrl |
|
405
|
|
|
* @param array $extra # Captcha options |
|
406
|
|
|
* @return string # Code to place in hidden form |
|
407
|
|
|
* @throws ErrorResponseException |
|
408
|
|
|
* @throws InvalidArgumentException |
|
409
|
|
|
* @throws RuntimeException |
|
410
|
|
|
* @throws GuzzleException |
|
411
|
|
|
*/ |
|
412
|
|
|
public function recognizeRecaptchaV2($googleKey, $pageUrl, $extra = []) |
|
413
|
|
|
{ |
|
414
|
|
|
$captchaId = $this->sendRecaptchaV2($googleKey, $pageUrl, $extra); |
|
415
|
|
|
$startTime = time(); |
|
416
|
|
|
|
|
417
|
|
|
while (true) { |
|
418
|
|
|
$this->getLogger()->info("Waiting {$this->rTimeout} sec."); |
|
419
|
|
|
|
|
420
|
|
|
sleep($this->recaptchaRTimeout); |
|
421
|
|
|
|
|
422
|
|
|
if (time() - $startTime >= $this->mTimeout) { |
|
423
|
|
|
throw new RuntimeException("Captcha waiting timeout."); |
|
424
|
|
|
} |
|
425
|
|
|
|
|
426
|
|
|
$result = $this->getCaptchaResult($captchaId); |
|
427
|
|
|
|
|
428
|
|
|
if ($result === false) { |
|
429
|
|
|
continue; |
|
430
|
|
|
} |
|
431
|
|
|
|
|
432
|
|
|
$this->getLogger()->info("Elapsed " . (time()-$startTime) . " second(s)."); |
|
433
|
|
|
|
|
434
|
|
|
return $result; |
|
435
|
|
|
} |
|
436
|
|
|
|
|
437
|
|
|
throw new RuntimeException('Unknown recognition logic error.'); |
|
438
|
|
|
} |
|
439
|
|
|
|
|
440
|
|
|
/* Recaptcha v3 */ |
|
441
|
|
|
|
|
442
|
|
|
/** |
|
443
|
|
|
* @param string $googleKey |
|
444
|
|
|
* @param string $pageUrl |
|
445
|
|
|
* @param string $action |
|
446
|
|
|
* @param string $minScore |
|
447
|
|
|
* @param array $extra |
|
448
|
|
|
* @return string |
|
449
|
|
|
* @throws ErrorResponseException |
|
450
|
|
|
* @throws GuzzleException |
|
451
|
|
|
* @see https://rucaptcha.com/blog/for_webmaster/recaptcha-v3-obhod |
|
452
|
|
|
*/ |
|
453
|
|
|
public function sendRecaptchaV3($googleKey, $pageUrl, $action, $minScore = '0.3', $extra = []) |
|
454
|
|
|
{ |
|
455
|
|
|
$this->getLogger()->info("Try send google key (recaptcha v3) on {$this->serverBaseUri}/in.php"); |
|
456
|
|
|
|
|
457
|
|
|
if ($this->softId && !isset($extra[Extra::SOFT_ID])) { |
|
458
|
|
|
$extra[Extra::SOFT_ID] = $this->softId; |
|
459
|
|
|
} |
|
460
|
|
|
|
|
461
|
|
|
$response = $this->getHttpClient()->request('POST', "/in.php", [ |
|
462
|
|
|
RequestOptions::QUERY => array_merge($extra, [ |
|
463
|
|
|
'method' => 'userrecaptcha', |
|
464
|
|
|
'version' => 'v3', |
|
465
|
|
|
'key' => $this->apiKey, |
|
466
|
|
|
'googlekey' => $googleKey, |
|
467
|
|
|
'pageurl' => $pageUrl, |
|
468
|
|
|
'action' => $action, |
|
469
|
|
|
'min_score' => $minScore |
|
470
|
|
|
]) |
|
471
|
|
|
]); |
|
472
|
|
|
|
|
473
|
|
|
$responseText = $response->getBody()->__toString(); |
|
474
|
|
|
|
|
475
|
|
|
if (strpos($responseText, 'OK|') !== false) { |
|
476
|
|
|
$this->lastCaptchaId = explode("|", $responseText)[1]; |
|
477
|
|
|
$this->getLogger()->info("Sending success. Got captcha id `{$this->lastCaptchaId}`."); |
|
478
|
|
|
return $this->lastCaptchaId; |
|
479
|
|
|
} |
|
480
|
|
|
|
|
481
|
|
|
throw new ErrorResponseException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`."); |
|
482
|
|
|
} |
|
483
|
|
|
|
|
484
|
|
|
/** |
|
485
|
|
|
* @param string $googleKey |
|
486
|
|
|
* @param string $pageUrl |
|
487
|
|
|
* @param string $action |
|
488
|
|
|
* @param string $minScore |
|
489
|
|
|
* @param array $extra |
|
490
|
|
|
* @return false|string |
|
491
|
|
|
* @throws ErrorResponseException |
|
492
|
|
|
* @throws InvalidArgumentException |
|
493
|
|
|
* @throws RuntimeException |
|
494
|
|
|
* @throws GuzzleException |
|
495
|
|
|
*/ |
|
496
|
|
|
public function recognizeRecaptchaV3($googleKey, $pageUrl, $action, $minScore = '0.3', $extra = []) |
|
497
|
|
|
{ |
|
498
|
|
|
$captchaId = $this->sendRecaptchaV3($googleKey, $pageUrl, $action, $minScore, $extra); |
|
499
|
|
|
$startTime = time(); |
|
500
|
|
|
|
|
501
|
|
|
while (true) { |
|
502
|
|
|
$this->getLogger()->info("Waiting {$this->rTimeout} sec."); |
|
503
|
|
|
|
|
504
|
|
|
sleep($this->recaptchaRTimeout); |
|
505
|
|
|
|
|
506
|
|
|
if (time() - $startTime >= $this->mTimeout) { |
|
507
|
|
|
throw new RuntimeException("Captcha waiting timeout."); |
|
508
|
|
|
} |
|
509
|
|
|
|
|
510
|
|
|
$result = $this->getCaptchaResult($captchaId); |
|
511
|
|
|
|
|
512
|
|
|
if ($result === false) { |
|
513
|
|
|
continue; |
|
514
|
|
|
} |
|
515
|
|
|
|
|
516
|
|
|
$this->getLogger()->info("Elapsed " . (time()-$startTime) . " second(s)."); |
|
517
|
|
|
|
|
518
|
|
|
return $result; |
|
519
|
|
|
} |
|
520
|
|
|
|
|
521
|
|
|
throw new RuntimeException('Unknown recognition logic error.'); |
|
522
|
|
|
} |
|
523
|
|
|
|
|
524
|
|
|
/** |
|
525
|
|
|
* Keycaptcha recognition. |
|
526
|
|
|
* |
|
527
|
|
|
* @param string $SSCUserId |
|
528
|
|
|
* @param string $SSCSessionId |
|
529
|
|
|
* @param string $SSCWebServerSign |
|
530
|
|
|
* @param string $SSCWebServerSign2 |
|
531
|
|
|
* @param string $pageUrl |
|
532
|
|
|
* @param array $extra |
|
533
|
|
|
* @return string # Captcha ID |
|
534
|
|
|
* @throws ErrorResponseException |
|
535
|
|
|
* @throws GuzzleException |
|
536
|
|
|
*/ |
|
537
|
|
|
public function sendKeyCaptcha( |
|
538
|
|
|
$SSCUserId, |
|
539
|
|
|
$SSCSessionId, |
|
540
|
|
|
$SSCWebServerSign, |
|
541
|
|
|
$SSCWebServerSign2, |
|
542
|
|
|
$pageUrl, |
|
543
|
|
|
$extra = [] |
|
544
|
|
|
) { |
|
545
|
|
|
|
|
546
|
|
|
$this->getLogger()->info("Try send google key (recaptcha) on {$this->serverBaseUri}/in.php"); |
|
547
|
|
|
|
|
548
|
|
|
if ($this->softId && !isset($extra[Extra::SOFT_ID])) { |
|
549
|
|
|
$extra[Extra::SOFT_ID] = $this->softId; |
|
550
|
|
|
} |
|
551
|
|
|
|
|
552
|
|
|
$response = $this->getHttpClient()->request('POST', "/in.php", [ |
|
553
|
|
|
RequestOptions::QUERY => array_merge($extra, [ |
|
554
|
|
|
'method' => 'keycaptcha', |
|
555
|
|
|
'key' => $this->apiKey, |
|
556
|
|
|
's_s_c_user_id' => $SSCUserId, |
|
557
|
|
|
's_s_c_session_id' => $SSCSessionId, |
|
558
|
|
|
's_s_c_web_server_sign' => $SSCWebServerSign, |
|
559
|
|
|
's_s_c_web_server_sign2' => $SSCWebServerSign2, |
|
560
|
|
|
'pageurl' => $pageUrl |
|
561
|
|
|
]) |
|
562
|
|
|
]); |
|
563
|
|
|
|
|
564
|
|
|
$responseText = $response->getBody()->__toString(); |
|
565
|
|
|
|
|
566
|
|
|
if (strpos($responseText, 'OK|') !== false) { |
|
567
|
|
|
$this->lastCaptchaId = explode("|", $responseText)[1]; |
|
568
|
|
|
$this->getLogger()->info("Sending success. Got captcha id `{$this->lastCaptchaId}`."); |
|
569
|
|
|
return $this->lastCaptchaId; |
|
570
|
|
|
} |
|
571
|
|
|
|
|
572
|
|
|
throw new ErrorResponseException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`."); |
|
573
|
|
|
} |
|
574
|
|
|
|
|
575
|
|
|
/** |
|
576
|
|
|
* Keycaptcha recognition. |
|
577
|
|
|
* |
|
578
|
|
|
* @param string $SSCUserId |
|
579
|
|
|
* @param string $SSCSessionId |
|
580
|
|
|
* @param string $SSCWebServerSign |
|
581
|
|
|
* @param string $SSCWebServerSign2 |
|
582
|
|
|
* @param string $pageUrl |
|
583
|
|
|
* @param array $extra |
|
584
|
|
|
* @return string # Code to place into id="capcode" input value |
|
585
|
|
|
* @throws ErrorResponseException |
|
586
|
|
|
* @throws InvalidArgumentException |
|
587
|
|
|
* @throws RuntimeException |
|
588
|
|
|
* @throws GuzzleException |
|
589
|
|
|
*/ |
|
590
|
|
|
public function recognizeKeyCaptcha( |
|
591
|
|
|
$SSCUserId, |
|
592
|
|
|
$SSCSessionId, |
|
593
|
|
|
$SSCWebServerSign, |
|
594
|
|
|
$SSCWebServerSign2, |
|
595
|
|
|
$pageUrl, |
|
596
|
|
|
$extra = [] |
|
597
|
|
|
) { |
|
598
|
|
|
$captchaId = $this |
|
599
|
|
|
->sendKeyCaptcha($SSCUserId, $SSCSessionId, $SSCWebServerSign, $SSCWebServerSign2, $pageUrl, $extra); |
|
600
|
|
|
|
|
601
|
|
|
$startTime = time(); |
|
602
|
|
|
|
|
603
|
|
|
while (true) { |
|
604
|
|
|
$this->getLogger()->info("Waiting {$this->rTimeout} sec."); |
|
605
|
|
|
|
|
606
|
|
|
sleep($this->recaptchaRTimeout); |
|
607
|
|
|
|
|
608
|
|
|
if (time() - $startTime >= $this->mTimeout) { |
|
609
|
|
|
throw new RuntimeException("Captcha waiting timeout."); |
|
610
|
|
|
} |
|
611
|
|
|
|
|
612
|
|
|
$result = $this->getCaptchaResult($captchaId); |
|
613
|
|
|
|
|
614
|
|
|
if ($result === false) { |
|
615
|
|
|
continue; |
|
616
|
|
|
} |
|
617
|
|
|
|
|
618
|
|
|
$this->getLogger()->info("Elapsed " . (time()-$startTime) . " second(s)."); |
|
619
|
|
|
|
|
620
|
|
|
return $result; |
|
621
|
|
|
} |
|
622
|
|
|
|
|
623
|
|
|
throw new RuntimeException('Unknown recognition logic error.'); |
|
624
|
|
|
} |
|
625
|
|
|
|
|
626
|
|
|
/** |
|
627
|
|
|
* Override generic method for using json response. |
|
628
|
|
|
* |
|
629
|
|
|
* @param string $captchaId # Captcha task ID |
|
630
|
|
|
* @return false|string # Solved captcha text or false if captcha is not ready |
|
631
|
|
|
* @throws ErrorResponseException |
|
632
|
|
|
* @throws InvalidArgumentException |
|
633
|
|
|
* @throws GuzzleException |
|
634
|
|
|
*/ |
|
635
|
|
|
public function getCaptchaResult($captchaId) |
|
636
|
|
|
{ |
|
637
|
|
|
$response = $this |
|
638
|
|
|
->getHttpClient() |
|
639
|
|
|
->request('GET', "/res.php?key={$this->apiKey}&action=get&id={$captchaId}&json=1"); |
|
640
|
|
|
|
|
641
|
|
|
$responseData = json_decode($response->getBody()->__toString(), true); |
|
642
|
|
|
|
|
643
|
|
|
if (JSON_ERROR_NONE !== json_last_error()) { |
|
644
|
|
|
throw new InvalidArgumentException( |
|
645
|
|
|
'json_decode error: ' . json_last_error_msg() |
|
646
|
|
|
); |
|
647
|
|
|
} |
|
648
|
|
|
|
|
649
|
|
|
if ($responseData['status'] === self::STATUS_CODE_CAPCHA_NOT_READY) { |
|
650
|
|
|
return false; |
|
651
|
|
|
} |
|
652
|
|
|
|
|
653
|
|
|
if ($responseData['status'] === self::STATUS_CODE_OK) { |
|
654
|
|
|
$this->getLogger()->info("Got OK response: `{$responseData['request']}`."); |
|
655
|
|
|
return $responseData['request']; |
|
656
|
|
|
} |
|
657
|
|
|
|
|
658
|
|
|
throw new ErrorResponseException( |
|
659
|
|
|
$this->getErrorMessage( |
|
660
|
|
|
$responseData['request'] |
|
661
|
|
|
) ?: $responseData['request'], |
|
662
|
|
|
$responseData['status'] |
|
663
|
|
|
); |
|
664
|
|
|
} |
|
665
|
|
|
|
|
666
|
|
|
/** |
|
667
|
|
|
* Match error code by response. |
|
668
|
|
|
* |
|
669
|
|
|
* @param string $responseText |
|
670
|
|
|
* @return int |
|
671
|
|
|
*/ |
|
672
|
|
|
private function getErrorCode($responseText) |
|
673
|
|
|
{ |
|
674
|
|
|
if (preg_match('/ERROR:\s*(\d{0,4})/ui', $responseText, $matches)) { |
|
675
|
|
|
return intval($matches[1]); |
|
676
|
|
|
} |
|
677
|
|
|
return 0; |
|
678
|
|
|
} |
|
679
|
|
|
} |
|
680
|
|
|
|
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:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.