Completed
Push — master ( 2028e7...238e6a )
by Dmitry
05:44 queued 01:42
created

GenericClient::recognizeFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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