Completed
Push — master ( 65944d...04d3b7 )
by Dmitry
03:22 queued 01:22
created

GenericClient   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 231
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 67.11%

Importance

Changes 5
Bugs 1 Features 0
Metric Value
wmc 23
c 5
b 1
f 0
lcom 1
cbo 10
dl 0
loc 231
ccs 51
cts 76
cp 0.6711
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getLastCaptchaId() 0 4 1
A setHttpClient() 0 6 1
A recognizeFile() 0 16 3
B recognize() 0 38 4
A sendCaptcha() 0 22 3
A getCaptchaResult() 0 16 4
A getErrorMessage() 0 6 2
A getHttpClient() 0 9 2
A getLogger() 0 11 2
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
        /* Send image to recognition server */
125
126
        $this->getLogger()->info("Try send captcha image on {$this->serverBaseUri}/in.php");
127
128
        $captchaId = $this->sendCaptcha($content, $extra);
129
130
131
        /* Get captcha recognition result */
132
133
        $this->getLogger()->info("Sending success. Got captcha id `$captchaId`.");
134
135
        $startTime = time();
136
137
        while (true) {
138
139
            $this->getLogger()->info("Waiting {$this->rTimeout} sec.");
140
141
            sleep($this->rTimeout);
142
143
            if (time() - $startTime >= $this->mTimeout) {
144
                throw new RuntimeException("Captcha waiting timeout.");
145
            }
146
147
            $result = $this->getCaptchaResult($captchaId);
148
149
            if ($result === false) {
150
                continue;
151
            }
152
153
            $this->getLogger()->info("Got OK response: `{$result}`. Elapsed " . (time() - $startTime) . ' sec.');
154
155
            return $result;
156
        }
157
158
        throw new RuntimeException('Unknown recognition logic error.');
159
    }
160
161
    /**
162
     * @param string $content       # Captcha image content
163
     * @param array $extra          # Array of recognition options
164
     * @return string               # Captcha task ID
165
     * @throws RuntimeException
166
     */
167 13
    public function sendCaptcha($content, array $extra = [])
168
    {
169 13
        $response = $this->getHttpClient()->request('POST', '/in.php', [
170 13
            RequestOptions::HEADERS => [
171
                'Content-Type' => 'application/x-www-form-urlencoded'
172 13
            ],
173 13
            RequestOptions::FORM_PARAMS => array_merge($extra, [
174 13
                'method' => 'base64',
175 13
                'key' => $this->apiKey,
176 13
                'body' => base64_encode($content)
177 13
            ])
178 13
        ]);
179
180 13
        $responseText = $response->getBody()->__toString();
181
182 13
        if (strpos($responseText, 'OK|') !== false) {
183 1
            $this->lastCaptchaId = explode("|", $responseText)[1];
184 1
            return $this->lastCaptchaId;
185
        }
186
187 12
        throw new RuntimeException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`.");
188
    }
189
190
    /**
191
     * @param string $captchaId     # Captcha task ID
192
     * @return string|false         # Solved captcha text or false if captcha is not ready
193
     * @throws RuntimeException
194
     */
195 9
    public function getCaptchaResult($captchaId)
196
    {
197 9
        $response = $this->getHttpClient()->request('GET', "/res.php?key={$this->apiKey}&action=get&id={$captchaId}");
198
199 9
        $responseText = $response->getBody()->__toString();
200
201 9
        if ($responseText === self::STATUS_CAPTCHA_NOT_READY) {
202 1
            return false;
203
        }
204
205 8
        if (strpos($responseText, 'OK|') !== false) {
206 1
            return html_entity_decode(trim(explode('|', $responseText)[1]));
207
        }
208
209 7
        throw new RuntimeException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`.");
210
    }
211
212
    /**
213
     * @param string $responseText  # Server response text usually begin with `ERROR_` prefix
214
     * @return false|string         # Error message text or false if associated message in not found
215
     */
216 19
    protected function getErrorMessage($responseText)
217
    {
218 19
        return isset(Error::$messages[$responseText])
219 19
            ? Error::$messages[$responseText]
220 19
            : false;
221
    }
222
223
    /**
224
     * @return ClientInterface
225
     */
226 23
    protected function getHttpClient()
227
    {
228 23
        if ($this->httpClient === null) {
229 1
            $this->httpClient = new GuzzleClient([
230 1
                'base_uri' => $this->serverBaseUri
231 1
            ]);
232 1
        }
233 23
        return $this->httpClient;
234
    }
235
236
    /**
237
     * @return LoggerInterface
238
     */
239 1
    protected function getLogger()
240
    {
241 1
        if ($this->logger === null)
242 1
        {
243 1
            $defaultLogger = new Logger;
244 1
            $defaultLogger->verbose =& $this->verbose;
245
246 1
            $this->setLogger($defaultLogger);
247 1
        }
248 1
        return $this->logger;
249
    }
250
}