1
|
|
|
<?php namespace zServices\Sintegra\Services\Portais\SP; |
2
|
|
|
|
3
|
|
|
use Captcha\Interfaces\ServiceInterface; |
4
|
|
|
use zServices\Miscellany\ClientHttp; |
5
|
|
|
use zServices\Miscellany\Curl; |
6
|
|
|
use zServices\Miscellany\Exceptions\ImageNotFound; |
7
|
|
|
use zServices\Miscellany\Exceptions\NoCaptchaResponse; |
8
|
|
|
use zServices\Miscellany\Exceptions\NoServiceCall; |
9
|
|
|
use zServices\Miscellany\Exceptions\NoServiceResponse; |
10
|
|
|
use zServices\Miscellany\Interfaces\SearchInterface; |
11
|
|
|
use zServices\Sintegra\Services\Portais\SP\Crawler; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* |
15
|
|
|
*/ |
16
|
|
|
class Search implements SearchInterface { |
17
|
|
|
/** |
18
|
|
|
* Armazena a instãncia atual do request no serviço |
19
|
|
|
* @var object |
20
|
|
|
*/ |
21
|
|
|
private $instanceResponse; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Armazena o cookie atual |
25
|
|
|
* @var string |
26
|
|
|
*/ |
27
|
|
|
private $cookie; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Captcha request response |
31
|
|
|
* @var string |
32
|
|
|
*/ |
33
|
|
|
private $captcha; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Armazena o base64 da imagem do captcha |
37
|
|
|
* @var string base64 |
38
|
|
|
*/ |
39
|
|
|
private $captchaImage; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var object zServices\Sintegra\Services\ClientHttp |
43
|
|
|
*/ |
44
|
|
|
private $client; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Armazena as configurações para as requisições e crawler |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
private $configurations; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* [$params description] |
54
|
|
|
* @var array |
55
|
|
|
*/ |
56
|
|
|
private $params = []; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* decaptcher instance |
60
|
|
|
* @var \Captcha\Interfaces\ServiceInterface |
61
|
|
|
*/ |
62
|
|
|
public $decaptcher; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Antes de chamar o cookie e o captcha, é preciso efetuar uma requisição |
66
|
|
|
* primária no serviço. Capturando tais informações. |
67
|
|
|
* Este método deverá fazer essa requisição, armazenando o request |
68
|
|
|
* para os método como cookie e captcha prepararem suas informações |
69
|
|
|
* |
70
|
|
|
* @param array $configurations @ref zServices\Sintegra\Services\Sintegra\{Service}\Service::$configurations |
71
|
|
|
* @return Search |
72
|
|
|
*/ |
73
|
|
View Code Duplication |
public function request($configurations) { |
|
|
|
|
74
|
|
|
$this->configurations = $configurations; |
75
|
|
|
|
76
|
|
|
// instancia o client http |
77
|
|
|
$this->client = new ClientHttp(); |
78
|
|
|
|
79
|
|
|
// Executa um request para URL do serviço, retornando o cookie da requisição primária |
80
|
|
|
$this->instanceResponse = $this->client->request('GET', $this->configurations['home']); |
81
|
|
|
|
82
|
|
|
// Captura o cookie da requisição, será usuado posteriormente |
83
|
|
|
$this->cookie = $this->client->cookie(); |
84
|
|
|
|
85
|
|
|
return $this; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Verifica se existe existencia de request |
90
|
|
|
* @return boolean |
91
|
|
|
*/ |
92
|
|
|
private function hasRequested() { |
93
|
|
|
if (!$this->instanceResponse) { |
94
|
|
|
throw new NoServiceCall("No request from this service, please call first method request", 1); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
return true; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Retorna o captcha do serviço para o usuário |
102
|
|
|
* @return string base64_image |
103
|
|
|
*/ |
104
|
|
|
public function getCaptcha() { |
105
|
|
|
$this->hasRequested(); |
106
|
|
|
|
107
|
|
|
$imageSrc = $this->instanceResponse->filter( |
108
|
|
|
array_get($this->configurations, 'selectors.image') |
109
|
|
|
); |
110
|
|
|
|
111
|
|
|
if (!$imageSrc->count()) { |
112
|
|
|
throw new ImageNotFound("Impossible to crawler image from response", 1); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
$paramBot = $this->instanceResponse->filter( |
116
|
|
|
array_get($this->configurations, 'selectors.paramBot') |
117
|
|
|
); |
118
|
|
|
|
119
|
|
|
if (!$paramBot->count()) { |
120
|
|
|
throw new ImageNotFound("Impossible to crawler parambot from response", 1); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
// Inicia instancia do cURL |
124
|
|
|
$curl = new Curl; |
125
|
|
|
|
126
|
|
|
// Inicia uma requisição para capturar a imagem do captcha |
127
|
|
|
// informando cookie da requisição passada e os headers |
128
|
|
|
// |
129
|
|
|
// to-do: implementar guzzlehttp? |
130
|
|
|
// ele é melhor que o curl? ou mais organizado? |
131
|
|
|
$curl->init($this->configurations['base'] . $imageSrc->attr('src')); |
132
|
|
|
|
133
|
|
|
$this->params['parambot'] = trim($paramBot->attr('value')); |
134
|
|
|
|
135
|
|
|
// headers da requisição |
136
|
|
|
$curl->options([ |
137
|
|
|
CURLOPT_COOKIEJAR => 'cookiejar', |
138
|
|
|
CURLOPT_HTTPHEADER => array( |
139
|
|
|
"Pragma: no-cache", |
140
|
|
|
"Origin: " . $this->configurations['base'], |
141
|
|
|
"Host: " . array_get($this->configurations, 'headers.Host'), |
142
|
|
|
"User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0", |
143
|
|
|
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", |
144
|
|
|
"Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3", |
145
|
|
|
"Accept-Encoding: gzip, deflate", |
146
|
|
|
"Referer: " . $this->configurations['captcha'], |
147
|
|
|
"Cookie: flag=1; " . $this->cookie, |
148
|
|
|
"Connection: keep-alive", |
149
|
|
|
), |
150
|
|
|
CURLOPT_RETURNTRANSFER => true, |
151
|
|
|
CURLOPT_FOLLOWLOCATION => 1, |
152
|
|
|
CURLOPT_BINARYTRANSFER => TRUE, |
153
|
|
|
CURLOPT_CONNECTTIMEOUT => 10, |
154
|
|
|
CURLOPT_TIMEOUT => 10, |
155
|
|
|
]); |
156
|
|
|
|
157
|
|
|
// executa o curl, logo após fechando a conexão |
158
|
|
|
$curl->exec(); |
159
|
|
|
$curl->close(); |
160
|
|
|
|
161
|
|
|
// captura do retorno do curl |
162
|
|
|
// o esperado deverá ser o HTML da imagem |
163
|
|
|
$this->captcha = $curl->response(); |
164
|
|
|
|
165
|
|
|
// é uma imagem o retorno? |
166
|
|
|
if (@imagecreatefromstring($this->captcha) == false) { |
167
|
|
|
throw new NoCaptchaResponse('Não foi possível capturar o captcha'); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
// constroe o base64 da imagem para o usuário digitar |
171
|
|
|
// to-do: um serviço automatizado para decifrar o captcha? |
172
|
|
|
// talvez deathbycaptcha? |
173
|
|
|
$this->captchaImage = 'data:image/png;base64,' . base64_encode($this->captcha); |
174
|
|
|
|
175
|
|
|
return $this->captchaImage; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Retorna o cookie da requisição para as |
180
|
|
|
* próximas requisições |
181
|
|
|
* @return string $cookie |
182
|
|
|
*/ |
183
|
|
|
public function getCookie() { |
184
|
|
|
$this->hasRequested(); |
185
|
|
|
|
186
|
|
|
return $this->cookie; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Alguns serviços possuem outros parametros. |
191
|
|
|
* Como por exemplo o serviço de SP. |
192
|
|
|
* No formulário possui o input "parambot" |
193
|
|
|
* e nas requisições posteriores é preciso enviá-lo. |
194
|
|
|
* |
195
|
|
|
* Este método irá buscar no crawler estes parametros avulsos. |
196
|
|
|
* @return array $params |
197
|
|
|
*/ |
198
|
|
|
public function getParams() { |
199
|
|
|
$this->hasRequested(); |
200
|
|
|
|
201
|
|
|
return $this->params; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* ServiceInterface from decaptcher |
206
|
|
|
* |
207
|
|
|
* Impoe o serviço a ser utilizado para efetuar a quebra do captcha |
208
|
|
|
* @param ServiceInterface $decaptcher |
209
|
|
|
* @return Search |
210
|
|
|
*/ |
211
|
|
|
public function decaptcher(ServiceInterface $decaptcher) { |
212
|
|
|
$this->decaptcher = $decaptcher; |
213
|
|
|
|
214
|
|
|
return $this; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Implement decaptcher |
219
|
|
|
* |
220
|
|
|
* @return string |
221
|
|
|
*/ |
222
|
|
|
private function resolveCaptcha($captchImageOrtxt) { |
223
|
|
|
// auto decaptcher |
224
|
|
|
if ($this->decaptcher) { |
225
|
|
|
$captchImageOrtxt = $this->decaptcher->upload($captchImageOrtxt); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
return $captchImageOrtxt; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Retorna as informações da empresa/pessoa consultada. |
233
|
|
|
* @param integer $document Documento de identificação da entidade |
234
|
|
|
* @param string $cookie Referencia: $service->cookie() |
235
|
|
|
* @param string $captcha Texto do captcha resolvido pelo usuário ou base64 |
236
|
|
|
* @param array $params Parametros avulsos de requisição. Referência $service->params() |
237
|
|
|
* @return Crawler $data Informações da entidade no serviço. |
238
|
|
|
*/ |
239
|
|
|
public function getData($document, $cookie, $captcha, $params, $configurations) { |
240
|
|
|
|
241
|
|
|
// resolve captcha |
242
|
|
|
$captcha = $this->resolveCaptcha($captcha); |
243
|
|
|
|
244
|
|
|
// prepara o form |
245
|
|
|
$postParams = [ |
246
|
|
|
'cnpj' => $document, // apenas números |
247
|
|
|
'Key' => $captcha, |
248
|
|
|
'botao' => 'Consulta por CNPJ', |
249
|
|
|
'hidFlag' => '1', |
250
|
|
|
'ie' => '', |
251
|
|
|
'servico' => 'cnpj', |
252
|
|
|
'paramBot' => $params['parambot'], |
253
|
|
|
]; |
254
|
|
|
|
255
|
|
|
// inicia o cURL |
256
|
|
|
$curl = new Curl; |
257
|
|
|
|
258
|
|
|
// vamos registrar qual serviço será consultado |
259
|
|
|
$curl->init($configurations['data']); |
260
|
|
|
|
261
|
|
|
// define os headers para requisição curl. |
262
|
|
|
$curl->options( |
263
|
|
|
array( |
264
|
|
|
CURLOPT_HTTPHEADER => array( |
265
|
|
|
"Origin: http://pfeserv1.fazenda.sp.gov.br", |
266
|
|
|
"Host: pfeserv1.fazenda.sp.gov.br", |
267
|
|
|
"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/49.0.2623.108 Chrome/49.0.2623.108 Safari/537.36", |
268
|
|
|
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", |
269
|
|
|
"Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4,es;q=0.2", |
270
|
|
|
"Accept-Encoding: gzip, deflate", |
271
|
|
|
"Referer: http://pfeserv1.fazenda.sp.gov.br/sintegrapfe/consultaSintegraServlet", |
272
|
|
|
"Cookie: flag=1; " . $cookie, |
273
|
|
|
"Connection: keep-alive", |
274
|
|
|
), |
275
|
|
|
CURLOPT_RETURNTRANSFER => 1, |
276
|
|
|
CURLOPT_BINARYTRANSFER => 1, |
277
|
|
|
CURLOPT_FOLLOWLOCATION => 1, |
278
|
|
|
) |
279
|
|
|
); |
280
|
|
|
|
281
|
|
|
// efetua a chamada passando os parametros de form |
282
|
|
|
$curl->post($postParams); |
283
|
|
|
$curl->exec(); |
284
|
|
|
|
285
|
|
|
// completa a chamda |
286
|
|
|
$curl->close(); |
287
|
|
|
|
288
|
|
|
// vamos capturar retorno, que deverá ser o HTML para scrapping |
289
|
|
|
$html = $curl->response(); |
290
|
|
|
|
291
|
|
|
if (empty($html)) { |
292
|
|
|
throw new NoServiceResponse('No response from service', 99); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
$crawler = new Crawler($html, array_get($configurations, 'selectors.data')); |
296
|
|
|
|
297
|
|
|
return $crawler; |
|
|
|
|
298
|
|
|
} |
299
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.