Completed
Push — master ( 9fe909...a7b507 )
by Dmitry
03:01 queued 01:06
created

GenericClient::getCaptchaResult()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 3
nop 1
crap 4
1
<?php
2
/**
3
 * @author Dmitry Gladyshev <[email protected]>
4
 */
5
6
namespace Rucaptcha;
7
8
use GuzzleHttp\Client as GuzzleClient;
9
use GuzzleHttp\ClientInterface;
10
use GuzzleHttp\RequestOptions;
11
use Psr\Log\LoggerAwareInterface;
12
use Psr\Log\LoggerAwareTrait;
13
use Psr\Log\LoggerInterface;
14
use Rucaptcha\Exception\InvalidArgumentException;
15
use Rucaptcha\Exception\RucaptchaException;
16
use Rucaptcha\Exception\RuntimeException;
17
use SplFileObject;
18
19
class GenericClient implements LoggerAwareInterface
20
{
21
    use LoggerAwareTrait;
22
    use ConfigurableTrait;
23
24
    /* Statuses */
25
    const STATUS_OK = 'OK';
26
    const STATUS_CAPTCHA_NOT_READY = 'CAPCHA_NOT_READY';
27
28
    /**
29
     * @var string
30
     */
31
    protected $lastCaptchaId = '';
32
33
    /**
34
     * @var string
35
     */
36
    protected $serverBaseUri = '';
37
38
    /**
39
     * @var bool
40
     */
41
    protected $verbose = false;
42
43
    /**
44
     * @var string
45
     */
46
    protected $apiKey = '';
47
48
    /**
49
     * @var int
50
     */
51
    protected $rTimeout = 5;
52
53
    /**
54
     * @var int
55
     */
56
    protected $mTimeout = 120;
57
58
    /**
59
     * @var ClientInterface
60
     */
61
    private $httpClient = null;
62
63
    /**
64
     * @param array $options
65
     * @param string $apiKey
66
     */
67 25
    public function __construct($apiKey, array $options = [])
68
    {
69 25
        $this->apiKey = $apiKey;
70 25
        $this->setOptions($options);
71 25
    }
72
73
    /**
74
     * @return string   # Last successfully sent captcha task ID
75
     */
76 2
    public function getLastCaptchaId()
77
    {
78 2
        return $this->lastCaptchaId;
79
    }
80
81
    /**
82
     * @param ClientInterface $client
83
     * @return $this
84
     */
85 22
    public function setHttpClient(ClientInterface $client)
86
    {
87 22
        $this->httpClient = $client;
88
89 22
        return $this;
90
    }
91
92
    /**
93
     * @param string $path
94
     * @param array $extra
95
     * @return string
96
     * @throws RucaptchaException
97
     */
98
    public function recognizeFile($path, array $extra = [])
99
    {
100
        if (!file_exists($path)) {
101
            throw new InvalidArgumentException("Captcha file `$path` not found.");
102
        }
103
104
        $file = new SplFileObject($path, 'r');
105
106
        $content = '';
107
108
        while (!$file->eof()) {
109
            $content .= $file->fgets();
110
        }
111
112
        return $this->recognize($content, $extra);
113
    }
114
115
    /**
116
     * @param string $content
117
     * @param array $extra
118
     * @return string
119
     * @throws RuntimeException
120
     */
121
    public function recognize($content, array $extra = [])
122
    {
123
        $captchaId = $this->sendCaptcha($content, $extra);
124
        $startTime = time();
125
126
        while (true) {
127
            $this->getLogger()->info("Waiting {$this->rTimeout} sec.");
128
129
            sleep($this->rTimeout);
130
131
            if (time() - $startTime >= $this->mTimeout) {
132
                throw new RuntimeException("Captcha waiting timeout.");
133
            }
134
135
            $result = $this->getCaptchaResult($captchaId);
136
137
            if ($result === false) {
138
                continue;
139
            }
140
141
            $this->getLogger()->info("Elapsed " . (time()-$startTime) . " second(s).");
142
143
            return $result;
144
        }
145
146
        throw new RuntimeException('Unknown recognition logic error.');
147
    }
148
149
    /**
150
     * @param string $content       # Captcha image content
151
     * @param array $extra          # Array of recognition options
152
     * @return string               # Captcha task ID
153
     * @throws RuntimeException
154
     */
155 13
    public function sendCaptcha($content, array $extra = [])
156
    {
157 13
        $this->getLogger()->info("Try send captcha image on {$this->serverBaseUri}/in.php");
158
159 13
        $response = $this->getHttpClient()->request('POST', '/in.php', [
160 13
            RequestOptions::HEADERS => [
161
                'Content-Type' => 'application/x-www-form-urlencoded'
162 13
            ],
163 13
            RequestOptions::FORM_PARAMS => array_merge($extra, [
164 13
                'method' => 'base64',
165 13
                'key' => $this->apiKey,
166 13
                'body' => base64_encode($content)
167 13
            ])
168 13
        ]);
169
170 13
        $responseText = $response->getBody()->__toString();
171
172 13
        if (strpos($responseText, 'OK|') !== false) {
173 1
            $this->lastCaptchaId = explode("|", $responseText)[1];
174 1
            $this->getLogger()->info("Sending success. Got captcha id `{$this->lastCaptchaId}`.");
175 1
            return $this->lastCaptchaId;
176
        }
177
178 12
        throw new RuntimeException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`.");
179
    }
180
181
    /**
182
     * @param string $captchaId     # Captcha task ID
183
     * @return string|false         # Solved captcha text or false if captcha is not ready
184
     * @throws RuntimeException
185
     */
186 9
    public function getCaptchaResult($captchaId)
187
    {
188 9
        $response = $this->getHttpClient()->request('GET', "/res.php?key={$this->apiKey}&action=get&id={$captchaId}");
189
190 9
        $responseText = $response->getBody()->__toString();
191
192 9
        if ($responseText === self::STATUS_CAPTCHA_NOT_READY) {
193 1
            return false;
194
        }
195
196 8
        if (strpos($responseText, 'OK|') !== false) {
197 1
            $this->getLogger()->info("Got OK response: `{$responseText}`.");
198 1
            return html_entity_decode(trim(explode('|', $responseText)[1]));
199
        }
200
201 7
        throw new RuntimeException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`.");
202
    }
203
204
    /**
205
     * @param string $responseText  # Server response text usually begin with `ERROR_` prefix
206
     * @return false|string         # Error message text or false if associated message in not found
207
     */
208 19
    protected function getErrorMessage($responseText)
209
    {
210 19
        return isset(Error::$messages[$responseText])
211 19
            ? Error::$messages[$responseText]
212 19
            : false;
213
    }
214
215
    /**
216
     * @return ClientInterface
217
     */
218 23
    protected function getHttpClient()
219
    {
220 23
        if ($this->httpClient === null) {
221 1
            $this->httpClient = new GuzzleClient([
222 1
                'base_uri' => $this->serverBaseUri
223 1
            ]);
224 1
        }
225 23
        return $this->httpClient;
226
    }
227
228
    /**
229
     * @return LoggerInterface
230
     */
231 15
    protected function getLogger()
232
    {
233 15
        if ($this->logger === null) {
234 15
            $defaultLogger = new Logger;
235 15
            $defaultLogger->verbose = & $this->verbose;
236
237 15
            $this->setLogger($defaultLogger);
238 15
        }
239 15
        return $this->logger;
240
    }
241
}
242