├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── examples
└── CreateVoucher.php
└── src
├── Afip.php
├── Class
├── ElectronicBilling.php
├── RegisterInscriptionProof.php
├── RegisterScopeFive.php
├── RegisterScopeFour.php
├── RegisterScopeTen.php
└── RegisterScopeThirteen.php
└── libs
└── Requests
├── Requests.php
└── Requests
├── Auth.php
├── Auth
└── Basic.php
├── Cookie.php
├── Cookie
└── Jar.php
├── Exception.php
├── Exception
├── HTTP.php
├── HTTP
│ ├── 304.php
│ ├── 305.php
│ ├── 306.php
│ ├── 400.php
│ ├── 401.php
│ ├── 402.php
│ ├── 403.php
│ ├── 404.php
│ ├── 405.php
│ ├── 406.php
│ ├── 407.php
│ ├── 408.php
│ ├── 409.php
│ ├── 410.php
│ ├── 411.php
│ ├── 412.php
│ ├── 413.php
│ ├── 414.php
│ ├── 415.php
│ ├── 416.php
│ ├── 417.php
│ ├── 418.php
│ ├── 428.php
│ ├── 429.php
│ ├── 431.php
│ ├── 500.php
│ ├── 501.php
│ ├── 502.php
│ ├── 503.php
│ ├── 504.php
│ ├── 505.php
│ ├── 511.php
│ └── Unknown.php
├── Transport.php
└── Transport
│ └── cURL.php
├── Hooker.php
├── Hooks.php
├── IDNAEncoder.php
├── IPv6.php
├── IRI.php
├── Proxy.php
├── Proxy
└── HTTP.php
├── Response.php
├── Response
└── Headers.php
├── SSL.php
├── Session.php
├── Transport.php
├── Transport
├── cURL.php
├── cacert.pem
└── fsockopen.php
└── Utility
├── CaseInsensitiveDictionary.php
└── FilteredIterator.php
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Afip SDK
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [![Packagist][packagist-shield]](https://packagist.org/packages/afipsdk/afip.php)
5 | [![Contributors][contributors-shield]](https://github.com/afipsdk/afip.php/graphs/contributors)
6 | [![Closed issues][issues-shield]](https://github.com/afipsdk/afip.php/issues)
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Afip.php
17 |
18 |
19 | Librería para conectarse a los Web Services de AFIP
20 |
21 | Explorar documentación »
22 |
23 |
24 | Comunidad Afip SDK
25 |
26 |
27 | Reportar un bug
28 |
29 |
30 |
31 |
32 |
33 | ## Documentación
34 | [Explorar documentación](https://docs.afipsdk.com)
35 |
36 |
37 | ## Comunidad
38 | [Comunidad Afip SDK](https://discord.gg/A6TuHEyAZm)
39 |
40 |
41 |
42 | ## Acerca del proyecto
43 | Con más de 100k descargas, desde el 2017, Afip SDK es la plataforma preferida entre los desarrolladores para conectarse a los web services de ARCA.
44 |
45 |
46 | ### Contacto
47 | Soporte de Afip SDK - ayuda@afipsdk.com
48 |
49 | Link del proyecto: [https://github.com/afipsdk/afip.php](https://github.com/afipsdk/afip.php)
50 |
51 |
52 | _Este software y sus desarrolladores no tienen ninguna relación con la AFIP._
53 |
54 |
55 | [packagist-shield]: https://img.shields.io/packagist/dt/afipsdk/afip.php.svg?logo=php&?logoColor=white
56 | [contributors-shield]: https://img.shields.io/github/contributors/afipsdk/afip.php.svg?color=orange
57 | [issues-shield]: https://img.shields.io/github/issues-closed-raw/afipsdk/afip.php.svg?color=blueviolet
58 | [license-shield]: https://img.shields.io/github/license/afipsdk/afip.php.svg?color=blue
59 |
60 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "afipsdk/afip.php",
3 | "type": "library",
4 | "description": "Libreria para usar los Web Services de AFIP (Argentina) ",
5 | "keywords": ["afip","facturacion-electronica","gratis","soap","cae","padron","contribuyente","libreria"],
6 | "homepage": "https://github.com/afipsdk/afip.php",
7 | "license": "MIT",
8 | "require": {
9 | "php": ">=5.3.3"
10 | },
11 | "authors": [
12 | {
13 | "name": "Afip SDK",
14 | "email": "afipsdk@gmail.com"
15 | }
16 | ],
17 | "autoload": {
18 | "files": [ "src/Afip.php" ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/CreateVoucher.php:
--------------------------------------------------------------------------------
1 | 1, // Cantidad de comprobantes a registrar
6 | 'PtoVta' => 1, // Punto de venta
7 | 'CbteTipo' => 6, // Tipo de comprobante (ver tipos disponibles)
8 | 'Concepto' => 1, // Concepto del Comprobante: (1)Productos, (2)Servicios, (3)Productos y Servicios
9 | 'DocTipo' => 80, // Tipo de documento del comprador (ver tipos disponibles)
10 | 'DocNro' => 20111111112, // Numero de documento del comprador
11 | 'CbteDesde' => 1, // Numero de comprobante o numero del primer comprobante en caso de ser mas de uno
12 | 'CbteHasta' => 1, // Numero de comprobante o numero del ultimo comprobante en caso de ser mas de uno
13 | 'CbteFch' => intval(date('Ymd')), // (Opcional) Fecha del comprobante (yyyymmdd) o fecha actual si es nulo
14 | 'ImpTotal' => 184.05, // Importe total del comprobante
15 | 'ImpTotConc' => 0, // Importe neto no gravado
16 | 'ImpNeto' => 150, // Importe neto gravado
17 | 'ImpOpEx' => 0, // Importe exento de IVA
18 | 'ImpIVA' => 26.25, //Importe total de IVA
19 | 'ImpTrib' => 7.8, //Importe total de tributos
20 | 'FchServDesde' => NULL, // (Opcional) Fecha de inicio del servicio (yyyymmdd), obligatorio para Concepto 2 y 3
21 | 'FchServHasta' => NULL, // (Opcional) Fecha de fin del servicio (yyyymmdd), obligatorio para Concepto 2 y 3
22 | 'FchVtoPago' => NULL, // (Opcional) Fecha de vencimiento del servicio (yyyymmdd), obligatorio para Concepto 2 y 3
23 | 'MonId' => 'PES', //Tipo de moneda usada en el comprobante (ver tipos disponibles)('PES' para pesos argentinos)
24 | 'MonCotiz' => 1, // Cotización de la moneda usada (1 para pesos argentinos)
25 | 'CbtesAsoc' => array( // (Opcional) Comprobantes asociados
26 | array(
27 | 'Tipo' => 6, // Tipo de comprobante (ver tipos disponibles)
28 | 'PtoVta' => 1, // Punto de venta
29 | 'Nro' => 1, // Numero de comprobante
30 | 'Cuit' => 20111111112 // (Opcional) Cuit del emisor del comprobante
31 | )
32 | ),
33 | 'Tributos' => array( // (Opcional) Tributos asociados al comprobante
34 | array(
35 | 'Id' => 99, // Id del tipo de tributo (ver tipos disponibles)
36 | 'Desc' => 'Ingresos Brutos', // (Opcional) Descripcion
37 | 'BaseImp' => 150, // Base imponible para el tributo
38 | 'Alic' => 5.2, // Alícuota
39 | 'Importe' => 7.8 // Importe del tributo
40 | )
41 | ),
42 | 'Iva' => array( // (Opcional) Alícuotas asociadas al comprobante
43 | array(
44 | 'Id' => 5, // Id del tipo de IVA (ver tipos disponibles)
45 | 'BaseImp' => 100, // Base imponible
46 | 'Importe' => 21 // Importe
47 | )
48 | ),
49 | 'Opcionales' => array( // (Opcional) Campos auxiliares
50 | array(
51 | 'Id' => 17, // Codigo de tipo de opcion (ver tipos disponibles)
52 | 'Valor' => 2 // Valor
53 | )
54 | ),
55 | 'Compradores' => array( // (Opcional) Detalles de los clientes del comprobante
56 | array(
57 | 'DocTipo' => 80, // Tipo de documento (ver tipos disponibles)
58 | 'DocNro' => 20111111112, // Numero de documento
59 | 'Porcentaje' => 100 // Porcentaje de titularidad del comprador
60 | )
61 | )
62 | );
63 |
64 | $afip = new Afip(array('CUIT' => 20111111112));
65 |
66 | $afip->ElectronicBilling->CreateVoucher($data);
67 |
68 | ?>
--------------------------------------------------------------------------------
/src/Afip.php:
--------------------------------------------------------------------------------
1 | CUIT = $options['CUIT'];
78 | }
79 |
80 | if (!isset($options['production'])) {
81 | $options['production'] = FALSE;
82 | }
83 |
84 | if (!isset($options['cert'])) {
85 | $options['cert'] = NULL;
86 | }
87 |
88 | if (!isset($options['key'])) {
89 | $options['key'] = NULL;
90 | }
91 |
92 | $this->options = $options;
93 |
94 | $this->CERT = $options['cert'];
95 | $this->PRIVATEKEY = $options['key'];
96 | }
97 |
98 | /**
99 | * Gets token authorization for an AFIP Web Service
100 | *
101 | * @param string $service Service for token authorization
102 | * @param boolean $force Force to create a new token
103 | * authorization even if it is not expired
104 | *
105 | * @throws Exception if an error occurs
106 | *
107 | * @return TokenAuthorization Token Authorization for AFIP Web Service
108 | **/
109 | public function GetServiceTA($service, $force = FALSE)
110 | {
111 | // Prepare data to for request
112 | $data = array(
113 | 'environment' => $this->options['production'] === TRUE ? "prod" : "dev",
114 | 'wsid' => $service,
115 | 'tax_id' => $this->options['CUIT'],
116 | 'force_create' => $force
117 | );
118 |
119 | // Add cert if is set
120 | if (isset($this->CERT)) {
121 | $data['cert'] = $this->CERT;
122 | }
123 |
124 | // Add key is is set
125 | if ($this->PRIVATEKEY) {
126 | $data['key'] = $this->PRIVATEKEY;
127 | }
128 |
129 | $headers = array(
130 | 'Content-Type' => 'application/json',
131 | 'sdk-version-number' => $this->sdk_version_number,
132 | 'sdk-library' => 'php',
133 | 'sdk-environment' => $this->options['production'] === TRUE ? "prod" : "dev"
134 | );
135 |
136 | if (isset($this->options['access_token'])) {
137 | $headers['Authorization'] = 'Bearer '.$this->options['access_token'];
138 | }
139 |
140 | $request = Requests::post('https://app.afipsdk.com/api/v1/afip/auth', $headers, json_encode($data));
141 |
142 | if ($request->success) {
143 | $decoded_res = json_decode($request->body);
144 |
145 | //Return response
146 | return new TokenAuthorization($decoded_res->token, $decoded_res->sign);
147 | }
148 | else {
149 | $error_message = $request->body;
150 |
151 | try {
152 | $json_res = json_decode($request->body);
153 |
154 | if (isset($json_res->message)) {
155 | $error_message = $json_res->message;
156 | }
157 | } catch (Exception $e) {}
158 |
159 | throw new Exception($error_message);
160 | }
161 | }
162 |
163 | /**
164 | * Get last request and last response XML
165 | **/
166 | public function GetLastRequestXML()
167 | {
168 | $headers = array(
169 | 'sdk-version-number' => $this->sdk_version_number,
170 | 'sdk-library' => 'php',
171 | 'sdk-environment' => $this->options['production'] === TRUE ? "prod" : "dev"
172 | );
173 |
174 | if (isset($this->options['access_token'])) {
175 | $headers['Authorization'] = 'Bearer '.$this->options['access_token'];
176 | }
177 |
178 | $request = Requests::get('https://app.afipsdk.com/api/v1/afip/requests/last-xml', $headers);
179 |
180 | if ($request->success) {
181 | $decoded_res = json_decode($request->body);
182 |
183 | //Return response
184 | return $decoded_res;
185 | }
186 | else {
187 | $error_message = $request->body;
188 |
189 | try {
190 | $json_res = json_decode($request->body);
191 |
192 | if (isset($json_res->message)) {
193 | $error_message = $json_res->message;
194 | }
195 | } catch (Exception $e) {}
196 |
197 | throw new Exception($error_message);
198 | }
199 | }
200 |
201 | /**
202 | * Create generic Web Service
203 | *
204 | * @param string $service Web Service name
205 | * @param array $options Web Service options
206 | *
207 | * @throws Exception if an error occurs
208 | *
209 | * @return AfipWebService New AFIP Web Service
210 | **/
211 | public function WebService($service, $options = array())
212 | {
213 | $options['service'] = $service;
214 | $options['generic'] = TRUE;
215 |
216 | return new AfipWebService($this, $options);
217 | }
218 |
219 | /**
220 | * Create AFIP cert
221 | *
222 | * @param string $username Username used in AFIP page
223 | * @param string $password Password used in AFIP page
224 | * @param string $alias Alias for the cert
225 | **/
226 | public function CreateCert($username, $password, $alias)
227 | {
228 | // Prepare data to for request
229 | $data = array(
230 | 'environment' => $this->options['production'] === TRUE ? "prod" : "dev",
231 | 'tax_id' => $this->options['CUIT'],
232 | 'username' => $username,
233 | 'password' => $password,
234 | 'alias' => $alias
235 | );
236 |
237 | $headers = array(
238 | 'Content-Type' => 'application/json',
239 | 'sdk-version-number' => $this->sdk_version_number,
240 | 'sdk-library' => 'php',
241 | 'sdk-environment' => $this->options['production'] === TRUE ? "prod" : "dev"
242 | );
243 |
244 | if (isset($this->options['access_token'])) {
245 | $headers['Authorization'] = 'Bearer '.$this->options['access_token'];
246 | }
247 |
248 | // Wait for max 120 seconds
249 | $retry = 24;
250 |
251 | while ($retry-- >= 0) {
252 | // Execute request
253 | $request = Requests::post('https://app.afipsdk.com/api/v1/afip/certs', $headers, json_encode($data));
254 |
255 | if ($request->success) {
256 | $decoded_res = json_decode($request->body);
257 |
258 | if ($decoded_res->status === 'complete') {
259 | return $decoded_res->data;
260 | }
261 |
262 | if (isset($decoded_res->long_job_id)) {
263 | $data['long_job_id'] = $decoded_res->long_job_id;
264 | }
265 |
266 | // Wait 5 seconds
267 | sleep(5);
268 | }
269 | else {
270 | $error_message = $request->body;
271 |
272 | try {
273 | $json_res = json_decode($request->body);
274 |
275 | if (isset($json_res->message)) {
276 | $error_message = $json_res->message;
277 | }
278 | } catch (Exception $e) {}
279 |
280 | throw new Exception($error_message);
281 | }
282 | }
283 |
284 | throw new Exception('Error: Waiting for too long');
285 |
286 | }
287 |
288 | /**
289 | * Create authorization to use a web service
290 | *
291 | * @param string $username Username used in AFIP page
292 | * @param string $password Password used in AFIP page
293 | * @param string $alias Cert alias
294 | * @param string $wsid Web service id
295 | **/
296 | public function CreateWSAuth($username, $password, $alias, $wsid)
297 | {
298 | // Prepare data to for request
299 | $data = array(
300 | 'environment' => $this->options['production'] === TRUE ? "prod" : "dev",
301 | 'tax_id' => $this->options['CUIT'],
302 | 'username' => $username,
303 | 'password' => $password,
304 | 'wsid' => $wsid,
305 | 'alias' => $alias
306 | );
307 |
308 | $headers = array(
309 | 'Content-Type' => 'application/json',
310 | 'sdk-version-number' => $this->sdk_version_number,
311 | 'sdk-library' => 'php',
312 | 'sdk-environment' => $this->options['production'] === TRUE ? "prod" : "dev"
313 | );
314 |
315 | if (isset($this->options['access_token'])) {
316 | $headers['Authorization'] = 'Bearer '.$this->options['access_token'];
317 | }
318 |
319 | // Wait for max 120 seconds
320 | $retry = 24;
321 |
322 | while ($retry-- >= 0) {
323 | // Execute request
324 | $request = Requests::post('https://app.afipsdk.com/api/v1/afip/ws-auths', $headers, json_encode($data));
325 |
326 | if ($request->success) {
327 | $decoded_res = json_decode($request->body);
328 |
329 | if ($decoded_res->status === 'complete') {
330 | return $decoded_res->data;
331 | }
332 |
333 | if (isset($decoded_res->long_job_id)) {
334 | $data['long_job_id'] = $decoded_res->long_job_id;
335 | }
336 |
337 | // Wait 5 seconds
338 | sleep(5);
339 | }
340 | else {
341 | $error_message = $request->body;
342 |
343 | try {
344 | $json_res = json_decode($request->body);
345 |
346 | if (isset($json_res->message)) {
347 | $error_message = $json_res->message;
348 | }
349 | } catch (Exception $e) {}
350 |
351 | throw new Exception($error_message);
352 | }
353 | }
354 |
355 | throw new Exception('Error: Waiting for too long');
356 | }
357 |
358 | public function __get($property)
359 | {
360 | if (in_array($property, $this->implemented_ws)) {
361 | if (isset($this->{$property})) {
362 | return $this->{$property};
363 | } else {
364 | $file = __DIR__.'/Class/'.$property.'.php';
365 | if (!file_exists($file))
366 | throw new Exception("Failed to open ".$file."\n", 1);
367 |
368 | include_once $file;
369 |
370 | return ($this->{$property} = new $property($this));
371 | }
372 | } else {
373 | return $this->{$property};
374 | }
375 | }
376 | }
377 |
378 | /**
379 | * Token Authorization
380 | **/
381 | class TokenAuthorization {
382 | /**
383 | * Authorization and authentication web service Token
384 | *
385 | * @var string
386 | **/
387 | var $token;
388 |
389 | /**
390 | * Authorization and authentication web service Sign
391 | *
392 | * @var string
393 | **/
394 | var $sign;
395 |
396 | function __construct($token, $sign)
397 | {
398 | $this->token = $token;
399 | $this->sign = $sign;
400 | }
401 | }
402 |
403 | /**
404 | * Base class for AFIP web services
405 | **/
406 | #[\AllowDynamicProperties]
407 | class AfipWebService {
408 | /**
409 | * Web service SOAP version
410 | *
411 | * @var intenger
412 | **/
413 | var $soap_version;
414 |
415 | /**
416 | * File name for the Web Services Description Language
417 | *
418 | * @var string
419 | **/
420 | var $WSDL;
421 |
422 | /**
423 | * The url to web service
424 | *
425 | * @var string
426 | **/
427 | var $URL;
428 |
429 | /**
430 | * File name for the Web Services Description
431 | * Language in test mode
432 | *
433 | * @var string
434 | **/
435 | var $WSDL_TEST;
436 |
437 | /**
438 | * The url to web service in test mode
439 | *
440 | * @var string
441 | **/
442 | var $URL_TEST;
443 |
444 | /**
445 | * The Afip parent Class
446 | *
447 | * @var Afip
448 | **/
449 | var $afip;
450 |
451 | /**
452 | * Class options
453 | *
454 | * @var object
455 | **/
456 | var $options;
457 |
458 | function __construct($afip, $options = array())
459 | {
460 | $this->afip = $afip;
461 | $this->options = $options;
462 |
463 | if (isset($options['WSDL'])) {
464 | $this->WSDL = $options['WSDL'];
465 | }
466 |
467 | if (isset($options['URL'])) {
468 | $this->URL = $options['URL'];
469 | }
470 |
471 | if (isset($options['WSDL_TEST'])) {
472 | $this->WSDL_TEST = $options['WSDL_TEST'];
473 | }
474 |
475 | if (isset($options['URL_TEST'])) {
476 | $this->URL_TEST = $options['URL_TEST'];
477 | }
478 |
479 | if (isset($options['generic']) && $options['generic'] === TRUE) {
480 | if (!isset($options['service'])) {
481 | throw new Exception("service field is required in options");
482 | }
483 |
484 | if (!isset($options['soap_version'])) {
485 | $options['soap_version'] = SOAP_1_2;
486 | }
487 |
488 | $this->soap_version = $options['soap_version'];
489 | }
490 | }
491 |
492 | /**
493 | * Get Web Service Token Authorization from WSAA
494 | *
495 | * @param boolean force Force to create a new token
496 | * authorization even if it is not expired
497 | *
498 | * @return TokenAuthorization Token Authorization for AFIP Web Service
499 | **/
500 | public function GetTokenAuthorization($force = FALSE)
501 | {
502 | return $this->afip->GetServiceTA($this->options['service'], $force);
503 | }
504 |
505 | /**
506 | * Sends request to AFIP servers
507 | *
508 | * @since 0.6
509 | *
510 | * @param string $method SOAP method to execute
511 | * @param array $params Parameters to send
512 | *
513 | * @return mixed Operation results
514 | **/
515 | public function ExecuteRequest($method, $params = array())
516 | {
517 | // Prepare data to for request
518 | $data = array(
519 | 'method' => $method,
520 | 'params' => $params,
521 | 'environment' => $this->afip->options['production'] === TRUE ? "prod" : "dev",
522 | 'wsid' => $this->options['service'],
523 | 'url' => $this->afip->options['production'] === TRUE ? $this->URL : $this->URL_TEST,
524 | 'wsdl' => $this->afip->options['production'] === TRUE ? $this->WSDL : $this->WSDL_TEST,
525 | 'soap_v_1_2' => $this->soap_version === SOAP_1_2
526 | );
527 |
528 | $headers = array(
529 | 'Content-Type' => 'application/json',
530 | 'sdk-version-number' => $this->afip->sdk_version_number,
531 | 'sdk-library' => 'php',
532 | 'sdk-environment' => $this->afip->options['production'] === TRUE ? "prod" : "dev"
533 | );
534 |
535 | if (isset($this->afip->options['access_token'])) {
536 | $headers['Authorization'] = 'Bearer '.$this->afip->options['access_token'];
537 | }
538 |
539 | $request = Requests::post('https://app.afipsdk.com/api/v1/afip/requests', $headers, json_encode($data));
540 |
541 | if ($request->success) {
542 | $decoded_res = json_decode($request->body);
543 |
544 | //Return response
545 | return $decoded_res;
546 | }
547 | else {
548 | $error_message = $request->body;
549 |
550 | throw new Exception($error_message);
551 | }
552 |
553 | return $results;
554 | }
555 | }
556 |
--------------------------------------------------------------------------------
/src/Class/ElectronicBilling.php:
--------------------------------------------------------------------------------
1 | 'wsfe'));
21 | }
22 |
23 | /**
24 | * Create PDF
25 | *
26 | * Send a request to Afip SDK server to create a PDF
27 | *
28 | * @param array $data Data for PDF creation
29 | **/
30 | public function CreatePDF($data)
31 | {
32 | $headers = array(
33 | 'sdk-version-number' => $this->afip->sdk_version_number,
34 | 'sdk-library' => 'php',
35 | 'sdk-environment' => $this->afip->options['production'] === TRUE ? "prod" : "dev"
36 | );
37 |
38 | if (isset($this->afip->options['access_token'])) {
39 | $headers['Authorization'] = 'Bearer '.$this->afip->options['access_token'];
40 | }
41 |
42 | $request = Requests::post('https://app.afipsdk.com/api/v1/pdfs', $headers, $data);
43 |
44 | if ($request->success) {
45 | $decoded_res = json_decode($request->body);
46 |
47 | return array(
48 | "file" => $decoded_res->file,
49 | "file_name" => $decoded_res->file_name
50 | );
51 | }
52 | else {
53 | $error_message = $request->body;
54 |
55 | try {
56 | $json_res = json_decode($request->body);
57 |
58 | if (isset($json_res->message)) {
59 | $error_message = $json_res->message;
60 | }
61 | } catch (Exception $e) {}
62 |
63 | throw new Exception($error_message);
64 | }
65 | }
66 |
67 | /**
68 | * Gets last voucher number
69 | *
70 | * Asks to Afip servers for number of the last voucher created for
71 | * certain sales point and voucher type {@see WS Specification
72 | * item 4.15}
73 | *
74 | * @since 0.7
75 | *
76 | * @param int $sales_point Sales point to ask for last voucher
77 | * @param int $type Voucher type to ask for last voucher
78 | *
79 | * @return int
80 | **/
81 | public function GetLastVoucher($sales_point, $type)
82 | {
83 | $req = array(
84 | 'PtoVta' => $sales_point,
85 | 'CbteTipo' => $type
86 | );
87 |
88 | return $this->ExecuteRequest('FECompUltimoAutorizado', $req)->CbteNro;
89 | }
90 |
91 | /**
92 | * Create a voucher from AFIP
93 | *
94 | * Send to AFIP servers request for create a voucher and assign
95 | * CAE to them {@see WS Specification item 4.1}
96 | *
97 | * @since 0.7
98 | *
99 | * @param array $data Voucher parameters {@see WS Specification
100 | * item 4.1.3}, some arrays were simplified for easy use {@example
101 | * examples/CreateVoucher.php Example with all allowed
102 | * attributes}
103 | * @param bool $return_response if is TRUE returns complete response
104 | * from AFIP
105 | *
106 | * @return array if $return_response is set to FALSE returns
107 | * [CAE => CAE assigned to voucher, CAEFchVto => Expiration date
108 | * for CAE (yyyy-mm-dd)] else returns complete response from
109 | * AFIP {@see WS Specification item 4.1.3}
110 | **/
111 | public function CreateVoucher($data, $return_response = FALSE)
112 | {
113 | $req = array(
114 | 'FeCAEReq' => array(
115 | 'FeCabReq' => array(
116 | 'CantReg' => $data['CbteHasta']-$data['CbteDesde']+1,
117 | 'PtoVta' => $data['PtoVta'],
118 | 'CbteTipo' => $data['CbteTipo']
119 | ),
120 | 'FeDetReq' => array(
121 | 'FECAEDetRequest' => &$data
122 | )
123 | )
124 | );
125 |
126 | unset($data['CantReg']);
127 | unset($data['PtoVta']);
128 | unset($data['CbteTipo']);
129 |
130 | if (isset($data['Tributos']))
131 | $data['Tributos'] = array('Tributo' => $data['Tributos']);
132 |
133 | if (isset($data['Compradores']))
134 | $data['Compradores'] = array('Comprador' => $data['Compradores']);
135 |
136 | if (isset($data['CbtesAsoc']))
137 | $data['CbtesAsoc'] = array('CbteAsoc' => $data['CbtesAsoc']);
138 |
139 | if (isset($data['Iva']))
140 | $data['Iva'] = array('AlicIva' => $data['Iva']);
141 |
142 | if (isset($data['Opcionales']))
143 | $data['Opcionales'] = array('Opcional' => $data['Opcionales']);
144 |
145 | $results = $this->ExecuteRequest('FECAESolicitar', $req);
146 |
147 | if ($return_response === TRUE) {
148 | return $results;
149 | }
150 | else{
151 | return array(
152 | 'CAE' => $results->FeDetResp->FECAEDetResponse->CAE,
153 | 'CAEFchVto' => $this->FormatDate($results->FeDetResp->FECAEDetResponse->CAEFchVto),
154 | );
155 | }
156 | }
157 |
158 | /**
159 | * Create next voucher from AFIP
160 | *
161 | * This method combines Afip::GetLastVoucher and Afip::CreateVoucher
162 | * for create the next voucher
163 | *
164 | * @since 0.7
165 | *
166 | * @param array $data Same to $data in Afip::CreateVoucher except that
167 | * don't need CbteDesde and CbteHasta attributes
168 | *
169 | * @param bool $return_response if is TRUE returns complete response
170 | * from AFIP
171 | *
172 | * @return array if $return_response is set to false returns
173 | * [CAE => CAE assigned to voucher, CAEFchVto => Expiration
174 | * date for CAE (yyyy-mm-dd), voucher_number => Number assigned to
175 | * voucher] else returns the complete response (same as in method CreateVoucher)
176 | * and the voucher_number
177 | **/
178 | public function CreateNextVoucher($data, $return_response = FALSE)
179 | {
180 | $last_voucher = $this->GetLastVoucher($data['PtoVta'], $data['CbteTipo']);
181 |
182 | $voucher_number = $last_voucher+1;
183 |
184 | $data['CbteDesde'] = $voucher_number;
185 | $data['CbteHasta'] = $voucher_number;
186 |
187 | $res = $this->CreateVoucher($data, $return_response);
188 | $res['voucher_number'] = $voucher_number;
189 |
190 | return $res;
191 | }
192 |
193 | /**
194 | * Get complete voucher information
195 | *
196 | * Asks to AFIP servers for complete information of voucher {@see WS
197 | * Specification item 4.19}
198 | *
199 | * @since 0.7
200 | *
201 | * @param int $number Number of voucher to get information
202 | * @param int $sales_point Sales point of voucher to get information
203 | * @param int $type Type of voucher to get information
204 | *
205 | * @return array|null returns array with complete voucher information
206 | * {@see WS Specification item 4.19} or null if there not exists
207 | **/
208 | public function GetVoucherInfo($number, $sales_point, $type)
209 | {
210 | $req = array(
211 | 'FeCompConsReq' => array(
212 | 'CbteNro' => $number,
213 | 'PtoVta' => $sales_point,
214 | 'CbteTipo' => $type
215 | )
216 | );
217 |
218 | try {
219 | $result = $this->ExecuteRequest('FECompConsultar', $req);
220 | } catch (Exception $e) {
221 | if ($e->getCode() == 602)
222 | return NULL;
223 | else
224 | throw $e;
225 | }
226 |
227 | return $result->ResultGet;
228 | }
229 |
230 | /**
231 | * Create CAEA
232 | *
233 | * Send a request to AFIP servers to create a CAEA
234 | *
235 | * @param int $period Time period
236 | * @param int $fortnight Monthly fortnight (1 or 2)
237 | **/
238 | public function CreateCAEA($period, $fortnight)
239 | {
240 | $req = array(
241 | 'Periodo' => $period,
242 | 'Orden' => $fortnight
243 | );
244 |
245 | return $this->ExecuteRequest('FECAEASolicitar', $req)->ResultGet;
246 | }
247 |
248 | /**
249 | * Get CAEA
250 | *
251 | * Ask to AFIP servers for a CAEA information
252 | *
253 | * @param int $period Time period
254 | * @param int $fortnight Monthly fortnight (1 or 2)
255 | **/
256 | public function GetCAEA($period, $fortnight)
257 | {
258 | $req = array(
259 | 'Periodo' => $period,
260 | 'Orden' => $fortnight
261 | );
262 |
263 | return $this->ExecuteRequest('FECAEAConsultar', $req)->ResultGet;
264 | }
265 |
266 | /**
267 | * Asks to AFIP Servers for sales points availables {@see WS
268 | * Specification item 4.11}
269 | *
270 | * @return array All sales points availables
271 | **/
272 | public function GetSalesPoints()
273 | {
274 | return $this->ExecuteRequest('FEParamGetPtosVenta')->ResultGet->PtoVenta;
275 | }
276 |
277 | /**
278 | * Asks to AFIP Servers for voucher types availables {@see WS
279 | * Specification item 4.4}
280 | *
281 | * @since 0.7
282 | *
283 | * @return array All voucher types availables
284 | **/
285 | public function GetVoucherTypes()
286 | {
287 | return $this->ExecuteRequest('FEParamGetTiposCbte')->ResultGet->CbteTipo;
288 | }
289 |
290 | /**
291 | * Asks to AFIP Servers for voucher concepts availables {@see WS
292 | * Specification item 4.5}
293 | *
294 | * @since 0.7
295 | *
296 | * @return array All voucher concepts availables
297 | **/
298 | public function GetConceptTypes()
299 | {
300 | return $this->ExecuteRequest('FEParamGetTiposConcepto')->ResultGet->ConceptoTipo;
301 | }
302 |
303 | /**
304 | * Asks to AFIP Servers for document types availables {@see WS
305 | * Specification item 4.6}
306 | *
307 | * @since 0.7
308 | *
309 | * @return array All document types availables
310 | **/
311 | public function GetDocumentTypes()
312 | {
313 | return $this->ExecuteRequest('FEParamGetTiposDoc')->ResultGet->DocTipo;
314 | }
315 |
316 | /**
317 | * Asks to AFIP Servers for aliquot availables {@see WS
318 | * Specification item 4.7}
319 | *
320 | * @since 0.7
321 | *
322 | * @return array All aliquot availables
323 | **/
324 | public function GetAliquotTypes()
325 | {
326 | return $this->ExecuteRequest('FEParamGetTiposIva')->ResultGet->IvaTipo;
327 | }
328 |
329 | /**
330 | * Asks to AFIP Servers for currencies availables {@see WS
331 | * Specification item 4.8}
332 | *
333 | * @since 0.7
334 | *
335 | * @return array All currencies availables
336 | **/
337 | public function GetCurrenciesTypes()
338 | {
339 | return $this->ExecuteRequest('FEParamGetTiposMonedas')->ResultGet->Moneda;
340 | }
341 |
342 | /**
343 | * Asks to AFIP Servers for voucher optional data available {@see WS
344 | * Specification item 4.9}
345 | *
346 | * @since 0.7
347 | *
348 | * @return array All voucher optional data available
349 | **/
350 | public function GetOptionsTypes()
351 | {
352 | return $this->ExecuteRequest('FEParamGetTiposOpcional')->ResultGet->OpcionalTipo;
353 | }
354 |
355 | /**
356 | * Asks to AFIP Servers for tax availables {@see WS
357 | * Specification item 4.10}
358 | *
359 | * @since 0.7
360 | *
361 | * @return array All tax availables
362 | **/
363 | public function GetTaxTypes()
364 | {
365 | return $this->ExecuteRequest('FEParamGetTiposTributos')->ResultGet->TributoTipo;
366 | }
367 |
368 | /**
369 | * Asks to web service for servers status {@see WS
370 | * Specification item 4.14}
371 | *
372 | * @since 0.7
373 | *
374 | * @return object { AppServer => Web Service status,
375 | * DbServer => Database status, AuthServer => Autentication
376 | * server status}
377 | **/
378 | public function GetServerStatus()
379 | {
380 | return $this->ExecuteRequest('FEDummy');
381 | }
382 |
383 | /**
384 | * Change date from AFIP used format (yyyymmdd) to yyyy-mm-dd
385 | *
386 | * @since 0.7
387 | *
388 | * @param string|int date to format
389 | *
390 | * @return string date in format yyyy-mm-dd
391 | **/
392 | public function FormatDate($date)
393 | {
394 | return date_format(DateTime::CreateFromFormat('Ymd', $date.''), 'Y-m-d');
395 | }
396 |
397 | /**
398 | * Sends request to AFIP servers
399 | *
400 | * @since 0.7
401 | *
402 | * @param string $operation SOAP operation to do
403 | * @param array $params Parameters to send
404 | *
405 | * @return mixed Operation results
406 | **/
407 | public function ExecuteRequest($operation, $params = array())
408 | {
409 | $this->options = array('service' => 'wsfe');
410 |
411 | $params = array_replace($this->GetWSInitialRequest($operation), $params);
412 |
413 | $results = parent::ExecuteRequest($operation, $params);
414 |
415 | $this->_CheckErrors($operation, $results);
416 |
417 | return $results->{$operation.'Result'};
418 | }
419 |
420 | /**
421 | * Make default request parameters for most of the operations
422 | *
423 | * @since 0.7
424 | *
425 | * @param string $operation SOAP Operation to do
426 | *
427 | * @return array Request parameters
428 | **/
429 | private function GetWSInitialRequest($operation)
430 | {
431 | if ($operation == 'FEDummy') {
432 | return array();
433 | }
434 |
435 | $ta = $this->afip->GetServiceTA('wsfe');
436 |
437 | return array(
438 | 'Auth' => array(
439 | 'Token' => $ta->token,
440 | 'Sign' => $ta->sign,
441 | 'Cuit' => $this->afip->CUIT
442 | )
443 | );
444 | }
445 |
446 | /**
447 | * Check if occurs an error on Web Service request
448 | *
449 | * @since 0.7
450 | *
451 | * @param string $operation SOAP operation to check
452 | * @param mixed $results AFIP response
453 | *
454 | * @throws Exception if exists an error in response
455 | *
456 | * @return void
457 | **/
458 | private function _CheckErrors($operation, $results)
459 | {
460 | $res = $results->{$operation.'Result'};
461 |
462 | if ($operation == 'FECAESolicitar' && isset($res->FeDetResp)) {
463 | if (is_array($res->FeDetResp->FECAEDetResponse)) {
464 | $res->FeDetResp->FECAEDetResponse = $res->FeDetResp->FECAEDetResponse[0];
465 | }
466 |
467 | if (isset($res->FeDetResp->FECAEDetResponse->Observaciones) && $res->FeDetResp->FECAEDetResponse->Resultado != 'A') {
468 | $res->Errors = new StdClass();
469 | $res->Errors->Err = $res->FeDetResp->FECAEDetResponse->Observaciones->Obs;
470 | }
471 | }
472 |
473 | if (isset($res->Errors)) {
474 | $err = is_array($res->Errors->Err) ? $res->Errors->Err[0] : $res->Errors->Err;
475 | throw new Exception('('.$err->Code.') '.$err->Msg, $err->Code);
476 | }
477 | }
478 |
479 | }
480 |
481 |
--------------------------------------------------------------------------------
/src/Class/RegisterInscriptionProof.php:
--------------------------------------------------------------------------------
1 | 'ws_sr_constancia_inscripcion'));
16 | }
17 |
18 | /**
19 | * Asks to web service for servers status {@see WS
20 | * Specification item 3.1}
21 | *
22 | * @since 1.0
23 | *
24 | * @return object { appserver => Web Service status,
25 | * dbserver => Database status, authserver => Autentication
26 | * server status}
27 | **/
28 | public function GetServerStatus()
29 | {
30 | return $this->ExecuteRequest('dummy');
31 | }
32 |
33 | /**
34 | * Asks to web service for taxpayer details {@see WS
35 | * Specification item 3.2}
36 | *
37 | * @since 1.0
38 | *
39 | * @throws Exception if exists an error in response
40 | *
41 | * @return object|null if taxpayer does not exists, return null,
42 | * if it exists, returns full response {@see
43 | * WS Specification item 3.2.2}
44 | **/
45 | public function GetTaxpayerDetails($identifier)
46 | {
47 | $ta = $this->afip->GetServiceTA('ws_sr_constancia_inscripcion');
48 |
49 | $params = array(
50 | 'token' => $ta->token,
51 | 'sign' => $ta->sign,
52 | 'cuitRepresentada' => $this->afip->CUIT,
53 | 'idPersona' => $identifier
54 | );
55 |
56 | try {
57 | return $this->ExecuteRequest('getPersona_v2', $params);
58 | } catch (Exception $e) {
59 | if (strpos($e->getMessage(), 'No existe') !== FALSE)
60 | return NULL;
61 | else
62 | throw $e;
63 | }
64 | }
65 |
66 | /**
67 | * Asks to web service for taxpayers details
68 | *
69 | * @throws Exception if exists an error in response
70 | *
71 | * @return [object] returns web service full response
72 | **/
73 | public function GetTaxpayersDetails($identifiers)
74 | {
75 | $ta = $this->afip->GetServiceTA('ws_sr_constancia_inscripcion');
76 |
77 | $params = array(
78 | 'token' => $ta->token,
79 | 'sign' => $ta->sign,
80 | 'cuitRepresentada' => $this->afip->CUIT,
81 | 'idPersona' => $identifiers
82 | );
83 |
84 | return $this->ExecuteRequest('getPersonaList_v2', $params)->persona;
85 | }
86 |
87 | /**
88 | * Sends request to AFIP servers
89 | *
90 | * @since 1.0
91 | *
92 | * @param string $operation SOAP operation to do
93 | * @param array $params Parameters to send
94 | *
95 | * @return mixed Operation results
96 | **/
97 | public function ExecuteRequest($operation, $params = array())
98 | {
99 | $this->options = array('service' => 'ws_sr_constancia_inscripcion');
100 |
101 | $results = parent::ExecuteRequest($operation, $params);
102 |
103 | return $results->{
104 | $operation === 'getPersona_v2' ? 'personaReturn' :
105 | ($operation === 'getPersonaList_v2' ? 'personaListReturn': 'return')
106 | };
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/src/Class/RegisterScopeFive.php:
--------------------------------------------------------------------------------
1 | 'ws_sr_padron_a5'));
22 | }
23 |
24 | /**
25 | * Asks to web service for servers status {@see WS
26 | * Specification item 3.1}
27 | *
28 | * @since 1.0
29 | *
30 | * @return object { appserver => Web Service status,
31 | * dbserver => Database status, authserver => Autentication
32 | * server status}
33 | **/
34 | public function GetServerStatus()
35 | {
36 | return $this->ExecuteRequest('dummy');
37 | }
38 |
39 | /**
40 | * Asks to web service for taxpayer details {@see WS
41 | * Specification item 3.2}
42 | *
43 | * @since 1.0
44 | *
45 | * @throws Exception if exists an error in response
46 | *
47 | * @return object|null if taxpayer does not exists, return null,
48 | * if it exists, returns full response {@see
49 | * WS Specification item 3.2.2}
50 | **/
51 | public function GetTaxpayerDetails($identifier)
52 | {
53 | $ta = $this->afip->GetServiceTA('ws_sr_padron_a5');
54 |
55 | $params = array(
56 | 'token' => $ta->token,
57 | 'sign' => $ta->sign,
58 | 'cuitRepresentada' => $this->afip->CUIT,
59 | 'idPersona' => $identifier
60 | );
61 |
62 | try {
63 | return $this->ExecuteRequest('getPersona_v2', $params);
64 | } catch (Exception $e) {
65 | if (strpos($e->getMessage(), 'No existe') !== FALSE)
66 | return NULL;
67 | else
68 | throw $e;
69 | }
70 | }
71 |
72 | /**
73 | * Asks to web service for taxpayers details
74 | *
75 | * @throws Exception if exists an error in response
76 | *
77 | * @return [object] returns web service full response
78 | **/
79 | public function GetTaxpayersDetails($identifiers)
80 | {
81 | $ta = $this->afip->GetServiceTA('ws_sr_padron_a5');
82 |
83 | $params = array(
84 | 'token' => $ta->token,
85 | 'sign' => $ta->sign,
86 | 'cuitRepresentada' => $this->afip->CUIT,
87 | 'idPersona' => $identifiers
88 | );
89 |
90 | return $this->ExecuteRequest('getPersonaList_v2', $params)->persona;
91 | }
92 |
93 | /**
94 | * Sends request to AFIP servers
95 | *
96 | * @since 1.0
97 | *
98 | * @param string $operation SOAP operation to do
99 | * @param array $params Parameters to send
100 | *
101 | * @return mixed Operation results
102 | **/
103 | public function ExecuteRequest($operation, $params = array())
104 | {
105 | $this->options = array('service' => 'ws_sr_padron_a5');
106 |
107 | $results = parent::ExecuteRequest($operation, $params);
108 |
109 | return $results->{
110 | $operation === 'getPersona_v2' ? 'personaReturn' :
111 | ($operation === 'getPersonaList_v2' ? 'personaListReturn': 'return')
112 | };
113 | }
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/src/Class/RegisterScopeFour.php:
--------------------------------------------------------------------------------
1 | 'ws_sr_padron_a4'));
22 | }
23 |
24 | /**
25 | * Asks to web service for servers status {@see WS
26 | * Specification item 3.1}
27 | *
28 | * @since 1.0
29 | *
30 | * @return object { appserver => Web Service status,
31 | * dbserver => Database status, authserver => Autentication
32 | * server status}
33 | **/
34 | public function GetServerStatus()
35 | {
36 | return $this->ExecuteRequest('dummy');
37 | }
38 |
39 | /**
40 | * Asks to web service for taxpayer details {@see WS
41 | * Specification item 3.2}
42 | *
43 | * @since 1.0
44 | *
45 | * @throws Exception if exists an error in response
46 | *
47 | * @return object|null if taxpayer does not exists, return null,
48 | * if it exists, returns persona property of response {@see
49 | * WS Specification item 3.2.2}
50 | **/
51 | public function GetTaxpayerDetails($identifier)
52 | {
53 | $ta = $this->afip->GetServiceTA('ws_sr_padron_a4');
54 |
55 | $params = array(
56 | 'token' => $ta->token,
57 | 'sign' => $ta->sign,
58 | 'cuitRepresentada' => $this->afip->CUIT,
59 | 'idPersona' => $identifier
60 | );
61 |
62 | try {
63 | return $this->ExecuteRequest('getPersona', $params)->persona;
64 | } catch (Exception $e) {
65 | if (strpos($e->getMessage(), 'No existe') !== FALSE)
66 | return NULL;
67 | else
68 | throw $e;
69 | }
70 | }
71 |
72 | /**
73 | * Sends request to AFIP servers
74 | *
75 | * @since 1.0
76 | *
77 | * @param string $operation SOAP operation to do
78 | * @param array $params Parameters to send
79 | *
80 | * @return mixed Operation results
81 | **/
82 | public function ExecuteRequest($operation, $params = array())
83 | {
84 | $this->options = array('service' => 'ws_sr_padron_a4');
85 |
86 | $results = parent::ExecuteRequest($operation, $params);
87 |
88 | return $results->{$operation == 'getPersona' ? 'personaReturn' : 'return'};
89 | }
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/src/Class/RegisterScopeTen.php:
--------------------------------------------------------------------------------
1 | 'ws_sr_padron_a10'));
22 | }
23 |
24 | /**
25 | * Asks to web service for servers status {@see WS
26 | * Specification item 3.1}
27 | *
28 | * @since 1.0
29 | *
30 | * @return object { appserver => Web Service status,
31 | * dbserver => Database status, authserver => Autentication
32 | * server status}
33 | **/
34 | public function GetServerStatus()
35 | {
36 | return $this->ExecuteRequest('dummy');
37 | }
38 |
39 | /**
40 | * Asks to web service for taxpayer details {@see WS
41 | * Specification item 3.2}
42 | *
43 | * @since 1.0
44 | *
45 | * @throws Exception if exists an error in response
46 | *
47 | * @return object|null if taxpayer does not exists, return null,
48 | * if it exists, returns persona property of response {@see
49 | * WS Specification item 3.2.2}
50 | **/
51 | public function GetTaxpayerDetails($identifier)
52 | {
53 | $ta = $this->afip->GetServiceTA('ws_sr_padron_a10');
54 |
55 | $params = array(
56 | 'token' => $ta->token,
57 | 'sign' => $ta->sign,
58 | 'cuitRepresentada' => $this->afip->CUIT,
59 | 'idPersona' => $identifier
60 | );
61 |
62 | try {
63 | return $this->ExecuteRequest('getPersona', $params)->persona;
64 | } catch (Exception $e) {
65 | if (strpos($e->getMessage(), 'No existe') !== FALSE)
66 | return NULL;
67 | else
68 | throw $e;
69 | }
70 | }
71 |
72 | /**
73 | * Sends request to AFIP servers
74 | *
75 | * @since 1.0
76 | *
77 | * @param string $operation SOAP operation to do
78 | * @param array $params Parameters to send
79 | *
80 | * @return mixed Operation results
81 | **/
82 | public function ExecuteRequest($operation, $params = array())
83 | {
84 | $this->options = array('service' => 'ws_sr_padron_a10');
85 |
86 | $results = parent::ExecuteRequest($operation, $params);
87 |
88 | return $results->{$operation == 'getPersona' ? 'personaReturn' : 'return'};
89 | }
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/src/Class/RegisterScopeThirteen.php:
--------------------------------------------------------------------------------
1 | 'ws_sr_padron_a13'));
22 | }
23 |
24 | /**
25 | * Asks to web service for servers status {@see WS
26 | * Specification item 3.1}
27 | *
28 | * @since 1.0
29 | *
30 | * @return object { appserver => Web Service status,
31 | * dbserver => Database status, authserver => Autentication
32 | * server status}
33 | **/
34 | public function GetServerStatus()
35 | {
36 | return $this->ExecuteRequest('dummy');
37 | }
38 |
39 | /**
40 | * Asks to web service for taxpayer details {@see WS
41 | * Specification item 3.2}
42 | *
43 | * @since 1.0
44 | *
45 | * @throws Exception if exists an error in response
46 | *
47 | * @return object|null if taxpayer does not exists, return null,
48 | * if it exists, returns persona property of response {@see
49 | * WS Specification item 3.2.2}
50 | **/
51 | public function GetTaxpayerDetails($identifier)
52 | {
53 | $ta = $this->afip->GetServiceTA('ws_sr_padron_a13');
54 |
55 | $params = array(
56 | 'token' => $ta->token,
57 | 'sign' => $ta->sign,
58 | 'cuitRepresentada' => $this->afip->CUIT,
59 | 'idPersona' => $identifier
60 | );
61 |
62 | try {
63 | return $this->ExecuteRequest('getPersona', $params)->persona;
64 | } catch (Exception $e) {
65 | if (strpos($e->getMessage(), 'No existe') !== FALSE)
66 | return NULL;
67 | else
68 | throw $e;
69 | }
70 | }
71 |
72 | /**
73 | * Asks to web service for tax id by document number
74 | *
75 | * @throws Exception if exists an error in response
76 | *
77 | * @return object|null if taxpayer does not exists, return null,
78 | * if it exists, returns idPersona property of response
79 | **/
80 | public function GetTaxIDByDocument($documentNumber)
81 | {
82 | $ta = $this->afip->GetServiceTA('ws_sr_padron_a13');
83 |
84 | $params = array(
85 | 'token' => $ta->token,
86 | 'sign' => $ta->sign,
87 | 'cuitRepresentada' => $this->afip->CUIT,
88 | 'documento' => $documentNumber
89 | );
90 |
91 | try {
92 | return $this->ExecuteRequest('getIdPersonaListByDocumento', $params)->idPersona;
93 | } catch (Exception $e) {
94 | if (strpos($e->getMessage(), 'No existe') !== FALSE)
95 | return NULL;
96 | else
97 | throw $e;
98 | }
99 | }
100 |
101 | /**
102 | * Sends request to AFIP servers
103 | *
104 | * @since 1.0
105 | *
106 | * @param string $operation SOAP operation to do
107 | * @param array $params Parameters to send
108 | *
109 | * @return mixed Operation results
110 | **/
111 | public function ExecuteRequest($operation, $params = array())
112 | {
113 | $this->options = array('service' => 'ws_sr_padron_a13');
114 |
115 | $results = parent::ExecuteRequest($operation, $params);
116 |
117 | return $results->{$operation == 'getPersona' ? 'personaReturn' :
118 | ($operation == 'getIdPersonaListByDocumento' ? 'idPersonaListReturn': 'return')
119 | };
120 | }
121 | }
122 |
123 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Auth.php:
--------------------------------------------------------------------------------
1 | user, $this->pass) = $args;
46 | }
47 | }
48 |
49 | /**
50 | * Register the necessary callbacks
51 | *
52 | * @see curl_before_send
53 | * @see fsockopen_header
54 | * @param Requests_Hooks $hooks Hook system
55 | */
56 | public function register(Requests_Hooks $hooks) {
57 | $hooks->register('curl.before_send', array($this, 'curl_before_send'));
58 | $hooks->register('fsockopen.after_headers', array($this, 'fsockopen_header'));
59 | }
60 |
61 | /**
62 | * Set cURL parameters before the data is sent
63 | *
64 | * @param resource $handle cURL resource
65 | */
66 | public function curl_before_send(&$handle) {
67 | curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
68 | curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString());
69 | }
70 |
71 | /**
72 | * Add extra headers to the request before sending
73 | *
74 | * @param string $out HTTP header string
75 | */
76 | public function fsockopen_header(&$out) {
77 | $out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString()));
78 | }
79 |
80 | /**
81 | * Get the authentication string (user:pass)
82 | *
83 | * @return string
84 | */
85 | public function getAuthString() {
86 | return $this->user . ':' . $this->pass;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Cookie.php:
--------------------------------------------------------------------------------
1 | name = $name;
69 | $this->value = $value;
70 | $this->attributes = $attributes;
71 | $default_flags = array(
72 | 'creation' => time(),
73 | 'last-access' => time(),
74 | 'persistent' => false,
75 | 'host-only' => true,
76 | );
77 | $this->flags = array_merge($default_flags, $flags);
78 |
79 | $this->reference_time = time();
80 | if ($reference_time !== null) {
81 | $this->reference_time = $reference_time;
82 | }
83 |
84 | $this->normalize();
85 | }
86 |
87 | /**
88 | * Check if a cookie is expired.
89 | *
90 | * Checks the age against $this->reference_time to determine if the cookie
91 | * is expired.
92 | *
93 | * @return boolean True if expired, false if time is valid.
94 | */
95 | public function is_expired() {
96 | // RFC6265, s. 4.1.2.2:
97 | // If a cookie has both the Max-Age and the Expires attribute, the Max-
98 | // Age attribute has precedence and controls the expiration date of the
99 | // cookie.
100 | if (isset($this->attributes['max-age'])) {
101 | $max_age = $this->attributes['max-age'];
102 | return $max_age < $this->reference_time;
103 | }
104 |
105 | if (isset($this->attributes['expires'])) {
106 | $expires = $this->attributes['expires'];
107 | return $expires < $this->reference_time;
108 | }
109 |
110 | return false;
111 | }
112 |
113 | /**
114 | * Check if a cookie is valid for a given URI
115 | *
116 | * @param Requests_IRI $uri URI to check
117 | * @return boolean Whether the cookie is valid for the given URI
118 | */
119 | public function uri_matches(Requests_IRI $uri) {
120 | if (!$this->domain_matches($uri->host)) {
121 | return false;
122 | }
123 |
124 | if (!$this->path_matches($uri->path)) {
125 | return false;
126 | }
127 |
128 | return empty($this->attributes['secure']) || $uri->scheme === 'https';
129 | }
130 |
131 | /**
132 | * Check if a cookie is valid for a given domain
133 | *
134 | * @param string $string Domain to check
135 | * @return boolean Whether the cookie is valid for the given domain
136 | */
137 | public function domain_matches($string) {
138 | if (!isset($this->attributes['domain'])) {
139 | // Cookies created manually; cookies created by Requests will set
140 | // the domain to the requested domain
141 | return true;
142 | }
143 |
144 | $domain_string = $this->attributes['domain'];
145 | if ($domain_string === $string) {
146 | // The domain string and the string are identical.
147 | return true;
148 | }
149 |
150 | // If the cookie is marked as host-only and we don't have an exact
151 | // match, reject the cookie
152 | if ($this->flags['host-only'] === true) {
153 | return false;
154 | }
155 |
156 | if (strlen($string) <= strlen($domain_string)) {
157 | // For obvious reasons, the string cannot be a suffix if the domain
158 | // is shorter than the domain string
159 | return false;
160 | }
161 |
162 | if (substr($string, -1 * strlen($domain_string)) !== $domain_string) {
163 | // The domain string should be a suffix of the string.
164 | return false;
165 | }
166 |
167 | $prefix = substr($string, 0, strlen($string) - strlen($domain_string));
168 | if (substr($prefix, -1) !== '.') {
169 | // The last character of the string that is not included in the
170 | // domain string should be a %x2E (".") character.
171 | return false;
172 | }
173 |
174 | // The string should be a host name (i.e., not an IP address).
175 | return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $string);
176 | }
177 |
178 | /**
179 | * Check if a cookie is valid for a given path
180 | *
181 | * From the path-match check in RFC 6265 section 5.1.4
182 | *
183 | * @param string $request_path Path to check
184 | * @return boolean Whether the cookie is valid for the given path
185 | */
186 | public function path_matches($request_path) {
187 | if (empty($request_path)) {
188 | // Normalize empty path to root
189 | $request_path = '/';
190 | }
191 |
192 | if (!isset($this->attributes['path'])) {
193 | // Cookies created manually; cookies created by Requests will set
194 | // the path to the requested path
195 | return true;
196 | }
197 |
198 | $cookie_path = $this->attributes['path'];
199 |
200 | if ($cookie_path === $request_path) {
201 | // The cookie-path and the request-path are identical.
202 | return true;
203 | }
204 |
205 | if (strlen($request_path) > strlen($cookie_path) && substr($request_path, 0, strlen($cookie_path)) === $cookie_path) {
206 | if (substr($cookie_path, -1) === '/') {
207 | // The cookie-path is a prefix of the request-path, and the last
208 | // character of the cookie-path is %x2F ("/").
209 | return true;
210 | }
211 |
212 | if (substr($request_path, strlen($cookie_path), 1) === '/') {
213 | // The cookie-path is a prefix of the request-path, and the
214 | // first character of the request-path that is not included in
215 | // the cookie-path is a %x2F ("/") character.
216 | return true;
217 | }
218 | }
219 |
220 | return false;
221 | }
222 |
223 | /**
224 | * Normalize cookie and attributes
225 | *
226 | * @return boolean Whether the cookie was successfully normalized
227 | */
228 | public function normalize() {
229 | foreach ($this->attributes as $key => $value) {
230 | $orig_value = $value;
231 | $value = $this->normalize_attribute($key, $value);
232 | if ($value === null) {
233 | unset($this->attributes[$key]);
234 | continue;
235 | }
236 |
237 | if ($value !== $orig_value) {
238 | $this->attributes[$key] = $value;
239 | }
240 | }
241 |
242 | return true;
243 | }
244 |
245 | /**
246 | * Parse an individual cookie attribute
247 | *
248 | * Handles parsing individual attributes from the cookie values.
249 | *
250 | * @param string $name Attribute name
251 | * @param string|boolean $value Attribute value (string value, or true if empty/flag)
252 | * @return mixed Value if available, or null if the attribute value is invalid (and should be skipped)
253 | */
254 | protected function normalize_attribute($name, $value) {
255 | switch (strtolower($name)) {
256 | case 'expires':
257 | // Expiration parsing, as per RFC 6265 section 5.2.1
258 | if (is_int($value)) {
259 | return $value;
260 | }
261 |
262 | $expiry_time = strtotime($value);
263 | if ($expiry_time === false) {
264 | return null;
265 | }
266 |
267 | return $expiry_time;
268 |
269 | case 'max-age':
270 | // Expiration parsing, as per RFC 6265 section 5.2.2
271 | if (is_int($value)) {
272 | return $value;
273 | }
274 |
275 | // Check that we have a valid age
276 | if (!preg_match('/^-?\d+$/', $value)) {
277 | return null;
278 | }
279 |
280 | $delta_seconds = (int) $value;
281 | if ($delta_seconds <= 0) {
282 | $expiry_time = 0;
283 | }
284 | else {
285 | $expiry_time = $this->reference_time + $delta_seconds;
286 | }
287 |
288 | return $expiry_time;
289 |
290 | case 'domain':
291 | // Domains are not required as per RFC 6265 section 5.2.3
292 | if (empty($value)) {
293 | return null;
294 | }
295 |
296 | // Domain normalization, as per RFC 6265 section 5.2.3
297 | if ($value[0] === '.') {
298 | $value = substr($value, 1);
299 | }
300 |
301 | return $value;
302 |
303 | default:
304 | return $value;
305 | }
306 | }
307 |
308 | /**
309 | * Format a cookie for a Cookie header
310 | *
311 | * This is used when sending cookies to a server.
312 | *
313 | * @return string Cookie formatted for Cookie header
314 | */
315 | public function format_for_header() {
316 | return sprintf('%s=%s', $this->name, $this->value);
317 | }
318 |
319 | /**
320 | * Format a cookie for a Cookie header
321 | *
322 | * @codeCoverageIgnore
323 | * @deprecated Use {@see Requests_Cookie::format_for_header}
324 | * @return string
325 | */
326 | public function formatForHeader() {
327 | return $this->format_for_header();
328 | }
329 |
330 | /**
331 | * Format a cookie for a Set-Cookie header
332 | *
333 | * This is used when sending cookies to clients. This isn't really
334 | * applicable to client-side usage, but might be handy for debugging.
335 | *
336 | * @return string Cookie formatted for Set-Cookie header
337 | */
338 | public function format_for_set_cookie() {
339 | $header_value = $this->format_for_header();
340 | if (!empty($this->attributes)) {
341 | $parts = array();
342 | foreach ($this->attributes as $key => $value) {
343 | // Ignore non-associative attributes
344 | if (is_numeric($key)) {
345 | $parts[] = $value;
346 | }
347 | else {
348 | $parts[] = sprintf('%s=%s', $key, $value);
349 | }
350 | }
351 |
352 | $header_value .= '; ' . implode('; ', $parts);
353 | }
354 | return $header_value;
355 | }
356 |
357 | /**
358 | * Format a cookie for a Set-Cookie header
359 | *
360 | * @codeCoverageIgnore
361 | * @deprecated Use {@see Requests_Cookie::format_for_set_cookie}
362 | * @return string
363 | */
364 | public function formatForSetCookie() {
365 | return $this->format_for_set_cookie();
366 | }
367 |
368 | /**
369 | * Get the cookie value
370 | *
371 | * Attributes and other data can be accessed via methods.
372 | */
373 | public function __toString() {
374 | return $this->value;
375 | }
376 |
377 | /**
378 | * Parse a cookie string into a cookie object
379 | *
380 | * Based on Mozilla's parsing code in Firefox and related projects, which
381 | * is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265
382 | * specifies some of this handling, but not in a thorough manner.
383 | *
384 | * @param string Cookie header value (from a Set-Cookie header)
385 | * @return Requests_Cookie Parsed cookie object
386 | */
387 | public static function parse($string, $name = '', $reference_time = null) {
388 | $parts = explode(';', $string);
389 | $kvparts = array_shift($parts);
390 |
391 | if (!empty($name)) {
392 | $value = $string;
393 | }
394 | elseif (strpos($kvparts, '=') === false) {
395 | // Some sites might only have a value without the equals separator.
396 | // Deviate from RFC 6265 and pretend it was actually a blank name
397 | // (`=foo`)
398 | //
399 | // https://bugzilla.mozilla.org/show_bug.cgi?id=169091
400 | $name = '';
401 | $value = $kvparts;
402 | }
403 | else {
404 | list($name, $value) = explode('=', $kvparts, 2);
405 | }
406 | $name = trim($name);
407 | $value = trim($value);
408 |
409 | // Attribute key are handled case-insensitively
410 | $attributes = new Requests_Utility_CaseInsensitiveDictionary();
411 |
412 | if (!empty($parts)) {
413 | foreach ($parts as $part) {
414 | if (strpos($part, '=') === false) {
415 | $part_key = $part;
416 | $part_value = true;
417 | }
418 | else {
419 | list($part_key, $part_value) = explode('=', $part, 2);
420 | $part_value = trim($part_value);
421 | }
422 |
423 | $part_key = trim($part_key);
424 | $attributes[$part_key] = $part_value;
425 | }
426 | }
427 |
428 | return new Requests_Cookie($name, $value, $attributes, array(), $reference_time);
429 | }
430 |
431 | /**
432 | * Parse all Set-Cookie headers from request headers
433 | *
434 | * @param Requests_Response_Headers $headers Headers to parse from
435 | * @param Requests_IRI|null $origin URI for comparing cookie origins
436 | * @param int|null $time Reference time for expiration calculation
437 | * @return array
438 | */
439 | public static function parse_from_headers(Requests_Response_Headers $headers, Requests_IRI $origin = null, $time = null) {
440 | $cookie_headers = $headers->getValues('Set-Cookie');
441 | if (empty($cookie_headers)) {
442 | return array();
443 | }
444 |
445 | $cookies = array();
446 | foreach ($cookie_headers as $header) {
447 | $parsed = self::parse($header, '', $time);
448 |
449 | // Default domain/path attributes
450 | if (empty($parsed->attributes['domain']) && !empty($origin)) {
451 | $parsed->attributes['domain'] = $origin->host;
452 | $parsed->flags['host-only'] = true;
453 | }
454 | else {
455 | $parsed->flags['host-only'] = false;
456 | }
457 |
458 | $path_is_valid = (!empty($parsed->attributes['path']) && $parsed->attributes['path'][0] === '/');
459 | if (!$path_is_valid && !empty($origin)) {
460 | $path = $origin->path;
461 |
462 | // Default path normalization as per RFC 6265 section 5.1.4
463 | if (substr($path, 0, 1) !== '/') {
464 | // If the uri-path is empty or if the first character of
465 | // the uri-path is not a %x2F ("/") character, output
466 | // %x2F ("/") and skip the remaining steps.
467 | $path = '/';
468 | }
469 | elseif (substr_count($path, '/') === 1) {
470 | // If the uri-path contains no more than one %x2F ("/")
471 | // character, output %x2F ("/") and skip the remaining
472 | // step.
473 | $path = '/';
474 | }
475 | else {
476 | // Output the characters of the uri-path from the first
477 | // character up to, but not including, the right-most
478 | // %x2F ("/").
479 | $path = substr($path, 0, strrpos($path, '/'));
480 | }
481 | $parsed->attributes['path'] = $path;
482 | }
483 |
484 | // Reject invalid cookie domains
485 | if (!empty($origin) && !$parsed->domain_matches($origin->host)) {
486 | continue;
487 | }
488 |
489 | $cookies[$parsed->name] = $parsed;
490 | }
491 |
492 | return $cookies;
493 | }
494 |
495 | /**
496 | * Parse all Set-Cookie headers from request headers
497 | *
498 | * @codeCoverageIgnore
499 | * @deprecated Use {@see Requests_Cookie::parse_from_headers}
500 | * @return array
501 | */
502 | public static function parseFromHeaders(Requests_Response_Headers $headers) {
503 | return self::parse_from_headers($headers);
504 | }
505 | }
506 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Cookie/Jar.php:
--------------------------------------------------------------------------------
1 | cookies = $cookies;
30 | }
31 |
32 | /**
33 | * Normalise cookie data into a Requests_Cookie
34 | *
35 | * @param string|Requests_Cookie $cookie
36 | * @return Requests_Cookie
37 | */
38 | public function normalize_cookie($cookie, $key = null) {
39 | if ($cookie instanceof Requests_Cookie) {
40 | return $cookie;
41 | }
42 |
43 | return Requests_Cookie::parse($cookie, $key);
44 | }
45 |
46 | /**
47 | * Normalise cookie data into a Requests_Cookie
48 | *
49 | * @codeCoverageIgnore
50 | * @deprecated Use {@see Requests_Cookie_Jar::normalize_cookie}
51 | * @return Requests_Cookie
52 | */
53 | public function normalizeCookie($cookie, $key = null) {
54 | return $this->normalize_cookie($cookie, $key);
55 | }
56 |
57 | /**
58 | * Check if the given item exists
59 | *
60 | * @param string $key Item key
61 | * @return boolean Does the item exist?
62 | */
63 | #[\ReturnTypeWillChange]
64 | public function offsetExists($key) {
65 | return isset($this->cookies[$key]);
66 | }
67 |
68 | /**
69 | * Get the value for the item
70 | *
71 | * @param string $key Item key
72 | * @return string|null Item value (null if offsetExists is false)
73 | */
74 | #[\ReturnTypeWillChange]
75 | public function offsetGet($key) {
76 | if (!isset($this->cookies[$key])) {
77 | return null;
78 | }
79 |
80 | return $this->cookies[$key];
81 | }
82 |
83 | /**
84 | * Set the given item
85 | *
86 | * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
87 | *
88 | * @param string $key Item name
89 | * @param string $value Item value
90 | */
91 | #[\ReturnTypeWillChange]
92 | public function offsetSet($key, $value) {
93 | if ($key === null) {
94 | throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
95 | }
96 |
97 | $this->cookies[$key] = $value;
98 | }
99 |
100 | /**
101 | * Unset the given header
102 | *
103 | * @param string $key
104 | */
105 | #[\ReturnTypeWillChange]
106 | public function offsetUnset($key) {
107 | unset($this->cookies[$key]);
108 | }
109 |
110 | /**
111 | * Get an iterator for the data
112 | *
113 | * @return ArrayIterator
114 | */
115 | #[\ReturnTypeWillChange]
116 | public function getIterator() {
117 | return new ArrayIterator($this->cookies);
118 | }
119 |
120 | /**
121 | * Register the cookie handler with the request's hooking system
122 | *
123 | * @param Requests_Hooker $hooks Hooking system
124 | */
125 | public function register(Requests_Hooker $hooks) {
126 | $hooks->register('requests.before_request', array($this, 'before_request'));
127 | $hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check'));
128 | }
129 |
130 | /**
131 | * Add Cookie header to a request if we have any
132 | *
133 | * As per RFC 6265, cookies are separated by '; '
134 | *
135 | * @param string $url
136 | * @param array $headers
137 | * @param array $data
138 | * @param string $type
139 | * @param array $options
140 | */
141 | public function before_request($url, &$headers, &$data, &$type, &$options) {
142 | if (!$url instanceof Requests_IRI) {
143 | $url = new Requests_IRI($url);
144 | }
145 |
146 | if (!empty($this->cookies)) {
147 | $cookies = array();
148 | foreach ($this->cookies as $key => $cookie) {
149 | $cookie = $this->normalize_cookie($cookie, $key);
150 |
151 | // Skip expired cookies
152 | if ($cookie->is_expired()) {
153 | continue;
154 | }
155 |
156 | if ($cookie->domain_matches($url->host)) {
157 | $cookies[] = $cookie->format_for_header();
158 | }
159 | }
160 |
161 | $headers['Cookie'] = implode('; ', $cookies);
162 | }
163 | }
164 |
165 | /**
166 | * Parse all cookies from a response and attach them to the response
167 | *
168 | * @var Requests_Response $response
169 | */
170 | public function before_redirect_check(Requests_Response $return) {
171 | $url = $return->url;
172 | if (!$url instanceof Requests_IRI) {
173 | $url = new Requests_IRI($url);
174 | }
175 |
176 | $cookies = Requests_Cookie::parse_from_headers($return->headers, $url);
177 | $this->cookies = array_merge($this->cookies, $cookies);
178 | $return->cookies = $this;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Exception.php:
--------------------------------------------------------------------------------
1 | type = $type;
40 | $this->data = $data;
41 | }
42 |
43 | /**
44 | * Like {@see getCode()}, but a string code.
45 | *
46 | * @codeCoverageIgnore
47 | * @return string
48 | */
49 | public function getType() {
50 | return $this->type;
51 | }
52 |
53 | /**
54 | * Gives any relevant data
55 | *
56 | * @codeCoverageIgnore
57 | * @return mixed
58 | */
59 | public function getData() {
60 | return $this->data;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Exception/HTTP.php:
--------------------------------------------------------------------------------
1 | reason = $reason;
40 | }
41 |
42 | $message = sprintf('%d %s', $this->code, $this->reason);
43 | parent::__construct($message, 'httpresponse', $data, $this->code);
44 | }
45 |
46 | /**
47 | * Get the status message
48 | */
49 | public function getReason() {
50 | return $this->reason;
51 | }
52 |
53 | /**
54 | * Get the correct exception class for a given error code
55 | *
56 | * @param int|bool $code HTTP status code, or false if unavailable
57 | * @return string Exception class name to use
58 | */
59 | public static function get_class($code) {
60 | if (!$code) {
61 | return 'Requests_Exception_HTTP_Unknown';
62 | }
63 |
64 | $class = sprintf('Requests_Exception_HTTP_%d', $code);
65 | if (class_exists($class)) {
66 | return $class;
67 | }
68 |
69 | return 'Requests_Exception_HTTP_Unknown';
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Exception/HTTP/304.php:
--------------------------------------------------------------------------------
1 | code = $data->status_code;
40 | }
41 |
42 | parent::__construct($reason, $data);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Exception/Transport.php:
--------------------------------------------------------------------------------
1 | type = $type;
35 | }
36 |
37 | if ($code !== null) {
38 | $this->code = $code;
39 | }
40 |
41 | if ($message !== null) {
42 | $this->reason = $message;
43 | }
44 |
45 | $message = sprintf('%d %s', $this->code, $this->reason);
46 | parent::__construct($message, $this->type, $data, $this->code);
47 | }
48 |
49 | /**
50 | * Get the error message
51 | */
52 | public function getReason() {
53 | return $this->reason;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Hooker.php:
--------------------------------------------------------------------------------
1 | 0 is executed later
22 | */
23 | public function register($hook, $callback, $priority = 0);
24 |
25 | /**
26 | * Dispatch a message
27 | *
28 | * @param string $hook Hook name
29 | * @param array $parameters Parameters to pass to callbacks
30 | * @return boolean Successfulness
31 | */
32 | public function dispatch($hook, $parameters = array());
33 | }
34 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Hooks.php:
--------------------------------------------------------------------------------
1 | 0 is executed later
36 | */
37 | public function register($hook, $callback, $priority = 0) {
38 | if (!isset($this->hooks[$hook])) {
39 | $this->hooks[$hook] = array();
40 | }
41 | if (!isset($this->hooks[$hook][$priority])) {
42 | $this->hooks[$hook][$priority] = array();
43 | }
44 |
45 | $this->hooks[$hook][$priority][] = $callback;
46 | }
47 |
48 | /**
49 | * Dispatch a message
50 | *
51 | * @param string $hook Hook name
52 | * @param array $parameters Parameters to pass to callbacks
53 | * @return boolean Successfulness
54 | */
55 | public function dispatch($hook, $parameters = array()) {
56 | if (empty($this->hooks[$hook])) {
57 | return false;
58 | }
59 |
60 | foreach ($this->hooks[$hook] as $priority => $hooked) {
61 | foreach ($hooked as $callback) {
62 | call_user_func_array($callback, $parameters);
63 | }
64 | }
65 |
66 | return true;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/IDNAEncoder.php:
--------------------------------------------------------------------------------
1 | 0) {
178 | if ($position + $length > $strlen) {
179 | throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
180 | }
181 | for ($position++; $remaining > 0; $position++) {
182 | $value = ord($input[$position]);
183 |
184 | // If it is invalid, count the sequence as invalid and reprocess the current byte:
185 | if (($value & 0xC0) !== 0x80) {
186 | throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
187 | }
188 |
189 | --$remaining;
190 | $character |= ($value & 0x3F) << ($remaining * 6);
191 | }
192 | $position--;
193 | }
194 |
195 | if (// Non-shortest form sequences are invalid
196 | $length > 1 && $character <= 0x7F
197 | || $length > 2 && $character <= 0x7FF
198 | || $length > 3 && $character <= 0xFFFF
199 | // Outside of range of ucschar codepoints
200 | // Noncharacters
201 | || ($character & 0xFFFE) === 0xFFFE
202 | || $character >= 0xFDD0 && $character <= 0xFDEF
203 | || (
204 | // Everything else not in ucschar
205 | $character > 0xD7FF && $character < 0xF900
206 | || $character < 0x20
207 | || $character > 0x7E && $character < 0xA0
208 | || $character > 0xEFFFD
209 | )
210 | ) {
211 | throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
212 | }
213 |
214 | $codepoints[] = $character;
215 | }
216 |
217 | return $codepoints;
218 | }
219 |
220 | /**
221 | * RFC3492-compliant encoder
222 | *
223 | * @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code
224 | * @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
225 | *
226 | * @param string $input UTF-8 encoded string to encode
227 | * @return string Punycode-encoded string
228 | */
229 | public static function punycode_encode($input) {
230 | $output = '';
231 | // let n = initial_n
232 | $n = self::BOOTSTRAP_INITIAL_N;
233 | // let delta = 0
234 | $delta = 0;
235 | // let bias = initial_bias
236 | $bias = self::BOOTSTRAP_INITIAL_BIAS;
237 | // let h = b = the number of basic code points in the input
238 | $h = 0;
239 | $b = 0; // see loop
240 | // copy them to the output in order
241 | $codepoints = self::utf8_to_codepoints($input);
242 | $extended = array();
243 |
244 | foreach ($codepoints as $char) {
245 | if ($char < 128) {
246 | // Character is valid ASCII
247 | // TODO: this should also check if it's valid for a URL
248 | $output .= chr($char);
249 | $h++;
250 | }
251 | // Check if the character is non-ASCII, but below initial n
252 | // This never occurs for Punycode, so ignore in coverage
253 | // @codeCoverageIgnoreStart
254 | elseif ($char < $n) {
255 | throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char);
256 | }
257 | // @codeCoverageIgnoreEnd
258 | else {
259 | $extended[$char] = true;
260 | }
261 | }
262 | $extended = array_keys($extended);
263 | sort($extended);
264 | $b = $h;
265 | // [copy them] followed by a delimiter if b > 0
266 | if (strlen($output) > 0) {
267 | $output .= '-';
268 | }
269 | // {if the input contains a non-basic code point < n then fail}
270 | // while h < length(input) do begin
271 | $codepointcount = count($codepoints);
272 | while ($h < $codepointcount) {
273 | // let m = the minimum code point >= n in the input
274 | $m = array_shift($extended);
275 | //printf('next code point to insert is %s' . PHP_EOL, dechex($m));
276 | // let delta = delta + (m - n) * (h + 1), fail on overflow
277 | $delta += ($m - $n) * ($h + 1);
278 | // let n = m
279 | $n = $m;
280 | // for each code point c in the input (in order) do begin
281 | for ($num = 0; $num < $codepointcount; $num++) {
282 | $c = $codepoints[$num];
283 | // if c < n then increment delta, fail on overflow
284 | if ($c < $n) {
285 | $delta++;
286 | }
287 | // if c == n then begin
288 | elseif ($c === $n) {
289 | // let q = delta
290 | $q = $delta;
291 | // for k = base to infinity in steps of base do begin
292 | for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) {
293 | // let t = tmin if k <= bias {+ tmin}, or
294 | // tmax if k >= bias + tmax, or k - bias otherwise
295 | if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
296 | $t = self::BOOTSTRAP_TMIN;
297 | }
298 | elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
299 | $t = self::BOOTSTRAP_TMAX;
300 | }
301 | else {
302 | $t = $k - $bias;
303 | }
304 | // if q < t then break
305 | if ($q < $t) {
306 | break;
307 | }
308 | // output the code point for digit t + ((q - t) mod (base - t))
309 | $digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t));
310 | $output .= self::digit_to_char($digit);
311 | // let q = (q - t) div (base - t)
312 | $q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t));
313 | } // end
314 | // output the code point for digit q
315 | $output .= self::digit_to_char($q);
316 | // let bias = adapt(delta, h + 1, test h equals b?)
317 | $bias = self::adapt($delta, $h + 1, $h === $b);
318 | // let delta = 0
319 | $delta = 0;
320 | // increment h
321 | $h++;
322 | } // end
323 | } // end
324 | // increment delta and n
325 | $delta++;
326 | $n++;
327 | } // end
328 |
329 | return $output;
330 | }
331 |
332 | /**
333 | * Convert a digit to its respective character
334 | *
335 | * @see https://tools.ietf.org/html/rfc3492#section-5
336 | * @throws Requests_Exception On invalid digit (`idna.invalid_digit`)
337 | *
338 | * @param int $digit Digit in the range 0-35
339 | * @return string Single character corresponding to digit
340 | */
341 | protected static function digit_to_char($digit) {
342 | // @codeCoverageIgnoreStart
343 | // As far as I know, this never happens, but still good to be sure.
344 | if ($digit < 0 || $digit > 35) {
345 | throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
346 | }
347 | // @codeCoverageIgnoreEnd
348 | $digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
349 | return substr($digits, $digit, 1);
350 | }
351 |
352 | /**
353 | * Adapt the bias
354 | *
355 | * @see https://tools.ietf.org/html/rfc3492#section-6.1
356 | * @param int $delta
357 | * @param int $numpoints
358 | * @param bool $firsttime
359 | * @return int New bias
360 | *
361 | * function adapt(delta,numpoints,firsttime):
362 | */
363 | protected static function adapt($delta, $numpoints, $firsttime) {
364 | // if firsttime then let delta = delta div damp
365 | if ($firsttime) {
366 | $delta = floor($delta / self::BOOTSTRAP_DAMP);
367 | }
368 | // else let delta = delta div 2
369 | else {
370 | $delta = floor($delta / 2);
371 | }
372 | // let delta = delta + (delta div numpoints)
373 | $delta += floor($delta / $numpoints);
374 | // let k = 0
375 | $k = 0;
376 | // while delta > ((base - tmin) * tmax) div 2 do begin
377 | $max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2);
378 | while ($delta > $max) {
379 | // let delta = delta div (base - tmin)
380 | $delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN));
381 | // let k = k + base
382 | $k += self::BOOTSTRAP_BASE;
383 | } // end
384 | // return k + (((base - tmin + 1) * delta) div (delta + skew))
385 | return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW));
386 | }
387 | }
388 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/IPv6.php:
--------------------------------------------------------------------------------
1 | FF01:0:0:0:0:0:0:101
27 | * ::1 -> 0:0:0:0:0:0:0:1
28 | *
29 | * @author Alexander Merz
30 | * @author elfrink at introweb dot nl
31 | * @author Josh Peck
32 | * @copyright 2003-2005 The PHP Group
33 | * @license http://www.opensource.org/licenses/bsd-license.php
34 | * @param string $ip An IPv6 address
35 | * @return string The uncompressed IPv6 address
36 | */
37 | public static function uncompress($ip) {
38 | if (substr_count($ip, '::') !== 1) {
39 | return $ip;
40 | }
41 |
42 | list($ip1, $ip2) = explode('::', $ip);
43 | $c1 = ($ip1 === '') ? -1 : substr_count($ip1, ':');
44 | $c2 = ($ip2 === '') ? -1 : substr_count($ip2, ':');
45 |
46 | if (strpos($ip2, '.') !== false) {
47 | $c2++;
48 | }
49 | // ::
50 | if ($c1 === -1 && $c2 === -1) {
51 | $ip = '0:0:0:0:0:0:0:0';
52 | }
53 | // ::xxx
54 | elseif ($c1 === -1) {
55 | $fill = str_repeat('0:', 7 - $c2);
56 | $ip = str_replace('::', $fill, $ip);
57 | }
58 | // xxx::
59 | elseif ($c2 === -1) {
60 | $fill = str_repeat(':0', 7 - $c1);
61 | $ip = str_replace('::', $fill, $ip);
62 | }
63 | // xxx::xxx
64 | else {
65 | $fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
66 | $ip = str_replace('::', $fill, $ip);
67 | }
68 | return $ip;
69 | }
70 |
71 | /**
72 | * Compresses an IPv6 address
73 | *
74 | * RFC 4291 allows you to compress consecutive zero pieces in an address to
75 | * '::'. This method expects a valid IPv6 address and compresses consecutive
76 | * zero pieces to '::'.
77 | *
78 | * Example: FF01:0:0:0:0:0:0:101 -> FF01::101
79 | * 0:0:0:0:0:0:0:1 -> ::1
80 | *
81 | * @see uncompress()
82 | * @param string $ip An IPv6 address
83 | * @return string The compressed IPv6 address
84 | */
85 | public static function compress($ip) {
86 | // Prepare the IP to be compressed
87 | $ip = self::uncompress($ip);
88 | $ip_parts = self::split_v6_v4($ip);
89 |
90 | // Replace all leading zeros
91 | $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
92 |
93 | // Find bunches of zeros
94 | if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
95 | $max = 0;
96 | $pos = null;
97 | foreach ($matches[0] as $match) {
98 | if (strlen($match[0]) > $max) {
99 | $max = strlen($match[0]);
100 | $pos = $match[1];
101 | }
102 | }
103 |
104 | $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
105 | }
106 |
107 | if ($ip_parts[1] !== '') {
108 | return implode(':', $ip_parts);
109 | }
110 | else {
111 | return $ip_parts[0];
112 | }
113 | }
114 |
115 | /**
116 | * Splits an IPv6 address into the IPv6 and IPv4 representation parts
117 | *
118 | * RFC 4291 allows you to represent the last two parts of an IPv6 address
119 | * using the standard IPv4 representation
120 | *
121 | * Example: 0:0:0:0:0:0:13.1.68.3
122 | * 0:0:0:0:0:FFFF:129.144.52.38
123 | *
124 | * @param string $ip An IPv6 address
125 | * @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part
126 | */
127 | protected static function split_v6_v4($ip) {
128 | if (strpos($ip, '.') !== false) {
129 | $pos = strrpos($ip, ':');
130 | $ipv6_part = substr($ip, 0, $pos);
131 | $ipv4_part = substr($ip, $pos + 1);
132 | return array($ipv6_part, $ipv4_part);
133 | }
134 | else {
135 | return array($ip, '');
136 | }
137 | }
138 |
139 | /**
140 | * Checks an IPv6 address
141 | *
142 | * Checks if the given IP is a valid IPv6 address
143 | *
144 | * @param string $ip An IPv6 address
145 | * @return bool true if $ip is a valid IPv6 address
146 | */
147 | public static function check_ipv6($ip) {
148 | $ip = self::uncompress($ip);
149 | list($ipv6, $ipv4) = self::split_v6_v4($ip);
150 | $ipv6 = explode(':', $ipv6);
151 | $ipv4 = explode('.', $ipv4);
152 | if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
153 | foreach ($ipv6 as $ipv6_part) {
154 | // The section can't be empty
155 | if ($ipv6_part === '') {
156 | return false;
157 | }
158 |
159 | // Nor can it be over four characters
160 | if (strlen($ipv6_part) > 4) {
161 | return false;
162 | }
163 |
164 | // Remove leading zeros (this is safe because of the above)
165 | $ipv6_part = ltrim($ipv6_part, '0');
166 | if ($ipv6_part === '') {
167 | $ipv6_part = '0';
168 | }
169 |
170 | // Check the value is valid
171 | $value = hexdec($ipv6_part);
172 | if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) {
173 | return false;
174 | }
175 | }
176 | if (count($ipv4) === 4) {
177 | foreach ($ipv4 as $ipv4_part) {
178 | $value = (int) $ipv4_part;
179 | if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
180 | return false;
181 | }
182 | }
183 | }
184 | return true;
185 | }
186 | else {
187 | return false;
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/IRI.php:
--------------------------------------------------------------------------------
1 | array(
126 | 'port' => 674
127 | ),
128 | 'dict' => array(
129 | 'port' => 2628
130 | ),
131 | 'file' => array(
132 | 'ihost' => 'localhost'
133 | ),
134 | 'http' => array(
135 | 'port' => 80,
136 | ),
137 | 'https' => array(
138 | 'port' => 443,
139 | ),
140 | );
141 |
142 | /**
143 | * Return the entire IRI when you try and read the object as a string
144 | *
145 | * @return string
146 | */
147 | public function __toString() {
148 | return $this->get_iri();
149 | }
150 |
151 | /**
152 | * Overload __set() to provide access via properties
153 | *
154 | * @param string $name Property name
155 | * @param mixed $value Property value
156 | */
157 | public function __set($name, $value) {
158 | if (method_exists($this, 'set_' . $name)) {
159 | call_user_func(array($this, 'set_' . $name), $value);
160 | }
161 | elseif (
162 | $name === 'iauthority'
163 | || $name === 'iuserinfo'
164 | || $name === 'ihost'
165 | || $name === 'ipath'
166 | || $name === 'iquery'
167 | || $name === 'ifragment'
168 | ) {
169 | call_user_func(array($this, 'set_' . substr($name, 1)), $value);
170 | }
171 | }
172 |
173 | /**
174 | * Overload __get() to provide access via properties
175 | *
176 | * @param string $name Property name
177 | * @return mixed
178 | */
179 | public function __get($name) {
180 | // isset() returns false for null, we don't want to do that
181 | // Also why we use array_key_exists below instead of isset()
182 | $props = get_object_vars($this);
183 |
184 | if (
185 | $name === 'iri' ||
186 | $name === 'uri' ||
187 | $name === 'iauthority' ||
188 | $name === 'authority'
189 | ) {
190 | $method = 'get_' . $name;
191 | $return = $this->$method();
192 | }
193 | elseif (array_key_exists($name, $props)) {
194 | $return = $this->$name;
195 | }
196 | // host -> ihost
197 | elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) {
198 | $name = $prop;
199 | $return = $this->$prop;
200 | }
201 | // ischeme -> scheme
202 | elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) {
203 | $name = $prop;
204 | $return = $this->$prop;
205 | }
206 | else {
207 | trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE);
208 | $return = null;
209 | }
210 |
211 | if ($return === null && isset($this->normalization[$this->scheme][$name])) {
212 | return $this->normalization[$this->scheme][$name];
213 | }
214 | else {
215 | return $return;
216 | }
217 | }
218 |
219 | /**
220 | * Overload __isset() to provide access via properties
221 | *
222 | * @param string $name Property name
223 | * @return bool
224 | */
225 | public function __isset($name) {
226 | return (method_exists($this, 'get_' . $name) || isset($this->$name));
227 | }
228 |
229 | /**
230 | * Overload __unset() to provide access via properties
231 | *
232 | * @param string $name Property name
233 | */
234 | public function __unset($name) {
235 | if (method_exists($this, 'set_' . $name)) {
236 | call_user_func(array($this, 'set_' . $name), '');
237 | }
238 | }
239 |
240 | /**
241 | * Create a new IRI object, from a specified string
242 | *
243 | * @param string|null $iri
244 | */
245 | public function __construct($iri = null) {
246 | $this->set_iri($iri);
247 | }
248 |
249 | /**
250 | * Create a new IRI object by resolving a relative IRI
251 | *
252 | * Returns false if $base is not absolute, otherwise an IRI.
253 | *
254 | * @param Requests_IRI|string $base (Absolute) Base IRI
255 | * @param Requests_IRI|string $relative Relative IRI
256 | * @return Requests_IRI|false
257 | */
258 | public static function absolutize($base, $relative) {
259 | if (!($relative instanceof Requests_IRI)) {
260 | $relative = new Requests_IRI($relative);
261 | }
262 | if (!$relative->is_valid()) {
263 | return false;
264 | }
265 | elseif ($relative->scheme !== null) {
266 | return clone $relative;
267 | }
268 |
269 | if (!($base instanceof Requests_IRI)) {
270 | $base = new Requests_IRI($base);
271 | }
272 | if ($base->scheme === null || !$base->is_valid()) {
273 | return false;
274 | }
275 |
276 | if ($relative->get_iri() !== '') {
277 | if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) {
278 | $target = clone $relative;
279 | $target->scheme = $base->scheme;
280 | }
281 | else {
282 | $target = new Requests_IRI;
283 | $target->scheme = $base->scheme;
284 | $target->iuserinfo = $base->iuserinfo;
285 | $target->ihost = $base->ihost;
286 | $target->port = $base->port;
287 | if ($relative->ipath !== '') {
288 | if ($relative->ipath[0] === '/') {
289 | $target->ipath = $relative->ipath;
290 | }
291 | elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') {
292 | $target->ipath = '/' . $relative->ipath;
293 | }
294 | elseif (($last_segment = strrpos($base->ipath, '/')) !== false) {
295 | $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath;
296 | }
297 | else {
298 | $target->ipath = $relative->ipath;
299 | }
300 | $target->ipath = $target->remove_dot_segments($target->ipath);
301 | $target->iquery = $relative->iquery;
302 | }
303 | else {
304 | $target->ipath = $base->ipath;
305 | if ($relative->iquery !== null) {
306 | $target->iquery = $relative->iquery;
307 | }
308 | elseif ($base->iquery !== null) {
309 | $target->iquery = $base->iquery;
310 | }
311 | }
312 | $target->ifragment = $relative->ifragment;
313 | }
314 | }
315 | else {
316 | $target = clone $base;
317 | $target->ifragment = null;
318 | }
319 | $target->scheme_normalization();
320 | return $target;
321 | }
322 |
323 | /**
324 | * Parse an IRI into scheme/authority/path/query/fragment segments
325 | *
326 | * @param string $iri
327 | * @return array
328 | */
329 | protected function parse_iri($iri) {
330 | $iri = trim($iri, "\x20\x09\x0A\x0C\x0D");
331 | $has_match = preg_match('/^((?P[^:\/?#]+):)?(\/\/(?P[^\/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?$/', $iri, $match);
332 | if (!$has_match) {
333 | throw new Requests_Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri);
334 | }
335 |
336 | if ($match[1] === '') {
337 | $match['scheme'] = null;
338 | }
339 | if (!isset($match[3]) || $match[3] === '') {
340 | $match['authority'] = null;
341 | }
342 | if (!isset($match[5])) {
343 | $match['path'] = '';
344 | }
345 | if (!isset($match[6]) || $match[6] === '') {
346 | $match['query'] = null;
347 | }
348 | if (!isset($match[8]) || $match[8] === '') {
349 | $match['fragment'] = null;
350 | }
351 | return $match;
352 | }
353 |
354 | /**
355 | * Remove dot segments from a path
356 | *
357 | * @param string $input
358 | * @return string
359 | */
360 | protected function remove_dot_segments($input) {
361 | $output = '';
362 | while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') {
363 | // A: If the input buffer begins with a prefix of "../" or "./",
364 | // then remove that prefix from the input buffer; otherwise,
365 | if (strpos($input, '../') === 0) {
366 | $input = substr($input, 3);
367 | }
368 | elseif (strpos($input, './') === 0) {
369 | $input = substr($input, 2);
370 | }
371 | // B: if the input buffer begins with a prefix of "/./" or "/.",
372 | // where "." is a complete path segment, then replace that prefix
373 | // with "/" in the input buffer; otherwise,
374 | elseif (strpos($input, '/./') === 0) {
375 | $input = substr($input, 2);
376 | }
377 | elseif ($input === '/.') {
378 | $input = '/';
379 | }
380 | // C: if the input buffer begins with a prefix of "/../" or "/..",
381 | // where ".." is a complete path segment, then replace that prefix
382 | // with "/" in the input buffer and remove the last segment and its
383 | // preceding "/" (if any) from the output buffer; otherwise,
384 | elseif (strpos($input, '/../') === 0) {
385 | $input = substr($input, 3);
386 | $output = substr_replace($output, '', strrpos($output, '/'));
387 | }
388 | elseif ($input === '/..') {
389 | $input = '/';
390 | $output = substr_replace($output, '', strrpos($output, '/'));
391 | }
392 | // D: if the input buffer consists only of "." or "..", then remove
393 | // that from the input buffer; otherwise,
394 | elseif ($input === '.' || $input === '..') {
395 | $input = '';
396 | }
397 | // E: move the first path segment in the input buffer to the end of
398 | // the output buffer, including the initial "/" character (if any)
399 | // and any subsequent characters up to, but not including, the next
400 | // "/" character or the end of the input buffer
401 | elseif (($pos = strpos($input, '/', 1)) !== false) {
402 | $output .= substr($input, 0, $pos);
403 | $input = substr_replace($input, '', 0, $pos);
404 | }
405 | else {
406 | $output .= $input;
407 | $input = '';
408 | }
409 | }
410 | return $output . $input;
411 | }
412 |
413 | /**
414 | * Replace invalid character with percent encoding
415 | *
416 | * @param string $string Input string
417 | * @param string $extra_chars Valid characters not in iunreserved or
418 | * iprivate (this is ASCII-only)
419 | * @param bool $iprivate Allow iprivate
420 | * @return string
421 | */
422 | protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) {
423 | // Normalize as many pct-encoded sections as possible
424 | $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string);
425 |
426 | // Replace invalid percent characters
427 | $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
428 |
429 | // Add unreserved and % to $extra_chars (the latter is safe because all
430 | // pct-encoded sections are now valid).
431 | $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
432 |
433 | // Now replace any bytes that aren't allowed with their pct-encoded versions
434 | $position = 0;
435 | $strlen = strlen($string);
436 | while (($position += strspn($string, $extra_chars, $position)) < $strlen) {
437 | $value = ord($string[$position]);
438 |
439 | // Start position
440 | $start = $position;
441 |
442 | // By default we are valid
443 | $valid = true;
444 |
445 | // No one byte sequences are valid due to the while.
446 | // Two byte sequence:
447 | if (($value & 0xE0) === 0xC0) {
448 | $character = ($value & 0x1F) << 6;
449 | $length = 2;
450 | $remaining = 1;
451 | }
452 | // Three byte sequence:
453 | elseif (($value & 0xF0) === 0xE0) {
454 | $character = ($value & 0x0F) << 12;
455 | $length = 3;
456 | $remaining = 2;
457 | }
458 | // Four byte sequence:
459 | elseif (($value & 0xF8) === 0xF0) {
460 | $character = ($value & 0x07) << 18;
461 | $length = 4;
462 | $remaining = 3;
463 | }
464 | // Invalid byte:
465 | else {
466 | $valid = false;
467 | $length = 1;
468 | $remaining = 0;
469 | }
470 |
471 | if ($remaining) {
472 | if ($position + $length <= $strlen) {
473 | for ($position++; $remaining; $position++) {
474 | $value = ord($string[$position]);
475 |
476 | // Check that the byte is valid, then add it to the character:
477 | if (($value & 0xC0) === 0x80) {
478 | $character |= ($value & 0x3F) << (--$remaining * 6);
479 | }
480 | // If it is invalid, count the sequence as invalid and reprocess the current byte:
481 | else {
482 | $valid = false;
483 | $position--;
484 | break;
485 | }
486 | }
487 | }
488 | else {
489 | $position = $strlen - 1;
490 | $valid = false;
491 | }
492 | }
493 |
494 | // Percent encode anything invalid or not in ucschar
495 | if (
496 | // Invalid sequences
497 | !$valid
498 | // Non-shortest form sequences are invalid
499 | || $length > 1 && $character <= 0x7F
500 | || $length > 2 && $character <= 0x7FF
501 | || $length > 3 && $character <= 0xFFFF
502 | // Outside of range of ucschar codepoints
503 | // Noncharacters
504 | || ($character & 0xFFFE) === 0xFFFE
505 | || $character >= 0xFDD0 && $character <= 0xFDEF
506 | || (
507 | // Everything else not in ucschar
508 | $character > 0xD7FF && $character < 0xF900
509 | || $character < 0xA0
510 | || $character > 0xEFFFD
511 | )
512 | && (
513 | // Everything not in iprivate, if it applies
514 | !$iprivate
515 | || $character < 0xE000
516 | || $character > 0x10FFFD
517 | )
518 | ) {
519 | // If we were a character, pretend we weren't, but rather an error.
520 | if ($valid) {
521 | $position--;
522 | }
523 |
524 | for ($j = $start; $j <= $position; $j++) {
525 | $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
526 | $j += 2;
527 | $position += 2;
528 | $strlen += 2;
529 | }
530 | }
531 | }
532 |
533 | return $string;
534 | }
535 |
536 | /**
537 | * Callback function for preg_replace_callback.
538 | *
539 | * Removes sequences of percent encoded bytes that represent UTF-8
540 | * encoded characters in iunreserved
541 | *
542 | * @param array $match PCRE match
543 | * @return string Replacement
544 | */
545 | protected function remove_iunreserved_percent_encoded($match) {
546 | // As we just have valid percent encoded sequences we can just explode
547 | // and ignore the first member of the returned array (an empty string).
548 | $bytes = explode('%', $match[0]);
549 |
550 | // Initialize the new string (this is what will be returned) and that
551 | // there are no bytes remaining in the current sequence (unsurprising
552 | // at the first byte!).
553 | $string = '';
554 | $remaining = 0;
555 |
556 | // Loop over each and every byte, and set $value to its value
557 | for ($i = 1, $len = count($bytes); $i < $len; $i++) {
558 | $value = hexdec($bytes[$i]);
559 |
560 | // If we're the first byte of sequence:
561 | if (!$remaining) {
562 | // Start position
563 | $start = $i;
564 |
565 | // By default we are valid
566 | $valid = true;
567 |
568 | // One byte sequence:
569 | if ($value <= 0x7F) {
570 | $character = $value;
571 | $length = 1;
572 | }
573 | // Two byte sequence:
574 | elseif (($value & 0xE0) === 0xC0) {
575 | $character = ($value & 0x1F) << 6;
576 | $length = 2;
577 | $remaining = 1;
578 | }
579 | // Three byte sequence:
580 | elseif (($value & 0xF0) === 0xE0) {
581 | $character = ($value & 0x0F) << 12;
582 | $length = 3;
583 | $remaining = 2;
584 | }
585 | // Four byte sequence:
586 | elseif (($value & 0xF8) === 0xF0) {
587 | $character = ($value & 0x07) << 18;
588 | $length = 4;
589 | $remaining = 3;
590 | }
591 | // Invalid byte:
592 | else {
593 | $valid = false;
594 | $remaining = 0;
595 | }
596 | }
597 | // Continuation byte:
598 | else {
599 | // Check that the byte is valid, then add it to the character:
600 | if (($value & 0xC0) === 0x80) {
601 | $remaining--;
602 | $character |= ($value & 0x3F) << ($remaining * 6);
603 | }
604 | // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
605 | else {
606 | $valid = false;
607 | $remaining = 0;
608 | $i--;
609 | }
610 | }
611 |
612 | // If we've reached the end of the current byte sequence, append it to Unicode::$data
613 | if (!$remaining) {
614 | // Percent encode anything invalid or not in iunreserved
615 | if (
616 | // Invalid sequences
617 | !$valid
618 | // Non-shortest form sequences are invalid
619 | || $length > 1 && $character <= 0x7F
620 | || $length > 2 && $character <= 0x7FF
621 | || $length > 3 && $character <= 0xFFFF
622 | // Outside of range of iunreserved codepoints
623 | || $character < 0x2D
624 | || $character > 0xEFFFD
625 | // Noncharacters
626 | || ($character & 0xFFFE) === 0xFFFE
627 | || $character >= 0xFDD0 && $character <= 0xFDEF
628 | // Everything else not in iunreserved (this is all BMP)
629 | || $character === 0x2F
630 | || $character > 0x39 && $character < 0x41
631 | || $character > 0x5A && $character < 0x61
632 | || $character > 0x7A && $character < 0x7E
633 | || $character > 0x7E && $character < 0xA0
634 | || $character > 0xD7FF && $character < 0xF900
635 | ) {
636 | for ($j = $start; $j <= $i; $j++) {
637 | $string .= '%' . strtoupper($bytes[$j]);
638 | }
639 | }
640 | else {
641 | for ($j = $start; $j <= $i; $j++) {
642 | $string .= chr(hexdec($bytes[$j]));
643 | }
644 | }
645 | }
646 | }
647 |
648 | // If we have any bytes left over they are invalid (i.e., we are
649 | // mid-way through a multi-byte sequence)
650 | if ($remaining) {
651 | for ($j = $start; $j < $len; $j++) {
652 | $string .= '%' . strtoupper($bytes[$j]);
653 | }
654 | }
655 |
656 | return $string;
657 | }
658 |
659 | protected function scheme_normalization() {
660 | if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) {
661 | $this->iuserinfo = null;
662 | }
663 | if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) {
664 | $this->ihost = null;
665 | }
666 | if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) {
667 | $this->port = null;
668 | }
669 | if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) {
670 | $this->ipath = '';
671 | }
672 | if (isset($this->ihost) && empty($this->ipath)) {
673 | $this->ipath = '/';
674 | }
675 | if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) {
676 | $this->iquery = null;
677 | }
678 | if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) {
679 | $this->ifragment = null;
680 | }
681 | }
682 |
683 | /**
684 | * Check if the object represents a valid IRI. This needs to be done on each
685 | * call as some things change depending on another part of the IRI.
686 | *
687 | * @return bool
688 | */
689 | public function is_valid() {
690 | $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null;
691 | if ($this->ipath !== '' &&
692 | (
693 | $isauthority && $this->ipath[0] !== '/' ||
694 | (
695 | $this->scheme === null &&
696 | !$isauthority &&
697 | strpos($this->ipath, ':') !== false &&
698 | (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/'))
699 | )
700 | )
701 | ) {
702 | return false;
703 | }
704 |
705 | return true;
706 | }
707 |
708 | /**
709 | * Set the entire IRI. Returns true on success, false on failure (if there
710 | * are any invalid characters).
711 | *
712 | * @param string $iri
713 | * @return bool
714 | */
715 | protected function set_iri($iri) {
716 | static $cache;
717 | if (!$cache) {
718 | $cache = array();
719 | }
720 |
721 | if ($iri === null) {
722 | return true;
723 | }
724 | if (isset($cache[$iri])) {
725 | list($this->scheme,
726 | $this->iuserinfo,
727 | $this->ihost,
728 | $this->port,
729 | $this->ipath,
730 | $this->iquery,
731 | $this->ifragment,
732 | $return) = $cache[$iri];
733 | return $return;
734 | }
735 |
736 | $parsed = $this->parse_iri((string) $iri);
737 |
738 | $return = $this->set_scheme($parsed['scheme'])
739 | && $this->set_authority($parsed['authority'])
740 | && $this->set_path($parsed['path'])
741 | && $this->set_query($parsed['query'])
742 | && $this->set_fragment($parsed['fragment']);
743 |
744 | $cache[$iri] = array($this->scheme,
745 | $this->iuserinfo,
746 | $this->ihost,
747 | $this->port,
748 | $this->ipath,
749 | $this->iquery,
750 | $this->ifragment,
751 | $return);
752 | return $return;
753 | }
754 |
755 | /**
756 | * Set the scheme. Returns true on success, false on failure (if there are
757 | * any invalid characters).
758 | *
759 | * @param string $scheme
760 | * @return bool
761 | */
762 | protected function set_scheme($scheme) {
763 | if ($scheme === null) {
764 | $this->scheme = null;
765 | }
766 | elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) {
767 | $this->scheme = null;
768 | return false;
769 | }
770 | else {
771 | $this->scheme = strtolower($scheme);
772 | }
773 | return true;
774 | }
775 |
776 | /**
777 | * Set the authority. Returns true on success, false on failure (if there are
778 | * any invalid characters).
779 | *
780 | * @param string $authority
781 | * @return bool
782 | */
783 | protected function set_authority($authority) {
784 | static $cache;
785 | if (!$cache) {
786 | $cache = array();
787 | }
788 |
789 | if ($authority === null) {
790 | $this->iuserinfo = null;
791 | $this->ihost = null;
792 | $this->port = null;
793 | return true;
794 | }
795 | if (isset($cache[$authority])) {
796 | list($this->iuserinfo,
797 | $this->ihost,
798 | $this->port,
799 | $return) = $cache[$authority];
800 |
801 | return $return;
802 | }
803 |
804 | $remaining = $authority;
805 | if (($iuserinfo_end = strrpos($remaining, '@')) !== false) {
806 | $iuserinfo = substr($remaining, 0, $iuserinfo_end);
807 | $remaining = substr($remaining, $iuserinfo_end + 1);
808 | }
809 | else {
810 | $iuserinfo = null;
811 | }
812 | if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) {
813 | $port = substr($remaining, $port_start + 1);
814 | if ($port === false || $port === '') {
815 | $port = null;
816 | }
817 | $remaining = substr($remaining, 0, $port_start);
818 | }
819 | else {
820 | $port = null;
821 | }
822 |
823 | $return = $this->set_userinfo($iuserinfo) &&
824 | $this->set_host($remaining) &&
825 | $this->set_port($port);
826 |
827 | $cache[$authority] = array($this->iuserinfo,
828 | $this->ihost,
829 | $this->port,
830 | $return);
831 |
832 | return $return;
833 | }
834 |
835 | /**
836 | * Set the iuserinfo.
837 | *
838 | * @param string $iuserinfo
839 | * @return bool
840 | */
841 | protected function set_userinfo($iuserinfo) {
842 | if ($iuserinfo === null) {
843 | $this->iuserinfo = null;
844 | }
845 | else {
846 | $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:');
847 | $this->scheme_normalization();
848 | }
849 |
850 | return true;
851 | }
852 |
853 | /**
854 | * Set the ihost. Returns true on success, false on failure (if there are
855 | * any invalid characters).
856 | *
857 | * @param string $ihost
858 | * @return bool
859 | */
860 | protected function set_host($ihost) {
861 | if ($ihost === null) {
862 | $this->ihost = null;
863 | return true;
864 | }
865 | if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') {
866 | if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) {
867 | $this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']';
868 | }
869 | else {
870 | $this->ihost = null;
871 | return false;
872 | }
873 | }
874 | else {
875 | $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;=');
876 |
877 | // Lowercase, but ignore pct-encoded sections (as they should
878 | // remain uppercase). This must be done after the previous step
879 | // as that can add unescaped characters.
880 | $position = 0;
881 | $strlen = strlen($ihost);
882 | while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) {
883 | if ($ihost[$position] === '%') {
884 | $position += 3;
885 | }
886 | else {
887 | $ihost[$position] = strtolower($ihost[$position]);
888 | $position++;
889 | }
890 | }
891 |
892 | $this->ihost = $ihost;
893 | }
894 |
895 | $this->scheme_normalization();
896 |
897 | return true;
898 | }
899 |
900 | /**
901 | * Set the port. Returns true on success, false on failure (if there are
902 | * any invalid characters).
903 | *
904 | * @param string $port
905 | * @return bool
906 | */
907 | protected function set_port($port) {
908 | if ($port === null) {
909 | $this->port = null;
910 | return true;
911 | }
912 |
913 | if (strspn($port, '0123456789') === strlen($port)) {
914 | $this->port = (int) $port;
915 | $this->scheme_normalization();
916 | return true;
917 | }
918 |
919 | $this->port = null;
920 | return false;
921 | }
922 |
923 | /**
924 | * Set the ipath.
925 | *
926 | * @param string $ipath
927 | * @return bool
928 | */
929 | protected function set_path($ipath) {
930 | static $cache;
931 | if (!$cache) {
932 | $cache = array();
933 | }
934 |
935 | $ipath = (string) $ipath;
936 |
937 | if (isset($cache[$ipath])) {
938 | $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)];
939 | }
940 | else {
941 | $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/');
942 | $removed = $this->remove_dot_segments($valid);
943 |
944 | $cache[$ipath] = array($valid, $removed);
945 | $this->ipath = ($this->scheme !== null) ? $removed : $valid;
946 | }
947 | $this->scheme_normalization();
948 | return true;
949 | }
950 |
951 | /**
952 | * Set the iquery.
953 | *
954 | * @param string $iquery
955 | * @return bool
956 | */
957 | protected function set_query($iquery) {
958 | if ($iquery === null) {
959 | $this->iquery = null;
960 | }
961 | else {
962 | $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true);
963 | $this->scheme_normalization();
964 | }
965 | return true;
966 | }
967 |
968 | /**
969 | * Set the ifragment.
970 | *
971 | * @param string $ifragment
972 | * @return bool
973 | */
974 | protected function set_fragment($ifragment) {
975 | if ($ifragment === null) {
976 | $this->ifragment = null;
977 | }
978 | else {
979 | $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?');
980 | $this->scheme_normalization();
981 | }
982 | return true;
983 | }
984 |
985 | /**
986 | * Convert an IRI to a URI (or parts thereof)
987 | *
988 | * @param string|bool IRI to convert (or false from {@see get_iri})
989 | * @return string|false URI if IRI is valid, false otherwise.
990 | */
991 | protected function to_uri($string) {
992 | if (!is_string($string)) {
993 | return false;
994 | }
995 |
996 | static $non_ascii;
997 | if (!$non_ascii) {
998 | $non_ascii = implode('', range("\x80", "\xFF"));
999 | }
1000 |
1001 | $position = 0;
1002 | $strlen = strlen($string);
1003 | while (($position += strcspn($string, $non_ascii, $position)) < $strlen) {
1004 | $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1);
1005 | $position += 3;
1006 | $strlen += 2;
1007 | }
1008 |
1009 | return $string;
1010 | }
1011 |
1012 | /**
1013 | * Get the complete IRI
1014 | *
1015 | * @return string|false
1016 | */
1017 | protected function get_iri() {
1018 | if (!$this->is_valid()) {
1019 | return false;
1020 | }
1021 |
1022 | $iri = '';
1023 | if ($this->scheme !== null) {
1024 | $iri .= $this->scheme . ':';
1025 | }
1026 | if (($iauthority = $this->get_iauthority()) !== null) {
1027 | $iri .= '//' . $iauthority;
1028 | }
1029 | $iri .= $this->ipath;
1030 | if ($this->iquery !== null) {
1031 | $iri .= '?' . $this->iquery;
1032 | }
1033 | if ($this->ifragment !== null) {
1034 | $iri .= '#' . $this->ifragment;
1035 | }
1036 |
1037 | return $iri;
1038 | }
1039 |
1040 | /**
1041 | * Get the complete URI
1042 | *
1043 | * @return string
1044 | */
1045 | protected function get_uri() {
1046 | return $this->to_uri($this->get_iri());
1047 | }
1048 |
1049 | /**
1050 | * Get the complete iauthority
1051 | *
1052 | * @return string|null
1053 | */
1054 | protected function get_iauthority() {
1055 | if ($this->iuserinfo === null && $this->ihost === null && $this->port === null) {
1056 | return null;
1057 | }
1058 |
1059 | $iauthority = '';
1060 | if ($this->iuserinfo !== null) {
1061 | $iauthority .= $this->iuserinfo . '@';
1062 | }
1063 | if ($this->ihost !== null) {
1064 | $iauthority .= $this->ihost;
1065 | }
1066 | if ($this->port !== null) {
1067 | $iauthority .= ':' . $this->port;
1068 | }
1069 | return $iauthority;
1070 | }
1071 |
1072 | /**
1073 | * Get the complete authority
1074 | *
1075 | * @return string
1076 | */
1077 | protected function get_authority() {
1078 | $iauthority = $this->get_iauthority();
1079 | if (is_string($iauthority)) {
1080 | return $this->to_uri($iauthority);
1081 | }
1082 | else {
1083 | return $iauthority;
1084 | }
1085 | }
1086 | }
1087 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Proxy.php:
--------------------------------------------------------------------------------
1 | proxy = $args;
60 | }
61 | elseif (is_array($args)) {
62 | if (count($args) === 1) {
63 | list($this->proxy) = $args;
64 | }
65 | elseif (count($args) === 3) {
66 | list($this->proxy, $this->user, $this->pass) = $args;
67 | $this->use_authentication = true;
68 | }
69 | else {
70 | throw new Requests_Exception('Invalid number of arguments', 'proxyhttpbadargs');
71 | }
72 | }
73 | }
74 |
75 | /**
76 | * Register the necessary callbacks
77 | *
78 | * @since 1.6
79 | * @see curl_before_send
80 | * @see fsockopen_remote_socket
81 | * @see fsockopen_remote_host_path
82 | * @see fsockopen_header
83 | * @param Requests_Hooks $hooks Hook system
84 | */
85 | public function register(Requests_Hooks $hooks) {
86 | $hooks->register('curl.before_send', array($this, 'curl_before_send'));
87 |
88 | $hooks->register('fsockopen.remote_socket', array($this, 'fsockopen_remote_socket'));
89 | $hooks->register('fsockopen.remote_host_path', array($this, 'fsockopen_remote_host_path'));
90 | if ($this->use_authentication) {
91 | $hooks->register('fsockopen.after_headers', array($this, 'fsockopen_header'));
92 | }
93 | }
94 |
95 | /**
96 | * Set cURL parameters before the data is sent
97 | *
98 | * @since 1.6
99 | * @param resource $handle cURL resource
100 | */
101 | public function curl_before_send(&$handle) {
102 | curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
103 | curl_setopt($handle, CURLOPT_PROXY, $this->proxy);
104 |
105 | if ($this->use_authentication) {
106 | curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
107 | curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string());
108 | }
109 | }
110 |
111 | /**
112 | * Alter remote socket information before opening socket connection
113 | *
114 | * @since 1.6
115 | * @param string $remote_socket Socket connection string
116 | */
117 | public function fsockopen_remote_socket(&$remote_socket) {
118 | $remote_socket = $this->proxy;
119 | }
120 |
121 | /**
122 | * Alter remote path before getting stream data
123 | *
124 | * @since 1.6
125 | * @param string $path Path to send in HTTP request string ("GET ...")
126 | * @param string $url Full URL we're requesting
127 | */
128 | public function fsockopen_remote_host_path(&$path, $url) {
129 | $path = $url;
130 | }
131 |
132 | /**
133 | * Add extra headers to the request before sending
134 | *
135 | * @since 1.6
136 | * @param string $out HTTP header string
137 | */
138 | public function fsockopen_header(&$out) {
139 | $out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string()));
140 | }
141 |
142 | /**
143 | * Get the authentication string (user:pass)
144 | *
145 | * @since 1.6
146 | * @return string
147 | */
148 | public function get_auth_string() {
149 | return $this->user . ':' . $this->pass;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Response.php:
--------------------------------------------------------------------------------
1 | headers = new Requests_Response_Headers();
21 | $this->cookies = new Requests_Cookie_Jar();
22 | }
23 |
24 | /**
25 | * Response body
26 | *
27 | * @var string
28 | */
29 | public $body = '';
30 |
31 | /**
32 | * Raw HTTP data from the transport
33 | *
34 | * @var string
35 | */
36 | public $raw = '';
37 |
38 | /**
39 | * Headers, as an associative array
40 | *
41 | * @var Requests_Response_Headers Array-like object representing headers
42 | */
43 | public $headers = array();
44 |
45 | /**
46 | * Status code, false if non-blocking
47 | *
48 | * @var integer|boolean
49 | */
50 | public $status_code = false;
51 |
52 | /**
53 | * Protocol version, false if non-blocking
54 | *
55 | * @var float|boolean
56 | */
57 | public $protocol_version = false;
58 |
59 | /**
60 | * Whether the request succeeded or not
61 | *
62 | * @var boolean
63 | */
64 | public $success = false;
65 |
66 | /**
67 | * Number of redirects the request used
68 | *
69 | * @var integer
70 | */
71 | public $redirects = 0;
72 |
73 | /**
74 | * URL requested
75 | *
76 | * @var string
77 | */
78 | public $url = '';
79 |
80 | /**
81 | * Previous requests (from redirects)
82 | *
83 | * @var array Array of Requests_Response objects
84 | */
85 | public $history = array();
86 |
87 | /**
88 | * Cookies from the request
89 | *
90 | * @var Requests_Cookie_Jar Array-like object representing a cookie jar
91 | */
92 | public $cookies = array();
93 |
94 | /**
95 | * Is the response a redirect?
96 | *
97 | * @return boolean True if redirect (3xx status), false if not.
98 | */
99 | public function is_redirect() {
100 | $code = $this->status_code;
101 | return in_array($code, array(300, 301, 302, 303, 307), true) || $code > 307 && $code < 400;
102 | }
103 |
104 | /**
105 | * Throws an exception if the request was not successful
106 | *
107 | * @throws Requests_Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`)
108 | * @throws Requests_Exception_HTTP On non-successful status code. Exception class corresponds to code (e.g. {@see Requests_Exception_HTTP_404})
109 | * @param boolean $allow_redirects Set to false to throw on a 3xx as well
110 | */
111 | public function throw_for_status($allow_redirects = true) {
112 | if ($this->is_redirect()) {
113 | if (!$allow_redirects) {
114 | throw new Requests_Exception('Redirection not allowed', 'response.no_redirects', $this);
115 | }
116 | }
117 | elseif (!$this->success) {
118 | $exception = Requests_Exception_HTTP::get_class($this->status_code);
119 | throw new $exception(null, $this);
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Response/Headers.php:
--------------------------------------------------------------------------------
1 | data[$key])) {
29 | return null;
30 | }
31 |
32 | return $this->flatten($this->data[$key]);
33 | }
34 |
35 | /**
36 | * Set the given item
37 | *
38 | * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
39 | *
40 | * @param string $key Item name
41 | * @param string $value Item value
42 | */
43 | public function offsetSet($key, $value) {
44 | if ($key === null) {
45 | throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
46 | }
47 |
48 | $key = strtolower($key);
49 |
50 | if (!isset($this->data[$key])) {
51 | $this->data[$key] = array();
52 | }
53 |
54 | $this->data[$key][] = $value;
55 | }
56 |
57 | /**
58 | * Get all values for a given header
59 | *
60 | * @param string $key
61 | * @return array|null Header values
62 | */
63 | public function getValues($key) {
64 | $key = strtolower($key);
65 | if (!isset($this->data[$key])) {
66 | return null;
67 | }
68 |
69 | return $this->data[$key];
70 | }
71 |
72 | /**
73 | * Flattens a value into a string
74 | *
75 | * Converts an array into a string by imploding values with a comma, as per
76 | * RFC2616's rules for folding headers.
77 | *
78 | * @param string|array $value Value to flatten
79 | * @return string Flattened value
80 | */
81 | public function flatten($value) {
82 | if (is_array($value)) {
83 | $value = implode(',', $value);
84 | }
85 |
86 | return $value;
87 | }
88 |
89 | /**
90 | * Get an iterator for the data
91 | *
92 | * Converts the internal
93 | * @return ArrayIterator
94 | */
95 | public function getIterator() {
96 | return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten'));
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/SSL.php:
--------------------------------------------------------------------------------
1 | useragent = 'X';`
55 | *
56 | * @var array
57 | */
58 | public $options = array();
59 |
60 | /**
61 | * Create a new session
62 | *
63 | * @param string|null $url Base URL for requests
64 | * @param array $headers Default headers for requests
65 | * @param array $data Default data for requests
66 | * @param array $options Default options for requests
67 | */
68 | public function __construct($url = null, $headers = array(), $data = array(), $options = array()) {
69 | $this->url = $url;
70 | $this->headers = $headers;
71 | $this->data = $data;
72 | $this->options = $options;
73 |
74 | if (empty($this->options['cookies'])) {
75 | $this->options['cookies'] = new Requests_Cookie_Jar();
76 | }
77 | }
78 |
79 | /**
80 | * Get a property's value
81 | *
82 | * @param string $key Property key
83 | * @return mixed|null Property value, null if none found
84 | */
85 | public function __get($key) {
86 | if (isset($this->options[$key])) {
87 | return $this->options[$key];
88 | }
89 |
90 | return null;
91 | }
92 |
93 | /**
94 | * Set a property's value
95 | *
96 | * @param string $key Property key
97 | * @param mixed $value Property value
98 | */
99 | public function __set($key, $value) {
100 | $this->options[$key] = $value;
101 | }
102 |
103 | /**
104 | * Remove a property's value
105 | *
106 | * @param string $key Property key
107 | */
108 | public function __isset($key) {
109 | return isset($this->options[$key]);
110 | }
111 |
112 | /**
113 | * Remove a property's value
114 | *
115 | * @param string $key Property key
116 | */
117 | public function __unset($key) {
118 | if (isset($this->options[$key])) {
119 | unset($this->options[$key]);
120 | }
121 | }
122 |
123 | /**#@+
124 | * @see request()
125 | * @param string $url
126 | * @param array $headers
127 | * @param array $options
128 | * @return Requests_Response
129 | */
130 | /**
131 | * Send a GET request
132 | */
133 | public function get($url, $headers = array(), $options = array()) {
134 | return $this->request($url, $headers, null, Requests::GET, $options);
135 | }
136 |
137 | /**
138 | * Send a HEAD request
139 | */
140 | public function head($url, $headers = array(), $options = array()) {
141 | return $this->request($url, $headers, null, Requests::HEAD, $options);
142 | }
143 |
144 | /**
145 | * Send a DELETE request
146 | */
147 | public function delete($url, $headers = array(), $options = array()) {
148 | return $this->request($url, $headers, null, Requests::DELETE, $options);
149 | }
150 | /**#@-*/
151 |
152 | /**#@+
153 | * @see request()
154 | * @param string $url
155 | * @param array $headers
156 | * @param array $data
157 | * @param array $options
158 | * @return Requests_Response
159 | */
160 | /**
161 | * Send a POST request
162 | */
163 | public function post($url, $headers = array(), $data = array(), $options = array()) {
164 | return $this->request($url, $headers, $data, Requests::POST, $options);
165 | }
166 |
167 | /**
168 | * Send a PUT request
169 | */
170 | public function put($url, $headers = array(), $data = array(), $options = array()) {
171 | return $this->request($url, $headers, $data, Requests::PUT, $options);
172 | }
173 |
174 | /**
175 | * Send a PATCH request
176 | *
177 | * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the
178 | * specification recommends that should send an ETag
179 | *
180 | * @link https://tools.ietf.org/html/rfc5789
181 | */
182 | public function patch($url, $headers, $data = array(), $options = array()) {
183 | return $this->request($url, $headers, $data, Requests::PATCH, $options);
184 | }
185 | /**#@-*/
186 |
187 | /**
188 | * Main interface for HTTP requests
189 | *
190 | * This method initiates a request and sends it via a transport before
191 | * parsing.
192 | *
193 | * @see Requests::request()
194 | *
195 | * @throws Requests_Exception On invalid URLs (`nonhttp`)
196 | *
197 | * @param string $url URL to request
198 | * @param array $headers Extra headers to send with the request
199 | * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
200 | * @param string $type HTTP request type (use Requests constants)
201 | * @param array $options Options for the request (see {@see Requests::request})
202 | * @return Requests_Response
203 | */
204 | public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) {
205 | $request = $this->merge_request(compact('url', 'headers', 'data', 'options'));
206 |
207 | return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']);
208 | }
209 |
210 | /**
211 | * Send multiple HTTP requests simultaneously
212 | *
213 | * @see Requests::request_multiple()
214 | *
215 | * @param array $requests Requests data (see {@see Requests::request_multiple})
216 | * @param array $options Global and default options (see {@see Requests::request})
217 | * @return array Responses (either Requests_Response or a Requests_Exception object)
218 | */
219 | public function request_multiple($requests, $options = array()) {
220 | foreach ($requests as $key => $request) {
221 | $requests[$key] = $this->merge_request($request, false);
222 | }
223 |
224 | $options = array_merge($this->options, $options);
225 |
226 | // Disallow forcing the type, as that's a per request setting
227 | unset($options['type']);
228 |
229 | return Requests::request_multiple($requests, $options);
230 | }
231 |
232 | /**
233 | * Merge a request's data with the default data
234 | *
235 | * @param array $request Request data (same form as {@see request_multiple})
236 | * @param boolean $merge_options Should we merge options as well?
237 | * @return array Request data
238 | */
239 | protected function merge_request($request, $merge_options = true) {
240 | if ($this->url !== null) {
241 | $request['url'] = Requests_IRI::absolutize($this->url, $request['url']);
242 | $request['url'] = $request['url']->uri;
243 | }
244 |
245 | if (empty($request['headers'])) {
246 | $request['headers'] = array();
247 | }
248 | $request['headers'] = array_merge($this->headers, $request['headers']);
249 |
250 | if (empty($request['data'])) {
251 | if (is_array($this->data)) {
252 | $request['data'] = $this->data;
253 | }
254 | }
255 | elseif (is_array($request['data']) && is_array($this->data)) {
256 | $request['data'] = array_merge($this->data, $request['data']);
257 | }
258 |
259 | if ($merge_options !== false) {
260 | $request['options'] = array_merge($this->options, $request['options']);
261 |
262 | // Disallow forcing the type, as that's a per request setting
263 | unset($request['options']['type']);
264 | }
265 |
266 | return $request;
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Transport.php:
--------------------------------------------------------------------------------
1 | version = $curl['version_number'];
95 | $this->handle = curl_init();
96 |
97 | curl_setopt($this->handle, CURLOPT_HEADER, false);
98 | curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1);
99 | if ($this->version >= self::CURL_7_10_5) {
100 | curl_setopt($this->handle, CURLOPT_ENCODING, '');
101 | }
102 | if (defined('CURLOPT_PROTOCOLS')) {
103 | // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_protocolsFound
104 | curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
105 | }
106 | if (defined('CURLOPT_REDIR_PROTOCOLS')) {
107 | // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_redir_protocolsFound
108 | curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
109 | }
110 | }
111 |
112 | /**
113 | * Destructor
114 | */
115 | public function __destruct() {
116 | if (is_resource($this->handle)) {
117 | curl_close($this->handle);
118 | }
119 | }
120 |
121 | /**
122 | * Perform a request
123 | *
124 | * @throws Requests_Exception On a cURL error (`curlerror`)
125 | *
126 | * @param string $url URL to request
127 | * @param array $headers Associative array of request headers
128 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
129 | * @param array $options Request options, see {@see Requests::response()} for documentation
130 | * @return string Raw HTTP result
131 | */
132 | public function request($url, $headers = array(), $data = array(), $options = array()) {
133 | $this->hooks = $options['hooks'];
134 |
135 | $this->setup_handle($url, $headers, $data, $options);
136 |
137 | $options['hooks']->dispatch('curl.before_send', array(&$this->handle));
138 |
139 | if ($options['filename'] !== false) {
140 | $this->stream_handle = fopen($options['filename'], 'wb');
141 | }
142 |
143 | $this->response_data = '';
144 | $this->response_bytes = 0;
145 | $this->response_byte_limit = false;
146 | if ($options['max_bytes'] !== false) {
147 | $this->response_byte_limit = $options['max_bytes'];
148 | }
149 |
150 | if (isset($options['verify'])) {
151 | if ($options['verify'] === false) {
152 | curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
153 | curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0);
154 | }
155 | elseif (is_string($options['verify'])) {
156 | curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']);
157 | }
158 | }
159 |
160 | if (isset($options['verifyname']) && $options['verifyname'] === false) {
161 | curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
162 | }
163 |
164 | curl_exec($this->handle);
165 | $response = $this->response_data;
166 |
167 | $options['hooks']->dispatch('curl.after_send', array());
168 |
169 | if (curl_errno($this->handle) === 23 || curl_errno($this->handle) === 61) {
170 | // Reset encoding and try again
171 | curl_setopt($this->handle, CURLOPT_ENCODING, 'none');
172 |
173 | $this->response_data = '';
174 | $this->response_bytes = 0;
175 | curl_exec($this->handle);
176 | $response = $this->response_data;
177 | }
178 |
179 | $this->process_response($response, $options);
180 |
181 | // Need to remove the $this reference from the curl handle.
182 | // Otherwise Requests_Transport_cURL wont be garbage collected and the curl_close() will never be called.
183 | curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, null);
184 | curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, null);
185 |
186 | return $this->headers;
187 | }
188 |
189 | /**
190 | * Send multiple requests simultaneously
191 | *
192 | * @param array $requests Request data
193 | * @param array $options Global options
194 | * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
195 | */
196 | public function request_multiple($requests, $options) {
197 | // If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
198 | if (empty($requests)) {
199 | return array();
200 | }
201 |
202 | $multihandle = curl_multi_init();
203 | $subrequests = array();
204 | $subhandles = array();
205 |
206 | $class = get_class($this);
207 | foreach ($requests as $id => $request) {
208 | $subrequests[$id] = new $class();
209 | $subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']);
210 | $request['options']['hooks']->dispatch('curl.before_multi_add', array(&$subhandles[$id]));
211 | curl_multi_add_handle($multihandle, $subhandles[$id]);
212 | }
213 |
214 | $completed = 0;
215 | $responses = array();
216 | $subrequestcount = count($subrequests);
217 |
218 | $request['options']['hooks']->dispatch('curl.before_multi_exec', array(&$multihandle));
219 |
220 | do {
221 | $active = 0;
222 |
223 | do {
224 | $status = curl_multi_exec($multihandle, $active);
225 | }
226 | while ($status === CURLM_CALL_MULTI_PERFORM);
227 |
228 | $to_process = array();
229 |
230 | // Read the information as needed
231 | while ($done = curl_multi_info_read($multihandle)) {
232 | $key = array_search($done['handle'], $subhandles, true);
233 | if (!isset($to_process[$key])) {
234 | $to_process[$key] = $done;
235 | }
236 | }
237 |
238 | // Parse the finished requests before we start getting the new ones
239 | foreach ($to_process as $key => $done) {
240 | $options = $requests[$key]['options'];
241 | if ($done['result'] !== CURLE_OK) {
242 | //get error string for handle.
243 | $reason = curl_error($done['handle']);
244 | $exception = new Requests_Exception_Transport_cURL(
245 | $reason,
246 | Requests_Exception_Transport_cURL::EASY,
247 | $done['handle'],
248 | $done['result']
249 | );
250 | $responses[$key] = $exception;
251 | $options['hooks']->dispatch('transport.internal.parse_error', array(&$responses[$key], $requests[$key]));
252 | }
253 | else {
254 | $responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options);
255 |
256 | $options['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$key], $requests[$key]));
257 | }
258 |
259 | curl_multi_remove_handle($multihandle, $done['handle']);
260 | curl_close($done['handle']);
261 |
262 | if (!is_string($responses[$key])) {
263 | $options['hooks']->dispatch('multiple.request.complete', array(&$responses[$key], $key));
264 | }
265 | $completed++;
266 | }
267 | }
268 | while ($active || $completed < $subrequestcount);
269 |
270 | $request['options']['hooks']->dispatch('curl.after_multi_exec', array(&$multihandle));
271 |
272 | curl_multi_close($multihandle);
273 |
274 | return $responses;
275 | }
276 |
277 | /**
278 | * Get the cURL handle for use in a multi-request
279 | *
280 | * @param string $url URL to request
281 | * @param array $headers Associative array of request headers
282 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
283 | * @param array $options Request options, see {@see Requests::response()} for documentation
284 | * @return resource Subrequest's cURL handle
285 | */
286 | public function &get_subrequest_handle($url, $headers, $data, $options) {
287 | $this->setup_handle($url, $headers, $data, $options);
288 |
289 | if ($options['filename'] !== false) {
290 | $this->stream_handle = fopen($options['filename'], 'wb');
291 | }
292 |
293 | $this->response_data = '';
294 | $this->response_bytes = 0;
295 | $this->response_byte_limit = false;
296 | if ($options['max_bytes'] !== false) {
297 | $this->response_byte_limit = $options['max_bytes'];
298 | }
299 | $this->hooks = $options['hooks'];
300 |
301 | return $this->handle;
302 | }
303 |
304 | /**
305 | * Setup the cURL handle for the given data
306 | *
307 | * @param string $url URL to request
308 | * @param array $headers Associative array of request headers
309 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
310 | * @param array $options Request options, see {@see Requests::response()} for documentation
311 | */
312 | protected function setup_handle($url, $headers, $data, $options) {
313 | $options['hooks']->dispatch('curl.before_request', array(&$this->handle));
314 |
315 | // Force closing the connection for old versions of cURL (<7.22).
316 | if (!isset($headers['Connection'])) {
317 | $headers['Connection'] = 'close';
318 | }
319 |
320 | /**
321 | * Add "Expect" header.
322 | *
323 | * By default, cURL adds a "Expect: 100-Continue" to most requests. This header can
324 | * add as much as a second to the time it takes for cURL to perform a request. To
325 | * prevent this, we need to set an empty "Expect" header. To match the behaviour of
326 | * Guzzle, we'll add the empty header to requests that are smaller than 1 MB and use
327 | * HTTP/1.1.
328 | *
329 | * https://curl.se/mail/lib-2017-07/0013.html
330 | */
331 | if (!isset($headers['Expect']) && $options['protocol_version'] === 1.1) {
332 | $headers['Expect'] = $this->get_expect_header($data);
333 | }
334 |
335 | $headers = Requests::flatten($headers);
336 |
337 | if (!empty($data)) {
338 | $data_format = $options['data_format'];
339 |
340 | if ($data_format === 'query') {
341 | $url = self::format_get($url, $data);
342 | $data = '';
343 | }
344 | elseif (!is_string($data)) {
345 | $data = http_build_query($data, '', '&');
346 | }
347 | }
348 |
349 | switch ($options['type']) {
350 | case Requests::POST:
351 | curl_setopt($this->handle, CURLOPT_POST, true);
352 | curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data);
353 | break;
354 | case Requests::HEAD:
355 | curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
356 | curl_setopt($this->handle, CURLOPT_NOBODY, true);
357 | break;
358 | case Requests::TRACE:
359 | curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
360 | break;
361 | case Requests::PATCH:
362 | case Requests::PUT:
363 | case Requests::DELETE:
364 | case Requests::OPTIONS:
365 | default:
366 | curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']);
367 | if (!empty($data)) {
368 | curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data);
369 | }
370 | }
371 |
372 | // cURL requires a minimum timeout of 1 second when using the system
373 | // DNS resolver, as it uses `alarm()`, which is second resolution only.
374 | // There's no way to detect which DNS resolver is being used from our
375 | // end, so we need to round up regardless of the supplied timeout.
376 | //
377 | // https://github.com/curl/curl/blob/4f45240bc84a9aa648c8f7243be7b79e9f9323a5/lib/hostip.c#L606-L609
378 | $timeout = max($options['timeout'], 1);
379 |
380 | if (is_int($timeout) || $this->version < self::CURL_7_16_2) {
381 | curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout));
382 | }
383 | else {
384 | // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_timeout_msFound
385 | curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000));
386 | }
387 |
388 | if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) {
389 | curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout']));
390 | }
391 | else {
392 | // phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_connecttimeout_msFound
393 | curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000));
394 | }
395 | curl_setopt($this->handle, CURLOPT_URL, $url);
396 | curl_setopt($this->handle, CURLOPT_REFERER, $url);
397 | curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']);
398 | if (!empty($headers)) {
399 | curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers);
400 | }
401 | if ($options['protocol_version'] === 1.1) {
402 | curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
403 | }
404 | else {
405 | curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
406 | }
407 |
408 | if ($options['blocking'] === true) {
409 | curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, array($this, 'stream_headers'));
410 | curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, array($this, 'stream_body'));
411 | curl_setopt($this->handle, CURLOPT_BUFFERSIZE, Requests::BUFFER_SIZE);
412 | }
413 | }
414 |
415 | /**
416 | * Process a response
417 | *
418 | * @param string $response Response data from the body
419 | * @param array $options Request options
420 | * @return string|false HTTP response data including headers. False if non-blocking.
421 | * @throws Requests_Exception
422 | */
423 | public function process_response($response, $options) {
424 | if ($options['blocking'] === false) {
425 | $fake_headers = '';
426 | $options['hooks']->dispatch('curl.after_request', array(&$fake_headers));
427 | return false;
428 | }
429 | if ($options['filename'] !== false && $this->stream_handle) {
430 | fclose($this->stream_handle);
431 | $this->headers = trim($this->headers);
432 | }
433 | else {
434 | $this->headers .= $response;
435 | }
436 |
437 | if (curl_errno($this->handle)) {
438 | $error = sprintf(
439 | 'cURL error %s: %s',
440 | curl_errno($this->handle),
441 | curl_error($this->handle)
442 | );
443 | throw new Requests_Exception($error, 'curlerror', $this->handle);
444 | }
445 | $this->info = curl_getinfo($this->handle);
446 |
447 | $options['hooks']->dispatch('curl.after_request', array(&$this->headers, &$this->info));
448 | return $this->headers;
449 | }
450 |
451 | /**
452 | * Collect the headers as they are received
453 | *
454 | * @param resource $handle cURL resource
455 | * @param string $headers Header string
456 | * @return integer Length of provided header
457 | */
458 | public function stream_headers($handle, $headers) {
459 | // Why do we do this? cURL will send both the final response and any
460 | // interim responses, such as a 100 Continue. We don't need that.
461 | // (We may want to keep this somewhere just in case)
462 | if ($this->done_headers) {
463 | $this->headers = '';
464 | $this->done_headers = false;
465 | }
466 | $this->headers .= $headers;
467 |
468 | if ($headers === "\r\n") {
469 | $this->done_headers = true;
470 | }
471 | return strlen($headers);
472 | }
473 |
474 | /**
475 | * Collect data as it's received
476 | *
477 | * @since 1.6.1
478 | *
479 | * @param resource $handle cURL resource
480 | * @param string $data Body data
481 | * @return integer Length of provided data
482 | */
483 | public function stream_body($handle, $data) {
484 | $this->hooks->dispatch('request.progress', array($data, $this->response_bytes, $this->response_byte_limit));
485 | $data_length = strlen($data);
486 |
487 | // Are we limiting the response size?
488 | if ($this->response_byte_limit) {
489 | if ($this->response_bytes === $this->response_byte_limit) {
490 | // Already at maximum, move on
491 | return $data_length;
492 | }
493 |
494 | if (($this->response_bytes + $data_length) > $this->response_byte_limit) {
495 | // Limit the length
496 | $limited_length = ($this->response_byte_limit - $this->response_bytes);
497 | $data = substr($data, 0, $limited_length);
498 | }
499 | }
500 |
501 | if ($this->stream_handle) {
502 | fwrite($this->stream_handle, $data);
503 | }
504 | else {
505 | $this->response_data .= $data;
506 | }
507 |
508 | $this->response_bytes += strlen($data);
509 | return $data_length;
510 | }
511 |
512 | /**
513 | * Format a URL given GET data
514 | *
515 | * @param string $url
516 | * @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query}
517 | * @return string URL with data
518 | */
519 | protected static function format_get($url, $data) {
520 | if (!empty($data)) {
521 | $query = '';
522 | $url_parts = parse_url($url);
523 | if (empty($url_parts['query'])) {
524 | $url_parts['query'] = '';
525 | }
526 | else {
527 | $query = $url_parts['query'];
528 | }
529 |
530 | $query .= '&' . http_build_query($data, null, '&');
531 | $query = trim($query, '&');
532 |
533 | if (empty($url_parts['query'])) {
534 | $url .= '?' . $query;
535 | }
536 | else {
537 | $url = str_replace($url_parts['query'], $query, $url);
538 | }
539 | }
540 | return $url;
541 | }
542 |
543 | /**
544 | * Whether this transport is valid
545 | *
546 | * @codeCoverageIgnore
547 | * @return boolean True if the transport is valid, false otherwise.
548 | */
549 | public static function test($capabilities = array()) {
550 | if (!function_exists('curl_init') || !function_exists('curl_exec')) {
551 | return false;
552 | }
553 |
554 | // If needed, check that our installed curl version supports SSL
555 | if (isset($capabilities['ssl']) && $capabilities['ssl']) {
556 | $curl_version = curl_version();
557 | if (!(CURL_VERSION_SSL & $curl_version['features'])) {
558 | return false;
559 | }
560 | }
561 |
562 | return true;
563 | }
564 |
565 | /**
566 | * Get the correct "Expect" header for the given request data.
567 | *
568 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD.
569 | * @return string The "Expect" header.
570 | */
571 | protected function get_expect_header($data) {
572 | if (!is_array($data)) {
573 | return strlen((string) $data) >= 1048576 ? '100-Continue' : '';
574 | }
575 |
576 | $bytesize = 0;
577 | $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($data));
578 |
579 | foreach ($iterator as $datum) {
580 | $bytesize += strlen((string) $datum);
581 |
582 | if ($bytesize >= 1048576) {
583 | return '100-Continue';
584 | }
585 | }
586 |
587 | return '';
588 | }
589 | }
590 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Transport/fsockopen.php:
--------------------------------------------------------------------------------
1 | dispatch('fsockopen.before_request');
60 |
61 | $url_parts = parse_url($url);
62 | if (empty($url_parts)) {
63 | throw new Requests_Exception('Invalid URL.', 'invalidurl', $url);
64 | }
65 | $host = $url_parts['host'];
66 | $context = stream_context_create();
67 | $verifyname = false;
68 | $case_insensitive_headers = new Requests_Utility_CaseInsensitiveDictionary($headers);
69 |
70 | // HTTPS support
71 | if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
72 | $remote_socket = 'ssl://' . $host;
73 | if (!isset($url_parts['port'])) {
74 | $url_parts['port'] = 443;
75 | }
76 |
77 | $context_options = array(
78 | 'verify_peer' => true,
79 | 'capture_peer_cert' => true,
80 | );
81 | $verifyname = true;
82 |
83 | // SNI, if enabled (OpenSSL >=0.9.8j)
84 | // phpcs:ignore PHPCompatibility.Constants.NewConstants.openssl_tlsext_server_nameFound
85 | if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) {
86 | $context_options['SNI_enabled'] = true;
87 | if (isset($options['verifyname']) && $options['verifyname'] === false) {
88 | $context_options['SNI_enabled'] = false;
89 | }
90 | }
91 |
92 | if (isset($options['verify'])) {
93 | if ($options['verify'] === false) {
94 | $context_options['verify_peer'] = false;
95 | $context_options['verify_peer_name'] = false;
96 | $verifyname = false;
97 | }
98 | elseif (is_string($options['verify'])) {
99 | $context_options['cafile'] = $options['verify'];
100 | }
101 | }
102 |
103 | if (isset($options['verifyname']) && $options['verifyname'] === false) {
104 | $context_options['verify_peer_name'] = false;
105 | $verifyname = false;
106 | }
107 |
108 | stream_context_set_option($context, array('ssl' => $context_options));
109 | }
110 | else {
111 | $remote_socket = 'tcp://' . $host;
112 | }
113 |
114 | $this->max_bytes = $options['max_bytes'];
115 |
116 | if (!isset($url_parts['port'])) {
117 | $url_parts['port'] = 80;
118 | }
119 | $remote_socket .= ':' . $url_parts['port'];
120 |
121 | // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
122 | set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE);
123 |
124 | $options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket));
125 |
126 | $socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context);
127 |
128 | restore_error_handler();
129 |
130 | if ($verifyname && !$this->verify_certificate_from_context($host, $context)) {
131 | throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
132 | }
133 |
134 | if (!$socket) {
135 | if ($errno === 0) {
136 | // Connection issue
137 | throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
138 | }
139 |
140 | throw new Requests_Exception($errstr, 'fsockopenerror', null, $errno);
141 | }
142 |
143 | $data_format = $options['data_format'];
144 |
145 | if ($data_format === 'query') {
146 | $path = self::format_get($url_parts, $data);
147 | $data = '';
148 | }
149 | else {
150 | $path = self::format_get($url_parts, array());
151 | }
152 |
153 | $options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url));
154 |
155 | $request_body = '';
156 | $out = sprintf("%s %s HTTP/%.1F\r\n", $options['type'], $path, $options['protocol_version']);
157 |
158 | if ($options['type'] !== Requests::TRACE) {
159 | if (is_array($data)) {
160 | $request_body = http_build_query($data, '', '&');
161 | }
162 | else {
163 | $request_body = $data;
164 | }
165 |
166 | // Always include Content-length on POST requests to prevent
167 | // 411 errors from some servers when the body is empty.
168 | if (!empty($data) || $options['type'] === Requests::POST) {
169 | if (!isset($case_insensitive_headers['Content-Length'])) {
170 | $headers['Content-Length'] = strlen($request_body);
171 | }
172 |
173 | if (!isset($case_insensitive_headers['Content-Type'])) {
174 | $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
175 | }
176 | }
177 | }
178 |
179 | if (!isset($case_insensitive_headers['Host'])) {
180 | $out .= sprintf('Host: %s', $url_parts['host']);
181 |
182 | if ((strtolower($url_parts['scheme']) === 'http' && $url_parts['port'] !== 80) || (strtolower($url_parts['scheme']) === 'https' && $url_parts['port'] !== 443)) {
183 | $out .= ':' . $url_parts['port'];
184 | }
185 | $out .= "\r\n";
186 | }
187 |
188 | if (!isset($case_insensitive_headers['User-Agent'])) {
189 | $out .= sprintf("User-Agent: %s\r\n", $options['useragent']);
190 | }
191 |
192 | $accept_encoding = $this->accept_encoding();
193 | if (!isset($case_insensitive_headers['Accept-Encoding']) && !empty($accept_encoding)) {
194 | $out .= sprintf("Accept-Encoding: %s\r\n", $accept_encoding);
195 | }
196 |
197 | $headers = Requests::flatten($headers);
198 |
199 | if (!empty($headers)) {
200 | $out .= implode("\r\n", $headers) . "\r\n";
201 | }
202 |
203 | $options['hooks']->dispatch('fsockopen.after_headers', array(&$out));
204 |
205 | if (substr($out, -2) !== "\r\n") {
206 | $out .= "\r\n";
207 | }
208 |
209 | if (!isset($case_insensitive_headers['Connection'])) {
210 | $out .= "Connection: Close\r\n";
211 | }
212 |
213 | $out .= "\r\n" . $request_body;
214 |
215 | $options['hooks']->dispatch('fsockopen.before_send', array(&$out));
216 |
217 | fwrite($socket, $out);
218 | $options['hooks']->dispatch('fsockopen.after_send', array($out));
219 |
220 | if (!$options['blocking']) {
221 | fclose($socket);
222 | $fake_headers = '';
223 | $options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers));
224 | return '';
225 | }
226 |
227 | $timeout_sec = (int) floor($options['timeout']);
228 | if ($timeout_sec === $options['timeout']) {
229 | $timeout_msec = 0;
230 | }
231 | else {
232 | $timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS;
233 | }
234 | stream_set_timeout($socket, $timeout_sec, $timeout_msec);
235 |
236 | $response = '';
237 | $body = '';
238 | $headers = '';
239 | $this->info = stream_get_meta_data($socket);
240 | $size = 0;
241 | $doingbody = false;
242 | $download = false;
243 | if ($options['filename']) {
244 | $download = fopen($options['filename'], 'wb');
245 | }
246 |
247 | while (!feof($socket)) {
248 | $this->info = stream_get_meta_data($socket);
249 | if ($this->info['timed_out']) {
250 | throw new Requests_Exception('fsocket timed out', 'timeout');
251 | }
252 |
253 | $block = fread($socket, Requests::BUFFER_SIZE);
254 | if (!$doingbody) {
255 | $response .= $block;
256 | if (strpos($response, "\r\n\r\n")) {
257 | list($headers, $block) = explode("\r\n\r\n", $response, 2);
258 | $doingbody = true;
259 | }
260 | }
261 |
262 | // Are we in body mode now?
263 | if ($doingbody) {
264 | $options['hooks']->dispatch('request.progress', array($block, $size, $this->max_bytes));
265 | $data_length = strlen($block);
266 | if ($this->max_bytes) {
267 | // Have we already hit a limit?
268 | if ($size === $this->max_bytes) {
269 | continue;
270 | }
271 | if (($size + $data_length) > $this->max_bytes) {
272 | // Limit the length
273 | $limited_length = ($this->max_bytes - $size);
274 | $block = substr($block, 0, $limited_length);
275 | }
276 | }
277 |
278 | $size += strlen($block);
279 | if ($download) {
280 | fwrite($download, $block);
281 | }
282 | else {
283 | $body .= $block;
284 | }
285 | }
286 | }
287 | $this->headers = $headers;
288 |
289 | if ($download) {
290 | fclose($download);
291 | }
292 | else {
293 | $this->headers .= "\r\n\r\n" . $body;
294 | }
295 | fclose($socket);
296 |
297 | $options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers, &$this->info));
298 | return $this->headers;
299 | }
300 |
301 | /**
302 | * Send multiple requests simultaneously
303 | *
304 | * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request}
305 | * @param array $options Global options, see {@see Requests::response()} for documentation
306 | * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
307 | */
308 | public function request_multiple($requests, $options) {
309 | $responses = array();
310 | $class = get_class($this);
311 | foreach ($requests as $id => $request) {
312 | try {
313 | $handler = new $class();
314 | $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']);
315 |
316 | $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request));
317 | }
318 | catch (Requests_Exception $e) {
319 | $responses[$id] = $e;
320 | }
321 |
322 | if (!is_string($responses[$id])) {
323 | $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id));
324 | }
325 | }
326 |
327 | return $responses;
328 | }
329 |
330 | /**
331 | * Retrieve the encodings we can accept
332 | *
333 | * @return string Accept-Encoding header value
334 | */
335 | protected static function accept_encoding() {
336 | $type = array();
337 | if (function_exists('gzinflate')) {
338 | $type[] = 'deflate;q=1.0';
339 | }
340 |
341 | if (function_exists('gzuncompress')) {
342 | $type[] = 'compress;q=0.5';
343 | }
344 |
345 | $type[] = 'gzip;q=0.5';
346 |
347 | return implode(', ', $type);
348 | }
349 |
350 | /**
351 | * Format a URL given GET data
352 | *
353 | * @param array $url_parts
354 | * @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query}
355 | * @return string URL with data
356 | */
357 | protected static function format_get($url_parts, $data) {
358 | if (!empty($data)) {
359 | if (empty($url_parts['query'])) {
360 | $url_parts['query'] = '';
361 | }
362 |
363 | $url_parts['query'] .= '&' . http_build_query($data, '', '&');
364 | $url_parts['query'] = trim($url_parts['query'], '&');
365 | }
366 | if (isset($url_parts['path'])) {
367 | if (isset($url_parts['query'])) {
368 | $get = $url_parts['path'] . '?' . $url_parts['query'];
369 | }
370 | else {
371 | $get = $url_parts['path'];
372 | }
373 | }
374 | else {
375 | $get = '/';
376 | }
377 | return $get;
378 | }
379 |
380 | /**
381 | * Error handler for stream_socket_client()
382 | *
383 | * @param int $errno Error number (e.g. E_WARNING)
384 | * @param string $errstr Error message
385 | */
386 | public function connect_error_handler($errno, $errstr) {
387 | // Double-check we can handle it
388 | if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) {
389 | // Return false to indicate the default error handler should engage
390 | return false;
391 | }
392 |
393 | $this->connect_error .= $errstr . "\n";
394 | return true;
395 | }
396 |
397 | /**
398 | * Verify the certificate against common name and subject alternative names
399 | *
400 | * Unfortunately, PHP doesn't check the certificate against the alternative
401 | * names, leading things like 'https://www.github.com/' to be invalid.
402 | * Instead
403 | *
404 | * @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
405 | *
406 | * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
407 | * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
408 | * @param string $host Host name to verify against
409 | * @param resource $context Stream context
410 | * @return bool
411 | */
412 | public function verify_certificate_from_context($host, $context) {
413 | $meta = stream_context_get_options($context);
414 |
415 | // If we don't have SSL options, then we couldn't make the connection at
416 | // all
417 | if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) {
418 | throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error');
419 | }
420 |
421 | $cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
422 |
423 | return Requests_SSL::verify_certificate($host, $cert);
424 | }
425 |
426 | /**
427 | * Whether this transport is valid
428 | *
429 | * @codeCoverageIgnore
430 | * @return boolean True if the transport is valid, false otherwise.
431 | */
432 | public static function test($capabilities = array()) {
433 | if (!function_exists('fsockopen')) {
434 | return false;
435 | }
436 |
437 | // If needed, check that streams support SSL
438 | if (isset($capabilities['ssl']) && $capabilities['ssl']) {
439 | if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) {
440 | return false;
441 | }
442 |
443 | // Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156
444 | if (defined('HHVM_VERSION')) {
445 | return false;
446 | }
447 | }
448 |
449 | return true;
450 | }
451 | }
452 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Utility/CaseInsensitiveDictionary.php:
--------------------------------------------------------------------------------
1 | $value) {
30 | $this->offsetSet($key, $value);
31 | }
32 | }
33 |
34 | /**
35 | * Check if the given item exists
36 | *
37 | * @param string $key Item key
38 | * @return boolean Does the item exist?
39 | */
40 | #[\ReturnTypeWillChange]
41 | public function offsetExists($key) {
42 | $key = strtolower($key);
43 | return isset($this->data[$key]);
44 | }
45 |
46 | /**
47 | * Get the value for the item
48 | *
49 | * @param string $key Item key
50 | * @return string|null Item value (null if offsetExists is false)
51 | */
52 | #[\ReturnTypeWillChange]
53 | public function offsetGet($key) {
54 | $key = strtolower($key);
55 | if (!isset($this->data[$key])) {
56 | return null;
57 | }
58 |
59 | return $this->data[$key];
60 | }
61 |
62 | /**
63 | * Set the given item
64 | *
65 | * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
66 | *
67 | * @param string $key Item name
68 | * @param string $value Item value
69 | */
70 | #[\ReturnTypeWillChange]
71 | public function offsetSet($key, $value) {
72 | if ($key === null) {
73 | throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
74 | }
75 |
76 | $key = strtolower($key);
77 | $this->data[$key] = $value;
78 | }
79 |
80 | /**
81 | * Unset the given header
82 | *
83 | * @param string $key
84 | */
85 | #[\ReturnTypeWillChange]
86 | public function offsetUnset($key) {
87 | unset($this->data[strtolower($key)]);
88 | }
89 |
90 | /**
91 | * Get an iterator for the data
92 | *
93 | * @return ArrayIterator
94 | */
95 | #[\ReturnTypeWillChange]
96 | public function getIterator() {
97 | return new ArrayIterator($this->data);
98 | }
99 |
100 | /**
101 | * Get the headers as an array
102 | *
103 | * @return array Header data
104 | */
105 | public function getAll() {
106 | return $this->data;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/libs/Requests/Requests/Utility/FilteredIterator.php:
--------------------------------------------------------------------------------
1 | callback = $callback;
33 | }
34 |
35 | /**
36 | * Get the current item's value after filtering
37 | *
38 | * @return string
39 | */
40 | public function current() {
41 | $value = parent::current();
42 |
43 | if (is_callable($this->callback)) {
44 | $value = call_user_func($this->callback, $value);
45 | }
46 |
47 | return $value;
48 | }
49 |
50 | /**
51 | * @inheritdoc
52 | */
53 | public function unserialize($serialized) {}
54 |
55 | /**
56 | * @inheritdoc
57 | *
58 | * @phpcs:disable PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__unserializeFound
59 | */
60 | public function __unserialize($serialized) {}
61 |
62 | public function __wakeup() {
63 | unset($this->callback);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------