├── .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 | Logo 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 | --------------------------------------------------------------------------------