Completed
Push — master ( 7c51cd...7e9df0 )
by Dmitry
03:15
created

GenericClient::setVerbose()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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