Upload files to 'includes'
This commit is contained in:
parent
186f9d43fc
commit
e9d431635d
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 304 Not Modified responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 304 Not Modified responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_304 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 304;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Not Modified';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 305 Use Proxy responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 305 Use Proxy responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_305 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 305;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Use Proxy';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 306 Switch Proxy responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 306 Switch Proxy responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_306 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 306;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Switch Proxy';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 400 Bad Request responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 400 Bad Request responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_400 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 400;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Bad Request';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 401 Unauthorized responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 401 Unauthorized responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_401 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 401;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Unauthorized';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 402 Payment Required responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 402 Payment Required responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_402 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 402;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Payment Required';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 403 Forbidden responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 403 Forbidden responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_403 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 403;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Forbidden';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 404 Not Found responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 404 Not Found responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_404 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 404;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Not Found';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 405 Method Not Allowed responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 405 Method Not Allowed responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_405 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 405;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Method Not Allowed';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 406 Not Acceptable responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 406 Not Acceptable responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_406 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 406;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Not Acceptable';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 407 Proxy Authentication Required responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 407 Proxy Authentication Required responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_407 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 407;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Proxy Authentication Required';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 408 Request Timeout responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 408 Request Timeout responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_408 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 408;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Request Timeout';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 409 Conflict responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 409 Conflict responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_409 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 409;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Conflict';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 410 Gone responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 410 Gone responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_410 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 410;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Gone';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 411 Length Required responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 411 Length Required responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_411 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 411;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Length Required';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 412 Precondition Failed responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 412 Precondition Failed responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_412 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 412;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Precondition Failed';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 413 Request Entity Too Large responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 413 Request Entity Too Large responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_413 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 413;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Request Entity Too Large';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 414 Request-URI Too Large responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 414 Request-URI Too Large responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_414 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 414;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Request-URI Too Large';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 415 Unsupported Media Type responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 415 Unsupported Media Type responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_415 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 415;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Unsupported Media Type';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 416 Requested Range Not Satisfiable responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 416 Requested Range Not Satisfiable responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_416 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 416;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Requested Range Not Satisfiable';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 417 Expectation Failed responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 417 Expectation Failed responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_417 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 417;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Expectation Failed';
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 418 I'm A Teapot responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2324
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 418 I'm A Teapot responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2324
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_418 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 418;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = "I'm A Teapot";
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 428 Precondition Required responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6585
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 428 Precondition Required responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6585
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_428 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 428;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Precondition Required';
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 429 Too Many Requests responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 429 Too Many Requests responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_429 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 429;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Too Many Requests';
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 431 Request Header Fields Too Large responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6585
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 431 Request Header Fields Too Large responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6585
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_431 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 431;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Request Header Fields Too Large';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 500 Internal Server Error responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 500 Internal Server Error responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_500 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 500;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Internal Server Error';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 501 Not Implemented responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 501 Not Implemented responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_501 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 501;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Not Implemented';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 502 Bad Gateway responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 502 Bad Gateway responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_502 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 502;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Bad Gateway';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 503 Service Unavailable responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 503 Service Unavailable responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_503 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 503;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Service Unavailable';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 504 Gateway Timeout responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 504 Gateway Timeout responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_504 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 504;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Gateway Timeout';
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 505 HTTP Version Not Supported responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 505 HTTP Version Not Supported responses
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_505 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 505;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'HTTP Version Not Supported';
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for 511 Network Authentication Required responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6585
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for 511 Network Authentication Required responses
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc6585
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception_HTTP_511 extends Requests_Exception_HTTP {
|
||||
/**
|
||||
* HTTP status code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = 511;
|
||||
|
||||
/**
|
||||
* Reason phrase
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Network Authentication Required';
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* This is a simple Logger implementation that other Loggers can inherit from.
|
||||
*
|
||||
* It simply delegates all log-level-specific methods to the `log` method to
|
||||
* reduce boilerplate code that a simple Logger that does the same thing with
|
||||
* messages regardless of the error level has to implement.
|
||||
*/
|
||||
abstract class AbstractLogger implements LoggerInterface
|
||||
{
|
||||
/**
|
||||
* System is unusable.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function emergency($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::EMERGENCY, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action must be taken immediately.
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function alert($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::ALERT, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical conditions.
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function critical($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::CRITICAL, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::ERROR, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function warning($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::WARNING, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notice($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::NOTICE, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function info($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::INFO, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function debug($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::DEBUG, $message, $context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Model;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
abstract class AbstractModel implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $raw;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
$this->raw = $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get(string $field)
|
||||
{
|
||||
if (isset($this->raw[$field])) {
|
||||
return $this->raw[$field];
|
||||
}
|
||||
if (preg_match('/^is_/', $field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
if ($attr !== 'instance' && property_exists($this, $attr)) {
|
||||
return $this->{$attr};
|
||||
}
|
||||
|
||||
throw new \RuntimeException("Unknown attribute: $attr");
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __isset(string $attr): bool
|
||||
{
|
||||
return $attr !== 'instance' && isset($this->{$attr});
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return $this->raw;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql\Parser;
|
||||
|
||||
use Aura\Sql\Exception\MissingParameter;
|
||||
|
||||
/**
|
||||
*
|
||||
* Parsing/rebuilding functionality for all drivers.
|
||||
*
|
||||
* Note that this does not validate the syntax; it only replaces/rebuilds
|
||||
* placeholders in the query.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
abstract class AbstractParser implements ParserInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Split the query string on these regexes.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $split = [
|
||||
// single-quoted string
|
||||
"'(?:[^'\\\\]|\\\\'?)*'",
|
||||
// double-quoted string
|
||||
'"(?:[^"\\\\]|\\\\"?)*"',
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
* Skip query parts matching this regex.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
*/
|
||||
protected $skip = '/^(\'|\"|\:[^a-zA-Z_])/um';
|
||||
|
||||
/**
|
||||
*
|
||||
* The current numbered-placeholder in the original statement.
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
*/
|
||||
protected $num = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* How many times has a named placeholder been used?
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $count = [
|
||||
'__' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
* The initial values to be bound.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $values = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* Final placeholders and values to bind.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $final_values = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* Rebuilds a statement with placeholders and bound values.
|
||||
*
|
||||
* @param string $statement The statement to rebuild.
|
||||
*
|
||||
* @param array $values The values to bind and/or replace into a statement.
|
||||
*
|
||||
* @return array An array where element 0 is the rebuilt statement and
|
||||
* element 1 is the rebuilt array of values.
|
||||
*
|
||||
*/
|
||||
public function rebuild($statement, array $values = [])
|
||||
{
|
||||
// match standard PDO execute() behavior of zero-indexed arrays
|
||||
if (array_key_exists(0, $values)) {
|
||||
array_unshift($values, null);
|
||||
}
|
||||
|
||||
$this->values = $values;
|
||||
$statement = $this->rebuildStatement($statement);
|
||||
return [$statement, $this->final_values];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Given a statement, rebuilds it with array values embedded.
|
||||
*
|
||||
* @param string $statement The SQL statement.
|
||||
*
|
||||
* @return string The rebuilt statement.
|
||||
*
|
||||
*/
|
||||
protected function rebuildStatement($statement)
|
||||
{
|
||||
$parts = $this->getParts($statement);
|
||||
return $this->rebuildParts($parts);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Given an array of statement parts, rebuilds each part.
|
||||
*
|
||||
* @param array $parts The statement parts.
|
||||
*
|
||||
* @return string The rebuilt statement.
|
||||
*
|
||||
*/
|
||||
protected function rebuildParts(array $parts)
|
||||
{
|
||||
$statement = '';
|
||||
foreach ($parts as $part) {
|
||||
$statement .= $this->rebuildPart($part);
|
||||
}
|
||||
return $statement;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Rebuilds a single statement part.
|
||||
*
|
||||
* @param string $part The statement part.
|
||||
*
|
||||
* @return string The rebuilt statement.
|
||||
*
|
||||
*/
|
||||
protected function rebuildPart($part)
|
||||
{
|
||||
if (preg_match($this->skip, $part)) {
|
||||
return $part;
|
||||
}
|
||||
|
||||
// split into subparts by ":name" and "?"
|
||||
$subs = preg_split(
|
||||
"/(?<!:)(:[a-zA-Z_][a-zA-Z0-9_]*)|(\?)/um",
|
||||
$part,
|
||||
-1,
|
||||
PREG_SPLIT_DELIM_CAPTURE
|
||||
);
|
||||
|
||||
// check subparts to expand placeholders for bound arrays
|
||||
return $this->prepareValuePlaceholders($subs);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Prepares the sub-parts of a query with placeholders.
|
||||
*
|
||||
* @param array $subs The query subparts.
|
||||
*
|
||||
* @return string The prepared subparts.
|
||||
*
|
||||
*/
|
||||
protected function prepareValuePlaceholders(array $subs)
|
||||
{
|
||||
$str = '';
|
||||
foreach ($subs as $i => $sub) {
|
||||
$char = substr($sub, 0, 1);
|
||||
if ($char == '?') {
|
||||
$str .= $this->prepareNumberedPlaceholder($sub);
|
||||
} elseif ($char == ':') {
|
||||
$str .= $this->prepareNamedPlaceholder($sub);
|
||||
} else {
|
||||
$str .= $sub;
|
||||
}
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Bind or quote a numbered placeholder in a query subpart.
|
||||
*
|
||||
* @param string $sub The query subpart.
|
||||
*
|
||||
* @return string The prepared query subpart.
|
||||
*
|
||||
* @throws MissingParameter
|
||||
*/
|
||||
protected function prepareNumberedPlaceholder($sub)
|
||||
{
|
||||
$this->num ++;
|
||||
if (array_key_exists($this->num, $this->values) === false) {
|
||||
throw new MissingParameter("Parameter {$this->num} is missing from the bound values");
|
||||
}
|
||||
|
||||
$expanded = [];
|
||||
$values = (array) $this->values[$this->num];
|
||||
if (is_null($this->values[$this->num])) {
|
||||
$values[] = null;
|
||||
}
|
||||
foreach ($values as $value) {
|
||||
$count = ++ $this->count['__'];
|
||||
$name = "__{$count}";
|
||||
$expanded[] = ":{$name}";
|
||||
$this->final_values[$name] = $value;
|
||||
}
|
||||
return implode(', ', $expanded);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Bind or quote a named placeholder in a query subpart.
|
||||
*
|
||||
* @param string $sub The query subpart.
|
||||
*
|
||||
* @return string The prepared query subpart.
|
||||
*
|
||||
*/
|
||||
protected function prepareNamedPlaceholder($sub)
|
||||
{
|
||||
$orig = substr($sub, 1);
|
||||
if (array_key_exists($orig, $this->values) === false) {
|
||||
throw new MissingParameter("Parameter '{$orig}' is missing from the bound values");
|
||||
}
|
||||
|
||||
$name = $this->getPlaceholderName($orig);
|
||||
|
||||
// is the corresponding data element an array?
|
||||
$bind_array = is_array($this->values[$orig]);
|
||||
if ($bind_array) {
|
||||
// expand to multiple placeholders
|
||||
return $this->expandNamedPlaceholder($name, $this->values[$orig]);
|
||||
}
|
||||
|
||||
// not an array, retain the placeholder for later
|
||||
$this->final_values[$name] = $this->values[$orig];
|
||||
return ":$name";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Given an original placeholder name, return a replacement name.
|
||||
*
|
||||
* @param string $orig The original placeholder name.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function getPlaceholderName($orig)
|
||||
{
|
||||
if (! isset($this->count[$orig])) {
|
||||
$this->count[$orig] = 0;
|
||||
return $orig;
|
||||
}
|
||||
|
||||
$count = ++ $this->count[$orig];
|
||||
return "{$orig}__{$count}";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Given a named placeholder for an array, expand it for the array values,
|
||||
* and bind those values to the expanded names.
|
||||
*
|
||||
* @param string $prefix The named placeholder.
|
||||
*
|
||||
* @param array $values The array values to be bound.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
protected function expandNamedPlaceholder($prefix, array $values)
|
||||
{
|
||||
$i = 0;
|
||||
$expanded = [];
|
||||
foreach ($values as $value) {
|
||||
$name = "{$prefix}_{$i}";
|
||||
$expanded[] = ":{$name}";
|
||||
$this->final_values[$name] = $value;
|
||||
$i ++;
|
||||
}
|
||||
return implode(', ', $expanded);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Given a query string, split it into parts.
|
||||
*
|
||||
* @param string $statement The query string.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
protected function getParts($statement)
|
||||
{
|
||||
$split = implode('|', $this->split);
|
||||
return preg_split(
|
||||
"/($split)/um",
|
||||
$statement,
|
||||
-1,
|
||||
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Record;
|
||||
|
||||
abstract class AbstractPlaceRecord extends AbstractRecord
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private $locales;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(?array $record, array $locales = ['en'])
|
||||
{
|
||||
$this->locales = $locales;
|
||||
parent::__construct($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
if ($attr === 'name') {
|
||||
return $this->name();
|
||||
}
|
||||
|
||||
return parent::__get($attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __isset(string $attr): bool
|
||||
{
|
||||
if ($attr === 'name') {
|
||||
return $this->firstSetNameLocale() !== null;
|
||||
}
|
||||
|
||||
return parent::__isset($attr);
|
||||
}
|
||||
|
||||
private function name(): ?string
|
||||
{
|
||||
$locale = $this->firstSetNameLocale();
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
return $locale === null ? null : $this->names[$locale];
|
||||
}
|
||||
|
||||
private function firstSetNameLocale(): ?string
|
||||
{
|
||||
foreach ($this->locales as $locale) {
|
||||
// @phpstan-ignore-next-line
|
||||
if (isset($this->names[$locale])) {
|
||||
return $locale;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Record;
|
||||
|
||||
abstract class AbstractRecord implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private $record;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(?array $record)
|
||||
{
|
||||
$this->record = isset($record) ? $record : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
// XXX - kind of ugly but greatly reduces boilerplate code
|
||||
$key = $this->attributeToKey($attr);
|
||||
|
||||
if ($this->__isset($attr)) {
|
||||
return $this->record[$key];
|
||||
}
|
||||
if ($this->validAttribute($attr)) {
|
||||
if (preg_match('/^is_/', $key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new \RuntimeException("Unknown attribute: $attr");
|
||||
}
|
||||
|
||||
public function __isset(string $attr): bool
|
||||
{
|
||||
return $this->validAttribute($attr)
|
||||
&& isset($this->record[$this->attributeToKey($attr)]);
|
||||
}
|
||||
|
||||
private function attributeToKey(string $attr): string
|
||||
{
|
||||
return strtolower(preg_replace('/([A-Z])/', '_\1', $attr));
|
||||
}
|
||||
|
||||
private function validAttribute(string $attr): bool
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return \in_array($attr, $this->validAttributes, true);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): ?array
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents a generic error.
|
||||
*/
|
||||
class AddressNotFoundException extends GeoIp2Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
<?php
|
||||
/**
|
||||
* Parameters used to display links the admin view (eg admin/index.php)
|
||||
*
|
||||
* @since 1.8.2
|
||||
*/
|
||||
|
||||
namespace YOURLS\Views;
|
||||
|
||||
/**
|
||||
* Class AdminParams to get admin view parameters (number of links to display, search, ...)
|
||||
*
|
||||
* @since 1.8.2
|
||||
* @package YOURLS\Views
|
||||
*/
|
||||
class AdminParams
|
||||
{
|
||||
|
||||
/**
|
||||
* All possible search parameters. Populated in the constructor.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $possible_search_params;
|
||||
|
||||
/**
|
||||
* All possible sort parameters. Populated in the constructor.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $possible_sort_params;
|
||||
|
||||
/**
|
||||
* All possible date sorting parameters. Populated in the constructor.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $possible_date_sorting;
|
||||
|
||||
/**
|
||||
* Parameter translations. Populated in the constructor.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $params_translations;
|
||||
|
||||
|
||||
/**
|
||||
* Admin constructor : populate all default parameters
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->possible_search_params = yourls_apply_filter('admin_params_possible_search',
|
||||
['all', 'keyword', 'url', 'title', 'ip']);
|
||||
$this->possible_sort_params = yourls_apply_filter('admin_params_possible_sort',
|
||||
['keyword', 'url', 'title', 'ip', 'timestamp', 'clicks']);
|
||||
$this->params_translations = yourls_apply_filter('admin_params_possible_translations',[
|
||||
'all' => yourls__('All fields'),
|
||||
'keyword' => yourls__('Short URL'),
|
||||
'url' => yourls__('URL'),
|
||||
'title' => yourls__('Title'),
|
||||
'ip' => yourls__('IP Address'),
|
||||
'timestamp' => yourls__('Date'),
|
||||
'clicks' => yourls__('Clicks'),
|
||||
]);
|
||||
$this->possible_date_sorting = yourls_apply_filter('admin_params_possible_date_sort',
|
||||
['before', 'after', 'between']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of links to display per page
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param int $default default number of links to display
|
||||
* @return int
|
||||
*/
|
||||
public function get_per_page(int $default): int
|
||||
{
|
||||
// return if we have a value and it's not 0
|
||||
if (isset($_GET['perpage']) && intval($_GET['perpage'])) {
|
||||
$per_page = intval($_GET['perpage']);
|
||||
// otherwise return filtered default value
|
||||
} else {
|
||||
// @hook Default number of links to display (value provided by caller eg /admin/index.php)
|
||||
$per_page = yourls_apply_filter('admin_view_per_page', $default);
|
||||
}
|
||||
|
||||
return $per_page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current page number to be displayed
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_page(): int
|
||||
{
|
||||
return isset($_GET['page']) ? intval($_GET['page']) : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search text (the 'Search for') from query string variables search_protocol, search_slashes and search
|
||||
*
|
||||
* Some servers don't like query strings containing "(ht|f)tp(s)://". A javascript bit
|
||||
* explodes the search text into protocol, slashes and the rest (see JS function
|
||||
* split_search_text_before_search()) and this function glues pieces back together
|
||||
* See issue https://github.com/YOURLS/YOURLS/issues/1576
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_search(): string
|
||||
{
|
||||
$search = '';
|
||||
if (isset($_GET['search_protocol'])) {
|
||||
$search .= $_GET['search_protocol'];
|
||||
}
|
||||
if (isset($_GET['search_slashes'])) {
|
||||
$search .= $_GET['search_slashes'];
|
||||
}
|
||||
if (isset($_GET['search'])) {
|
||||
$search .= $_GET['search'];
|
||||
}
|
||||
|
||||
// @hook Default search text in links displayed
|
||||
return yourls_apply_filter('admin_view_get_search_text', htmlspecialchars(trim($search)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 'Search In' parameter (one of 'all', 'keyword', 'url', 'title', 'ip')
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_search_in(): string
|
||||
{
|
||||
if (isset($_GET['search_in']) && in_array($_GET['search_in'], $this->possible_search_params)) {
|
||||
$search_in = $_GET['search_in'];
|
||||
} else {
|
||||
// @hook Default searching in the admin view (in all fields)
|
||||
$search_in = yourls_apply_filter('admin_view_search_in', 'all');
|
||||
}
|
||||
|
||||
return $search_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 'Sort by' parameter
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_sort_by(): string
|
||||
{
|
||||
if (isset($_GET['sort_by']) && in_array($_GET['sort_by'], $this->possible_sort_params)) {
|
||||
$sort_by = $_GET['sort_by'];
|
||||
} else {
|
||||
// @hook Default sorting in the admin view (by Timestamp)
|
||||
$sort_by = yourls_apply_filter('admin_view_sort_by', 'timestamp');
|
||||
}
|
||||
|
||||
return $sort_by;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct phrasing associated to a search or sort parameter (ie 'all' -> 'All fields' for instance)
|
||||
*
|
||||
* No checks : you need to supply an existing parameter, see $params_translations
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @param string $param
|
||||
* @return string
|
||||
*/
|
||||
public function get_param_long_name(string $param): string
|
||||
{
|
||||
return $this->params_translations[$param];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sort order (asc or desc)
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_sort_order()
|
||||
{
|
||||
// @hook Default sorting order in the admin view (descending)
|
||||
return isset($_GET['sort_order']) && $_GET['sort_order'] == 'asc' ? 'asc' : yourls_apply_filter('admin_view_sort_order', 'desc');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the click "more or less than"
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_click_filter()
|
||||
{
|
||||
// @hook Default 'Show links with more/less than' ('more')
|
||||
return isset($_GET['click_filter']) && $_GET['click_filter'] == 'less' ? 'less' : yourls_apply_filter('admin_view_click_filter', 'more');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the click threshold
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function get_click_limit()
|
||||
{
|
||||
// @hook Default link click threshold (unset)
|
||||
return (isset($_GET['click_limit']) && intval($_GET['click_limit'])) ?
|
||||
intval($_GET['click_limit']) : yourls_apply_filter('admin_view_click_limit', '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the date parameters : the date "filter" and the two dates
|
||||
*
|
||||
* @since 1.8.2
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_date_params(): array
|
||||
{
|
||||
if (isset($_GET['date_filter']) && in_array($_GET['date_filter'], $this->possible_date_sorting)) {
|
||||
$date_filter = $_GET['date_filter'];
|
||||
} else {
|
||||
// @hook Default date filtering (unset)
|
||||
$date_filter = yourls_apply_filter('admin_view_date_filter', '');
|
||||
}
|
||||
|
||||
switch ($date_filter) {
|
||||
case 'after':
|
||||
case 'before':
|
||||
if (isset($_GET['date_first']) && yourls_sanitize_date($_GET['date_first'])) {
|
||||
$date_first = yourls_sanitize_date($_GET['date_first']);
|
||||
} else {
|
||||
// @hook Default date when date filter is either 'after' or 'before' (unset)
|
||||
// In such case, the filter is either 'admin_view_date_first_after' or 'admin_view_date_first_before'
|
||||
$date_first = yourls_apply_filter('admin_view_date_first_' . $date_filter, '');
|
||||
}
|
||||
$date_second = '';
|
||||
break;
|
||||
|
||||
case 'between':
|
||||
if (isset($_GET['date_first']) && isset($_GET['date_second']) && yourls_sanitize_date($_GET['date_first']) && yourls_sanitize_date($_GET['date_second'])) {
|
||||
$date_first = yourls_sanitize_date($_GET['date_first']);
|
||||
$date_second = yourls_sanitize_date($_GET['date_second']);
|
||||
} else {
|
||||
// @hook Default dates when date filter is 'between' (unset)
|
||||
$date_first = yourls_apply_filter('admin_view_date_first_between', '');
|
||||
$date_second = yourls_apply_filter('admin_view_date_second_between', '');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// @hook Default date when date filter is unset (unset)
|
||||
$date_first = yourls_apply_filter('admin_view_date_first_unset', '');
|
||||
$date_second = yourls_apply_filter('admin_view_date_second_unset', '');
|
||||
|
||||
}
|
||||
|
||||
return ['date_filter' => $date_filter, 'date_first' => $date_first, 'date_second' => $date_second];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Model;
|
||||
|
||||
use GeoIp2\Util;
|
||||
|
||||
/**
|
||||
* This class provides the GeoIP2 Anonymous IP model.
|
||||
*
|
||||
* @property-read bool $isAnonymous This is true if the IP address belongs to
|
||||
* any sort of anonymous network.
|
||||
* @property-read bool $isAnonymousVpn This is true if the IP address is
|
||||
* registered to an anonymous VPN provider. If a VPN provider does not
|
||||
* register subnets under names associated with them, we will likely only
|
||||
* flag their IP ranges using the isHostingProvider property.
|
||||
* @property-read bool $isHostingProvider This is true if the IP address belongs
|
||||
* to a hosting or VPN provider (see description of isAnonymousVpn property).
|
||||
* @property-read bool $isPublicProxy This is true if the IP address belongs to
|
||||
* a public proxy.
|
||||
* @property-read bool $isResidentialProxy This is true if the IP address is
|
||||
* on a suspected anonymizing network and belongs to a residential ISP.
|
||||
* @property-read bool $isTorExitNode This is true if the IP address is a Tor
|
||||
* exit node.
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class AnonymousIp extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isAnonymous;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isAnonymousVpn;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isHostingProvider;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isPublicProxy;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isResidentialProxy;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isTorExitNode;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
|
||||
$this->isAnonymous = $this->get('is_anonymous');
|
||||
$this->isAnonymousVpn = $this->get('is_anonymous_vpn');
|
||||
$this->isHostingProvider = $this->get('is_hosting_provider');
|
||||
$this->isPublicProxy = $this->get('is_public_proxy');
|
||||
$this->isResidentialProxy = $this->get('is_residential_proxy');
|
||||
$this->isTorExitNode = $this->get('is_tor_exit_node');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace WpOrg\Requests\Exception;
|
||||
|
||||
use WpOrg\Requests\Exception;
|
||||
|
||||
/**
|
||||
* Exception for when an incorrect number of arguments are passed to a method.
|
||||
*
|
||||
* Typically, this exception is used when all arguments for a method are optional,
|
||||
* but certain arguments need to be passed together, i.e. a method which can be called
|
||||
* with no arguments or with two arguments, but not with one argument.
|
||||
*
|
||||
* Along the same lines, this exception is also used if a method expects an array
|
||||
* with a certain number of elements and the provided number of elements does not comply.
|
||||
*
|
||||
* @package Requests\Exceptions
|
||||
* @since 2.0.0
|
||||
*/
|
||||
final class ArgumentCount extends Exception {
|
||||
|
||||
/**
|
||||
* Create a new argument count exception with a standardized text.
|
||||
*
|
||||
* @param string $expected The argument count expected as a phrase.
|
||||
* For example: `at least 2 arguments` or `exactly 1 argument`.
|
||||
* @param int $received The actual argument count received.
|
||||
* @param string $type Exception type.
|
||||
*
|
||||
* @return \WpOrg\Requests\Exception\ArgumentCount
|
||||
*/
|
||||
public static function create($expected, $received, $type) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
|
||||
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
|
||||
return new self(
|
||||
sprintf(
|
||||
'%s::%s() expects %s, %d given',
|
||||
$stack[1]['class'],
|
||||
$stack[1]['function'],
|
||||
$expected,
|
||||
$received
|
||||
),
|
||||
$type
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
<?php
|
||||
|
||||
namespace Spatie\ArrayToXml;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMException;
|
||||
use Exception;
|
||||
|
||||
class ArrayToXml
|
||||
{
|
||||
protected $document;
|
||||
|
||||
protected $replaceSpacesByUnderScoresInKeyNames = true;
|
||||
|
||||
protected $addXmlDeclaration = true;
|
||||
|
||||
protected $numericTagNamePrefix = 'numeric_';
|
||||
|
||||
public function __construct(
|
||||
array $array,
|
||||
$rootElement = '',
|
||||
$replaceSpacesByUnderScoresInKeyNames = true,
|
||||
$xmlEncoding = null,
|
||||
$xmlVersion = '1.0',
|
||||
$domProperties = [],
|
||||
$xmlStandalone = null
|
||||
) {
|
||||
$this->document = new DOMDocument($xmlVersion, $xmlEncoding);
|
||||
|
||||
if (! is_null($xmlStandalone)) {
|
||||
$this->document->xmlStandalone = $xmlStandalone;
|
||||
}
|
||||
|
||||
if (! empty($domProperties)) {
|
||||
$this->setDomProperties($domProperties);
|
||||
}
|
||||
|
||||
$this->replaceSpacesByUnderScoresInKeyNames = $replaceSpacesByUnderScoresInKeyNames;
|
||||
|
||||
if ($this->isArrayAllKeySequential($array) && ! empty($array)) {
|
||||
throw new DOMException('Invalid Character Error');
|
||||
}
|
||||
|
||||
$root = $this->createRootElement($rootElement);
|
||||
|
||||
$this->document->appendChild($root);
|
||||
|
||||
$this->convertElement($root, $array);
|
||||
}
|
||||
|
||||
public function setNumericTagNamePrefix(string $prefix)
|
||||
{
|
||||
$this->numericTagNamePrefix = $prefix;
|
||||
}
|
||||
|
||||
public static function convert(
|
||||
array $array,
|
||||
$rootElement = '',
|
||||
bool $replaceSpacesByUnderScoresInKeyNames = true,
|
||||
string $xmlEncoding = null,
|
||||
string $xmlVersion = '1.0',
|
||||
array $domProperties = [],
|
||||
bool $xmlStandalone = null
|
||||
) {
|
||||
$converter = new static(
|
||||
$array,
|
||||
$rootElement,
|
||||
$replaceSpacesByUnderScoresInKeyNames,
|
||||
$xmlEncoding,
|
||||
$xmlVersion,
|
||||
$domProperties,
|
||||
$xmlStandalone
|
||||
);
|
||||
|
||||
return $converter->toXml();
|
||||
}
|
||||
|
||||
public function toXml(): string
|
||||
{
|
||||
if ($this->addXmlDeclaration === false) {
|
||||
return $this->document->saveXml($this->document->documentElement);
|
||||
}
|
||||
|
||||
return $this->document->saveXML();
|
||||
}
|
||||
|
||||
public function toDom(): DOMDocument
|
||||
{
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
protected function ensureValidDomProperties(array $domProperties)
|
||||
{
|
||||
foreach ($domProperties as $key => $value) {
|
||||
if (! property_exists($this->document, $key)) {
|
||||
throw new Exception($key.' is not a valid property of DOMDocument');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setDomProperties(array $domProperties)
|
||||
{
|
||||
$this->ensureValidDomProperties($domProperties);
|
||||
|
||||
foreach ($domProperties as $key => $value) {
|
||||
$this->document->{$key} = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function prettify()
|
||||
{
|
||||
$this->document->preserveWhiteSpace = false;
|
||||
$this->document->formatOutput = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function dropXmlDeclaration()
|
||||
{
|
||||
$this->addXmlDeclaration = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function convertElement(DOMElement $element, $value)
|
||||
{
|
||||
$sequential = $this->isArrayAllKeySequential($value);
|
||||
|
||||
if (! is_array($value)) {
|
||||
$value = htmlspecialchars($value);
|
||||
|
||||
$value = $this->removeControlCharacters($value);
|
||||
|
||||
$element->nodeValue = $value;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($value as $key => $data) {
|
||||
if (! $sequential) {
|
||||
if (($key === '_attributes') || ($key === '@attributes')) {
|
||||
$this->addAttributes($element, $data);
|
||||
} elseif ((($key === '_value') || ($key === '@value')) && is_string($data)) {
|
||||
$element->nodeValue = htmlspecialchars($data);
|
||||
} elseif ((($key === '_cdata') || ($key === '@cdata')) && is_string($data)) {
|
||||
$element->appendChild($this->document->createCDATASection($data));
|
||||
} elseif ((($key === '_mixed') || ($key === '@mixed')) && is_string($data)) {
|
||||
$fragment = $this->document->createDocumentFragment();
|
||||
$fragment->appendXML($data);
|
||||
$element->appendChild($fragment);
|
||||
} elseif ($key === '__numeric') {
|
||||
$this->addNumericNode($element, $data);
|
||||
} elseif (substr($key, 0, 9) === '__custom:') {
|
||||
$this->addNode($element, str_replace('\:', ':', preg_split('/(?<!\\\):/', $key)[1]), $data);
|
||||
} else {
|
||||
$this->addNode($element, $key, $data);
|
||||
}
|
||||
} elseif (is_array($data)) {
|
||||
$this->addCollectionNode($element, $data);
|
||||
} else {
|
||||
$this->addSequentialNode($element, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addNumericNode(DOMElement $element, $value)
|
||||
{
|
||||
foreach ($value as $key => $item) {
|
||||
$this->convertElement($element, [$this->numericTagNamePrefix.$key => $item]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function addNode(DOMElement $element, $key, $value)
|
||||
{
|
||||
if ($this->replaceSpacesByUnderScoresInKeyNames) {
|
||||
$key = str_replace(' ', '_', $key);
|
||||
}
|
||||
|
||||
$child = $this->document->createElement($key);
|
||||
$element->appendChild($child);
|
||||
$this->convertElement($child, $value);
|
||||
}
|
||||
|
||||
protected function addCollectionNode(DOMElement $element, $value)
|
||||
{
|
||||
if ($element->childNodes->length === 0 && $element->attributes->length === 0) {
|
||||
$this->convertElement($element, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$child = $this->document->createElement($element->tagName);
|
||||
$element->parentNode->appendChild($child);
|
||||
$this->convertElement($child, $value);
|
||||
}
|
||||
|
||||
protected function addSequentialNode(DOMElement $element, $value)
|
||||
{
|
||||
if (empty($element->nodeValue) && ! is_numeric($element->nodeValue)) {
|
||||
$element->nodeValue = htmlspecialchars($value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$child = new DOMElement($element->tagName);
|
||||
$child->nodeValue = htmlspecialchars($value);
|
||||
$element->parentNode->appendChild($child);
|
||||
}
|
||||
|
||||
protected function isArrayAllKeySequential($value)
|
||||
{
|
||||
if (! is_array($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($value) <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (\key($value) === '__numeric') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_unique(array_map('is_int', array_keys($value))) === [true];
|
||||
}
|
||||
|
||||
protected function addAttributes(DOMElement $element, array $data)
|
||||
{
|
||||
foreach ($data as $attrKey => $attrVal) {
|
||||
$element->setAttribute($attrKey, $attrVal);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createRootElement($rootElement): DOMElement
|
||||
{
|
||||
if (is_string($rootElement)) {
|
||||
$rootElementName = $rootElement ?: 'root';
|
||||
|
||||
return $this->document->createElement($rootElementName);
|
||||
}
|
||||
|
||||
$rootElementName = $rootElement['rootElementName'] ?? 'root';
|
||||
|
||||
$element = $this->document->createElement($rootElementName);
|
||||
|
||||
foreach ($rootElement as $key => $value) {
|
||||
if ($key !== '_attributes' && $key !== '@attributes') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->addAttributes($element, $rootElement[$key]);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
protected function removeControlCharacters(string $value): string
|
||||
{
|
||||
return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Model;
|
||||
|
||||
use GeoIp2\Util;
|
||||
|
||||
/**
|
||||
* This class provides the GeoLite2 ASN model.
|
||||
*
|
||||
* @property-read int|null $autonomousSystemNumber The autonomous system number
|
||||
* associated with the IP address.
|
||||
* @property-read string|null $autonomousSystemOrganization The organization
|
||||
* associated with the registered autonomous system number for the IP
|
||||
* address.
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class Asn extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
protected $autonomousSystemNumber;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $autonomousSystemOrganization;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
$this->autonomousSystemNumber = $this->get('autonomous_system_number');
|
||||
$this->autonomousSystemOrganization =
|
||||
$this->get('autonomous_system_organization');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/**
|
||||
* Authentication provider interface
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Authentication
|
||||
*/
|
||||
|
||||
/**
|
||||
* Authentication provider interface
|
||||
*
|
||||
* Implement this interface to act as an authentication provider.
|
||||
*
|
||||
* Parameters should be passed via the constructor where possible, as this
|
||||
* makes it much easier for users to use your provider.
|
||||
*
|
||||
* @see Requests_Hooks
|
||||
* @package Requests
|
||||
* @subpackage Authentication
|
||||
*/
|
||||
interface Requests_Auth {
|
||||
/**
|
||||
* Register hooks as needed
|
||||
*
|
||||
* This method is called in {@see Requests::request} when the user has set
|
||||
* an instance as the 'auth' option. Use this callback to register all the
|
||||
* hooks you'll need.
|
||||
*
|
||||
* @see Requests_Hooks::register
|
||||
* @param Requests_Hooks $hooks Hook system
|
||||
*/
|
||||
public function register(Requests_Hooks &$hooks);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* This class represents an error authenticating.
|
||||
*/
|
||||
class AuthenticationException extends InvalidRequestException
|
||||
{
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
/**
|
||||
* Autoloader for Requests for PHP.
|
||||
*
|
||||
* Include this file if you'd like to avoid having to create your own autoloader.
|
||||
*
|
||||
* @package Requests
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
|
||||
namespace WpOrg\Requests;
|
||||
|
||||
/*
|
||||
* Ensure the autoloader is only declared once.
|
||||
* This safeguard is in place as this is the typical entry point for this library
|
||||
* and this file being required unconditionally could easily cause
|
||||
* fatal "Class already declared" errors.
|
||||
*/
|
||||
if (class_exists('WpOrg\Requests\Autoload') === false) {
|
||||
|
||||
/**
|
||||
* Autoloader for Requests for PHP.
|
||||
*
|
||||
* This autoloader supports the PSR-4 based Requests 2.0.0 classes in a case-sensitive manner
|
||||
* as the most common server OS-es are case-sensitive and the file names are in mixed case.
|
||||
*
|
||||
* For the PSR-0 Requests 1.x BC-layer, requested classes will be treated case-insensitively.
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
final class Autoload {
|
||||
|
||||
/**
|
||||
* List of the old PSR-0 class names in lowercase as keys with their PSR-4 case-sensitive name as a value.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $deprecated_classes = [
|
||||
// Interfaces.
|
||||
'requests_auth' => '\WpOrg\Requests\Auth',
|
||||
'requests_hooker' => '\WpOrg\Requests\HookManager',
|
||||
'requests_proxy' => '\WpOrg\Requests\Proxy',
|
||||
'requests_transport' => '\WpOrg\Requests\Transport',
|
||||
|
||||
// Classes.
|
||||
'requests_cookie' => '\WpOrg\Requests\Cookie',
|
||||
'requests_exception' => '\WpOrg\Requests\Exception',
|
||||
'requests_hooks' => '\WpOrg\Requests\Hooks',
|
||||
'requests_idnaencoder' => '\WpOrg\Requests\IdnaEncoder',
|
||||
'requests_ipv6' => '\WpOrg\Requests\Ipv6',
|
||||
'requests_iri' => '\WpOrg\Requests\Iri',
|
||||
'requests_response' => '\WpOrg\Requests\Response',
|
||||
'requests_session' => '\WpOrg\Requests\Session',
|
||||
'requests_ssl' => '\WpOrg\Requests\Ssl',
|
||||
'requests_auth_basic' => '\WpOrg\Requests\Auth\Basic',
|
||||
'requests_cookie_jar' => '\WpOrg\Requests\Cookie\Jar',
|
||||
'requests_proxy_http' => '\WpOrg\Requests\Proxy\Http',
|
||||
'requests_response_headers' => '\WpOrg\Requests\Response\Headers',
|
||||
'requests_transport_curl' => '\WpOrg\Requests\Transport\Curl',
|
||||
'requests_transport_fsockopen' => '\WpOrg\Requests\Transport\Fsockopen',
|
||||
'requests_utility_caseinsensitivedictionary' => '\WpOrg\Requests\Utility\CaseInsensitiveDictionary',
|
||||
'requests_utility_filterediterator' => '\WpOrg\Requests\Utility\FilteredIterator',
|
||||
'requests_exception_http' => '\WpOrg\Requests\Exception\Http',
|
||||
'requests_exception_transport' => '\WpOrg\Requests\Exception\Transport',
|
||||
'requests_exception_transport_curl' => '\WpOrg\Requests\Exception\Transport\Curl',
|
||||
'requests_exception_http_304' => '\WpOrg\Requests\Exception\Http\Status304',
|
||||
'requests_exception_http_305' => '\WpOrg\Requests\Exception\Http\Status305',
|
||||
'requests_exception_http_306' => '\WpOrg\Requests\Exception\Http\Status306',
|
||||
'requests_exception_http_400' => '\WpOrg\Requests\Exception\Http\Status400',
|
||||
'requests_exception_http_401' => '\WpOrg\Requests\Exception\Http\Status401',
|
||||
'requests_exception_http_402' => '\WpOrg\Requests\Exception\Http\Status402',
|
||||
'requests_exception_http_403' => '\WpOrg\Requests\Exception\Http\Status403',
|
||||
'requests_exception_http_404' => '\WpOrg\Requests\Exception\Http\Status404',
|
||||
'requests_exception_http_405' => '\WpOrg\Requests\Exception\Http\Status405',
|
||||
'requests_exception_http_406' => '\WpOrg\Requests\Exception\Http\Status406',
|
||||
'requests_exception_http_407' => '\WpOrg\Requests\Exception\Http\Status407',
|
||||
'requests_exception_http_408' => '\WpOrg\Requests\Exception\Http\Status408',
|
||||
'requests_exception_http_409' => '\WpOrg\Requests\Exception\Http\Status409',
|
||||
'requests_exception_http_410' => '\WpOrg\Requests\Exception\Http\Status410',
|
||||
'requests_exception_http_411' => '\WpOrg\Requests\Exception\Http\Status411',
|
||||
'requests_exception_http_412' => '\WpOrg\Requests\Exception\Http\Status412',
|
||||
'requests_exception_http_413' => '\WpOrg\Requests\Exception\Http\Status413',
|
||||
'requests_exception_http_414' => '\WpOrg\Requests\Exception\Http\Status414',
|
||||
'requests_exception_http_415' => '\WpOrg\Requests\Exception\Http\Status415',
|
||||
'requests_exception_http_416' => '\WpOrg\Requests\Exception\Http\Status416',
|
||||
'requests_exception_http_417' => '\WpOrg\Requests\Exception\Http\Status417',
|
||||
'requests_exception_http_418' => '\WpOrg\Requests\Exception\Http\Status418',
|
||||
'requests_exception_http_428' => '\WpOrg\Requests\Exception\Http\Status428',
|
||||
'requests_exception_http_429' => '\WpOrg\Requests\Exception\Http\Status429',
|
||||
'requests_exception_http_431' => '\WpOrg\Requests\Exception\Http\Status431',
|
||||
'requests_exception_http_500' => '\WpOrg\Requests\Exception\Http\Status500',
|
||||
'requests_exception_http_501' => '\WpOrg\Requests\Exception\Http\Status501',
|
||||
'requests_exception_http_502' => '\WpOrg\Requests\Exception\Http\Status502',
|
||||
'requests_exception_http_503' => '\WpOrg\Requests\Exception\Http\Status503',
|
||||
'requests_exception_http_504' => '\WpOrg\Requests\Exception\Http\Status504',
|
||||
'requests_exception_http_505' => '\WpOrg\Requests\Exception\Http\Status505',
|
||||
'requests_exception_http_511' => '\WpOrg\Requests\Exception\Http\Status511',
|
||||
'requests_exception_http_unknown' => '\WpOrg\Requests\Exception\Http\StatusUnknown',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the autoloader.
|
||||
*
|
||||
* Note: the autoloader is *prepended* in the autoload queue.
|
||||
* This is done to ensure that the Requests 2.0 autoloader takes precedence
|
||||
* over a potentially (dependency-registered) Requests 1.x autoloader.
|
||||
*
|
||||
* @internal This method contains a safeguard against the autoloader being
|
||||
* registered multiple times. This safeguard uses a global constant to
|
||||
* (hopefully/in most cases) still function correctly, even if the
|
||||
* class would be renamed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register() {
|
||||
if (defined('REQUESTS_AUTOLOAD_REGISTERED') === false) {
|
||||
spl_autoload_register([self::class, 'load'], true);
|
||||
define('REQUESTS_AUTOLOAD_REGISTERED', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoloader.
|
||||
*
|
||||
* @param string $class_name Name of the class name to load.
|
||||
*
|
||||
* @return bool Whether a class was loaded or not.
|
||||
*/
|
||||
public static function load($class_name) {
|
||||
// Check that the class starts with "Requests" (PSR-0) or "WpOrg\Requests" (PSR-4).
|
||||
$psr_4_prefix_pos = strpos($class_name, 'WpOrg\\Requests\\');
|
||||
|
||||
if (stripos($class_name, 'Requests') !== 0 && $psr_4_prefix_pos !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$class_lower = strtolower($class_name);
|
||||
|
||||
if ($class_lower === 'requests') {
|
||||
// Reference to the original PSR-0 Requests class.
|
||||
$file = dirname(__DIR__) . '/library/Requests.php';
|
||||
} elseif ($psr_4_prefix_pos === 0) {
|
||||
// PSR-4 classname.
|
||||
$file = __DIR__ . '/' . strtr(substr($class_name, 15), '\\', '/') . '.php';
|
||||
}
|
||||
|
||||
if (isset($file) && file_exists($file)) {
|
||||
include $file;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay, so the class starts with "Requests", but we couldn't find the file.
|
||||
* If this is one of the deprecated/renamed PSR-0 classes being requested,
|
||||
* let's alias it to the new name and throw a deprecation notice.
|
||||
*/
|
||||
if (isset(self::$deprecated_classes[$class_lower])) {
|
||||
/*
|
||||
* Integrators who cannot yet upgrade to the PSR-4 class names can silence deprecations
|
||||
* by defining a `REQUESTS_SILENCE_PSR0_DEPRECATIONS` constant and setting it to `true`.
|
||||
* The constant needs to be defined before the first deprecated class is requested
|
||||
* via this autoloader.
|
||||
*/
|
||||
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS') || REQUESTS_SILENCE_PSR0_DEPRECATIONS !== true) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
||||
trigger_error(
|
||||
'The PSR-0 `Requests_...` class names in the Request library are deprecated.'
|
||||
. ' Switch to the PSR-4 `WpOrg\Requests\...` class names at your earliest convenience.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
// Prevent the deprecation notice from being thrown twice.
|
||||
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS')) {
|
||||
define('REQUESTS_SILENCE_PSR0_DEPRECATIONS', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an alias and let the autoloader recursively kick in to load the PSR-4 class.
|
||||
return class_alias(self::$deprecated_classes[$class_lower], $class_name, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* Basic Authentication provider
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Authentication
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic Authentication provider
|
||||
*
|
||||
* Provides a handler for Basic HTTP authentication via the Authorization
|
||||
* header.
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Authentication
|
||||
*/
|
||||
class Requests_Auth_Basic implements Requests_Auth {
|
||||
/**
|
||||
* Username
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Password
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pass;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`)
|
||||
* @param array|null $args Array of user and password. Must have exactly two elements
|
||||
*/
|
||||
public function __construct($args = null) {
|
||||
if (is_array($args)) {
|
||||
if (count($args) !== 2) {
|
||||
throw new Requests_Exception('Invalid number of arguments', 'authbasicbadargs');
|
||||
}
|
||||
|
||||
list($this->user, $this->pass) = $args;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the necessary callbacks
|
||||
*
|
||||
* @see curl_before_send
|
||||
* @see fsockopen_header
|
||||
* @param Requests_Hooks $hooks Hook system
|
||||
*/
|
||||
public function register(Requests_Hooks &$hooks) {
|
||||
$hooks->register('curl.before_send', array(&$this, 'curl_before_send'));
|
||||
$hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cURL parameters before the data is sent
|
||||
*
|
||||
* @param resource $handle cURL resource
|
||||
*/
|
||||
public function curl_before_send(&$handle) {
|
||||
curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra headers to the request before sending
|
||||
*
|
||||
* @param string $out HTTP header string
|
||||
*/
|
||||
public function fsockopen_header(&$out) {
|
||||
$out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authentication string (user:pass)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthString() {
|
||||
return $this->user . ':' . $this->pass;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* BookmarkletGen : converts readable Javascript code into a bookmarklet link
|
||||
*
|
||||
* Features :
|
||||
* - removes comments
|
||||
* - compresses code, not literal strings
|
||||
* Example:
|
||||
* function someName( param ) { alert( "this is a string" ) }
|
||||
* will return:
|
||||
* function%20someName(param){alert("this is a string")}
|
||||
* - wraps code into a self invoking function
|
||||
*
|
||||
* This is basically a slightly enhanced PHP port of the excellent Bookmarklet Crunchinator
|
||||
* http://ted.mielczarek.org/code/mozilla/bookmarklet.html
|
||||
*
|
||||
*/
|
||||
namespace Ozh\Bookmarkletgen;
|
||||
|
||||
class Bookmarkletgen {
|
||||
|
||||
private $literal_strings = array();
|
||||
|
||||
/**
|
||||
* Main function, calls all others
|
||||
*
|
||||
* @param string $code Javascript code to bookmarkletify
|
||||
* @return string Bookmarklet link
|
||||
*/
|
||||
public function crunch( $code ) {
|
||||
$out = "(function() {\n" . $code . "\n})();";
|
||||
|
||||
$out = $this->replace_strings( $out );
|
||||
$out = $this->kill_comments( $out );
|
||||
$out = $this->compress_white_space( $out );
|
||||
$out = $this->combine_strings( $out );
|
||||
$out = $this->restore_strings( $out );
|
||||
$out = $this->encodeURIComponent( $out );
|
||||
$out = 'javascript:' . $out;
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP port of Javascript function encodeURIComponent
|
||||
*
|
||||
* From http://stackoverflow.com/a/1734255/36850
|
||||
*
|
||||
* @since
|
||||
* @param string $str String to encode
|
||||
* @return string Encoded string
|
||||
*/
|
||||
//
|
||||
private function encodeURIComponent( $str ) {
|
||||
$revert = array(
|
||||
'%21'=>'!', '%2A'=>'*', '%28'=>'(', '%29'=>')',
|
||||
);
|
||||
|
||||
return strtr( rawurlencode( $str ), $revert );
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill comment lines and blocks
|
||||
*
|
||||
* @param string $code Commented Javascript code
|
||||
* @return string Commentless code
|
||||
*/
|
||||
private function kill_comments( $code ) {
|
||||
$code = preg_replace( '!\s*//.+$!m', '', $code );
|
||||
$code = preg_replace( '!/\*.+?\*/!sm', '', $code ); // s modifier: dot matches new lines
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress white space
|
||||
*
|
||||
* Remove some extraneous spaces and make the whole script a one liner
|
||||
*
|
||||
* @param string $code Javascript code
|
||||
* @return string Compressed code
|
||||
*/
|
||||
private function compress_white_space( $code ) {
|
||||
// Tabs to space, no more than 1 consecutive space
|
||||
$code = preg_replace( '!\t!m', ' ', $code );
|
||||
$code = preg_replace( '![ ]{2,}!m', ' ', $code );
|
||||
|
||||
// Remove uneccessary white space around operators, braces and brackets.
|
||||
// \xHH sequence is: !%&()*+,-/:;<=>?[]\{|}~
|
||||
$code = preg_replace( '/\s([\x21\x25\x26\x28\x29\x2a\x2b\x2c\x2d\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5d\x5c\x7b\x7c\x7d\x7e])/m', "$1", $code );
|
||||
$code = preg_replace( '/([\x21\x25\x26\x28\x29\x2a\x2b\x2c\x2d\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5d\x5c\x7b\x7c\x7d\x7e])\s/m', "$1", $code );
|
||||
|
||||
// Split on each line, trim leading/trailing white space, kill empty lines, combine everything in one line
|
||||
$code = preg_split( '/\r\n|\r|\n/', $code );
|
||||
foreach( $code as $i => $line ) {
|
||||
$code[ $i ] = trim( $line );
|
||||
}
|
||||
$code = implode( '', $code );
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine any consecutive strings
|
||||
*
|
||||
* In the case we have two consecutive quoted strings (eg: "hello" + "world"), save a couple more
|
||||
* length and combine them
|
||||
*
|
||||
* @param string $code Javascript code
|
||||
* @return string Javascript code
|
||||
*/
|
||||
private function combine_strings( $code ) {
|
||||
$code = preg_replace('/"\+"/m', "", $code);
|
||||
$code = preg_replace("/'\+'/m", "", $code);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace all literal strings (eg: "hello world") with a placeholder and collect them in an array
|
||||
*
|
||||
* The idea is that strings cannot be trimmed or white-space optimized: take them out first before uglifying
|
||||
* the code, then we'll reinject them back in later
|
||||
*
|
||||
* @param string $code Javascript code
|
||||
* @return string Javascript code with placeholders (eg "__1__") instead of literal strings
|
||||
*/
|
||||
private function replace_strings( $code ) {
|
||||
$return = "";
|
||||
$literal = "";
|
||||
$quoteChar = "";
|
||||
$escaped = false;
|
||||
|
||||
// Split script into individual lines.
|
||||
$lines = explode("\n", $code);
|
||||
$count = count( $lines );
|
||||
for( $i = 0; $i < $count; $i++ ) {
|
||||
|
||||
$j = 0;
|
||||
$inQuote = false;
|
||||
while ($j < strlen( $lines[$i] ) ) {
|
||||
$c = $lines[ $i ][ $j ];
|
||||
|
||||
// If not already in a string, look for the start of one.
|
||||
if (!$inQuote) {
|
||||
if ($c == '"' || $c == "'") {
|
||||
$inQuote = true;
|
||||
$escaped = false;
|
||||
$quoteChar = $c;
|
||||
$literal = $c;
|
||||
}
|
||||
else {
|
||||
$return .= $c;
|
||||
}
|
||||
}
|
||||
|
||||
// Already in a string, look for end and copy characters.
|
||||
else {
|
||||
if ($c == $quoteChar && !$escaped) {
|
||||
$inQuote = false;
|
||||
$literal .= $quoteChar;
|
||||
$return .= "__" . count( $this->literal_strings ) . "__";
|
||||
$this->literal_strings[ count( $this->literal_strings ) ] = $literal;
|
||||
}
|
||||
else if ($c == "\\" && !$escaped) {
|
||||
$escaped = true;
|
||||
}
|
||||
else {
|
||||
$escaped = false;
|
||||
}
|
||||
$literal .= $c;
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
$return .= "\n";
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore literal strings by replacing their placeholders with actual strings
|
||||
*
|
||||
* @param string $code Javascript code with placeholders
|
||||
* @return string Javascript code with actual strings
|
||||
*/
|
||||
private function restore_strings( $code ) {
|
||||
foreach( $this->literal_strings as $i => $string ) {
|
||||
$code = preg_replace( '/__' . $i . '__/', $string, $code, 1 );
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Database and Contents Copyright (c) 2022 MaxMind, Inc.
|
|
@ -0,0 +1,431 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of composer/ca-bundle.
|
||||
*
|
||||
* (c) Composer <https://github.com/composer>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\CaBundle;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Process\PhpProcess;
|
||||
|
||||
/**
|
||||
* @author Chris Smith <chris@cs278.org>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class CaBundle
|
||||
{
|
||||
/** @var string|null */
|
||||
private static $caPath;
|
||||
/** @var array<string, bool> */
|
||||
private static $caFileValidity = array();
|
||||
/** @var bool|null */
|
||||
private static $useOpensslParse;
|
||||
|
||||
/**
|
||||
* Returns the system CA bundle path, or a path to the bundled one
|
||||
*
|
||||
* This method was adapted from Sslurp.
|
||||
* https://github.com/EvanDotPro/Sslurp
|
||||
*
|
||||
* (c) Evan Coury <me@evancoury.com>
|
||||
*
|
||||
* For the full copyright and license information, please see below:
|
||||
*
|
||||
* Copyright (c) 2013, Evan Coury
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
|
||||
* @return string path to a CA bundle file or directory
|
||||
*/
|
||||
public static function getSystemCaRootBundlePath(LoggerInterface $logger = null)
|
||||
{
|
||||
if (self::$caPath !== null) {
|
||||
return self::$caPath;
|
||||
}
|
||||
$caBundlePaths = array();
|
||||
|
||||
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
|
||||
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
|
||||
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE');
|
||||
|
||||
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
|
||||
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
|
||||
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR');
|
||||
|
||||
$caBundlePaths[] = ini_get('openssl.cafile');
|
||||
$caBundlePaths[] = ini_get('openssl.capath');
|
||||
|
||||
$otherLocations = array(
|
||||
'/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
|
||||
'/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
|
||||
'/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
|
||||
'/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
|
||||
'/usr/ssl/certs/ca-bundle.crt', // Cygwin
|
||||
'/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
|
||||
'/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
|
||||
'/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
|
||||
'/etc/ssl/cert.pem', // OpenBSD
|
||||
'/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
|
||||
'/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
|
||||
'/usr/local/etc/openssl@1.1/cert.pem', // OS X homebrew, openssl@1.1 package
|
||||
);
|
||||
|
||||
foreach($otherLocations as $location) {
|
||||
$otherLocations[] = dirname($location);
|
||||
}
|
||||
|
||||
$caBundlePaths = array_merge($caBundlePaths, $otherLocations);
|
||||
|
||||
foreach ($caBundlePaths as $caBundle) {
|
||||
if ($caBundle && self::caFileUsable($caBundle, $logger)) {
|
||||
return self::$caPath = $caBundle;
|
||||
}
|
||||
|
||||
if ($caBundle && self::caDirUsable($caBundle, $logger)) {
|
||||
return self::$caPath = $caBundle;
|
||||
}
|
||||
}
|
||||
|
||||
return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the bundled CA file
|
||||
*
|
||||
* In case you don't want to trust the user or the system, you can use this directly
|
||||
*
|
||||
* @return string path to a CA bundle file
|
||||
*/
|
||||
public static function getBundledCaBundlePath()
|
||||
{
|
||||
$caBundleFile = __DIR__.'/../res/cacert.pem';
|
||||
|
||||
// cURL does not understand 'phar://' paths
|
||||
// see https://github.com/composer/ca-bundle/issues/10
|
||||
if (0 === strpos($caBundleFile, 'phar://')) {
|
||||
$tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-');
|
||||
if (false === $tempCaBundleFile) {
|
||||
throw new \RuntimeException('Could not create a temporary file to store the bundled CA file');
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$tempCaBundleFile,
|
||||
file_get_contents($caBundleFile)
|
||||
);
|
||||
|
||||
register_shutdown_function(function() use ($tempCaBundleFile) {
|
||||
@unlink($tempCaBundleFile);
|
||||
});
|
||||
|
||||
$caBundleFile = $tempCaBundleFile;
|
||||
}
|
||||
|
||||
return $caBundleFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a CA file using opensl_x509_parse only if it is safe to use
|
||||
*
|
||||
* @param string $filename
|
||||
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateCaFile($filename, LoggerInterface $logger = null)
|
||||
{
|
||||
static $warned = false;
|
||||
|
||||
if (isset(self::$caFileValidity[$filename])) {
|
||||
return self::$caFileValidity[$filename];
|
||||
}
|
||||
|
||||
$contents = file_get_contents($filename);
|
||||
|
||||
// assume the CA is valid if php is vulnerable to
|
||||
// https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
|
||||
if (!static::isOpensslParseSafe()) {
|
||||
if (!$warned && $logger) {
|
||||
$logger->warning(sprintf(
|
||||
'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.',
|
||||
PHP_VERSION
|
||||
));
|
||||
$warned = true;
|
||||
}
|
||||
|
||||
$isValid = !empty($contents);
|
||||
} elseif (is_string($contents) && strlen($contents) > 0) {
|
||||
$contents = preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents);
|
||||
if (null === $contents) {
|
||||
// regex extraction failed
|
||||
$isValid = false;
|
||||
} else {
|
||||
$isValid = (bool) openssl_x509_parse($contents);
|
||||
}
|
||||
} else {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
if ($logger) {
|
||||
$logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid'));
|
||||
}
|
||||
|
||||
return self::$caFileValidity[$filename] = $isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if it is safe to use the PHP function openssl_x509_parse().
|
||||
*
|
||||
* This checks if OpenSSL extensions is vulnerable to remote code execution
|
||||
* via the exploit documented as CVE-2013-6420.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isOpensslParseSafe()
|
||||
{
|
||||
if (null !== self::$useOpensslParse) {
|
||||
return self::$useOpensslParse;
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 50600) {
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
// Vulnerable:
|
||||
// PHP 5.3.0 - PHP 5.3.27
|
||||
// PHP 5.4.0 - PHP 5.4.22
|
||||
// PHP 5.5.0 - PHP 5.5.6
|
||||
if (
|
||||
(PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328)
|
||||
|| (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423)
|
||||
|| PHP_VERSION_ID >= 50507
|
||||
) {
|
||||
// This version of PHP has the fix for CVE-2013-6420 applied.
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
// Windows is probably insecure in this case.
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
$compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
|
||||
$regex = '{^'.preg_quote($prefix).'([0-9]+)$}';
|
||||
|
||||
if (preg_match($regex, PHP_VERSION, $m)) {
|
||||
return ((int) $m[1]) >= $fixedVersion;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Hard coded list of PHP distributions with the fix backported.
|
||||
if (
|
||||
$compareDistroVersionPrefix('5.3.3-7+squeeze', 18) // Debian 6 (Squeeze)
|
||||
|| $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) // Debian 7 (Wheezy)
|
||||
|| $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) // Ubuntu 12.04 (Precise)
|
||||
) {
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
// Symfony Process component is missing so we assume it is unsafe at this point
|
||||
if (!class_exists('Symfony\Component\Process\PhpProcess')) {
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
// This is where things get crazy, because distros backport security
|
||||
// fixes the chances are on NIX systems the fix has been applied but
|
||||
// it's not possible to verify that from the PHP version.
|
||||
//
|
||||
// To verify exec a new PHP process and run the issue testcase with
|
||||
// known safe input that replicates the bug.
|
||||
|
||||
// Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
|
||||
// changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
|
||||
$cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
|
||||
$script = <<<'EOT'
|
||||
|
||||
error_reporting(-1);
|
||||
$info = openssl_x509_parse(base64_decode('%s'));
|
||||
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);
|
||||
|
||||
EOT;
|
||||
$script = '<'."?php\n".sprintf($script, $cert);
|
||||
|
||||
try {
|
||||
$process = new PhpProcess($script);
|
||||
$process->mustRun();
|
||||
} catch (\Exception $e) {
|
||||
// In the case of any exceptions just accept it is not possible to
|
||||
// determine the safety of openssl_x509_parse and bail out.
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
$output = preg_split('{\r?\n}', trim($process->getOutput()));
|
||||
$errorOutput = trim($process->getErrorOutput());
|
||||
|
||||
if (
|
||||
is_array($output)
|
||||
&& count($output) === 3
|
||||
&& $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION)
|
||||
&& $output[1] === 'string(27) "stefan.esser@sektioneins.de"'
|
||||
&& $output[2] === 'int(-1)'
|
||||
&& preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput)
|
||||
) {
|
||||
// This PHP has the fix backported probably by a distro security team.
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the static caches
|
||||
* @return void
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
self::$caFileValidity = array();
|
||||
self::$caPath = null;
|
||||
self::$useOpensslParse = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return string|false
|
||||
*/
|
||||
private static function getEnvVariable($name)
|
||||
{
|
||||
if (isset($_SERVER[$name])) {
|
||||
return (string) $_SERVER[$name];
|
||||
}
|
||||
|
||||
if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|false $certFile
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function caFileUsable($certFile, LoggerInterface $logger = null)
|
||||
{
|
||||
return $certFile
|
||||
&& static::isFile($certFile, $logger)
|
||||
&& static::isReadable($certFile, $logger)
|
||||
&& static::validateCaFile($certFile, $logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|false $certDir
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function caDirUsable($certDir, LoggerInterface $logger = null)
|
||||
{
|
||||
return $certDir
|
||||
&& static::isDir($certDir, $logger)
|
||||
&& static::isReadable($certDir, $logger)
|
||||
&& static::glob($certDir . '/*', $logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $certFile
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function isFile($certFile, LoggerInterface $logger = null)
|
||||
{
|
||||
$isFile = @is_file($certFile);
|
||||
if (!$isFile && $logger) {
|
||||
$logger->debug(sprintf('Checked CA file %s does not exist or it is not a file.', $certFile));
|
||||
}
|
||||
|
||||
return $isFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $certDir
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function isDir($certDir, LoggerInterface $logger = null)
|
||||
{
|
||||
$isDir = @is_dir($certDir);
|
||||
if (!$isDir && $logger) {
|
||||
$logger->debug(sprintf('Checked directory %s does not exist or it is not a directory.', $certDir));
|
||||
}
|
||||
|
||||
return $isDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $certFileOrDir
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function isReadable($certFileOrDir, LoggerInterface $logger = null)
|
||||
{
|
||||
$isReadable = @is_readable($certFileOrDir);
|
||||
if (!$isReadable && $logger) {
|
||||
$logger->debug(sprintf('Checked file or directory %s is not readable.', $certFileOrDir));
|
||||
}
|
||||
|
||||
return $isReadable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $pattern
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function glob($pattern, LoggerInterface $logger = null)
|
||||
{
|
||||
$certs = glob($pattern);
|
||||
if ($certs === false) {
|
||||
if ($logger) {
|
||||
$logger->debug(sprintf("An error occurred while trying to find certificates for pattern: %s", $pattern));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($certs) === 0) {
|
||||
if ($logger) {
|
||||
$logger->debug(sprintf("No CA files found for pattern: %s", $pattern));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of the POMO package.
|
||||
*/
|
||||
|
||||
namespace POMO\Streams;
|
||||
|
||||
/**
|
||||
* Reads the contents of the file in the beginning.
|
||||
*
|
||||
* @author Danilo Segan <danilo@kvota.net>
|
||||
*/
|
||||
class CachedFileReader extends StringReader implements StreamInterface
|
||||
{
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->_str = file_get_contents($filename);
|
||||
if (false === $this->_str) {
|
||||
return false;
|
||||
}
|
||||
$this->_pos = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of the POMO package.
|
||||
*/
|
||||
|
||||
namespace POMO\Streams;
|
||||
|
||||
/**
|
||||
* Reads the contents of the file in the beginning.
|
||||
*
|
||||
* @author Danilo Segan <danilo@kvota.net>
|
||||
*/
|
||||
class CachedIntFileReader extends CachedFileReader implements StreamInterface
|
||||
{
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql\Exception;
|
||||
|
||||
use Aura\Sql\Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
* Could not bind a value to a placeholder in a statement, generally because
|
||||
* the value is an array, object, or resource.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
class CannotBindValue extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql\Exception;
|
||||
|
||||
use Aura\Sql\Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
* ExtendedPdo could not disconnect; e.g., because its PDO connection was
|
||||
* created externally and then injected.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
class CannotDisconnect extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
/**
|
||||
* Capability interface declaring the known capabilities.
|
||||
*
|
||||
* @package Requests\Utilities
|
||||
*/
|
||||
|
||||
namespace WpOrg\Requests;
|
||||
|
||||
/**
|
||||
* Capability interface declaring the known capabilities.
|
||||
*
|
||||
* This is used as the authoritative source for which capabilities can be queried.
|
||||
*
|
||||
* @package Requests\Utilities
|
||||
*/
|
||||
interface Capability {
|
||||
|
||||
/**
|
||||
* Support for SSL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SSL = 'ssl';
|
||||
|
||||
/**
|
||||
* Collection of all capabilities supported in Requests.
|
||||
*
|
||||
* Note: this does not automatically mean that the capability will be supported for your chosen transport!
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
const ALL = [
|
||||
self::SSL,
|
||||
];
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
/**
|
||||
* Case-insensitive dictionary, suitable for HTTP headers
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Case-insensitive dictionary, suitable for HTTP headers
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
class Requests_Utility_CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate {
|
||||
/**
|
||||
* Actual item data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Creates a case insensitive dictionary.
|
||||
*
|
||||
* @param array $data Dictionary/map to convert to case-insensitive
|
||||
*/
|
||||
public function __construct(array $data = array()) {
|
||||
foreach ($data as $key => $value) {
|
||||
$this->offsetSet($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given item exists
|
||||
*
|
||||
* @param string $key Item key
|
||||
* @return boolean Does the item exist?
|
||||
*/
|
||||
public function offsetExists($key) {
|
||||
$key = strtolower($key);
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for the item
|
||||
*
|
||||
* @param string $key Item key
|
||||
* @return string Item value
|
||||
*/
|
||||
public function offsetGet($key) {
|
||||
$key = strtolower($key);
|
||||
if (!isset($this->data[$key])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->data[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given item
|
||||
*
|
||||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
|
||||
*
|
||||
* @param string $key Item name
|
||||
* @param string $value Item value
|
||||
*/
|
||||
public function offsetSet($key, $value) {
|
||||
if ($key === null) {
|
||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
||||
}
|
||||
|
||||
$key = strtolower($key);
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the given header
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public function offsetUnset($key) {
|
||||
unset($this->data[strtolower($key)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator for the data
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the headers as an array
|
||||
*
|
||||
* @return array Header data
|
||||
*/
|
||||
public function getAll() {
|
||||
return $this->data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* City-level data associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases besides
|
||||
* Country.
|
||||
*
|
||||
* @property-read int|null $confidence A value from 0-100 indicating MaxMind's
|
||||
* confidence that the city is correct. This attribute is only available
|
||||
* from the Insights service and the GeoIP2 Enterprise database.
|
||||
* @property-read int|null $geonameId The GeoName ID for the city. This attribute
|
||||
* is returned by all location services and databases.
|
||||
* @property-read string|null $name The name of the city based on the locales list
|
||||
* passed to the constructor. This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read array|null $names A array map where the keys are locale codes
|
||||
* and the values are names. This attribute is returned by all location
|
||||
* services and databases.
|
||||
*/
|
||||
class City extends AbstractPlaceRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['confidence', 'geonameId', 'names'];
|
||||
}
|
|
@ -0,0 +1,477 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
}
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
//no-op
|
||||
} elseif ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||
*
|
||||
* @return self[]
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
|
@ -0,0 +1,490 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MaxMind\WebService;
|
||||
|
||||
use Composer\CaBundle\CaBundle;
|
||||
use MaxMind\Exception\AuthenticationException;
|
||||
use MaxMind\Exception\HttpException;
|
||||
use MaxMind\Exception\InsufficientFundsException;
|
||||
use MaxMind\Exception\InvalidInputException;
|
||||
use MaxMind\Exception\InvalidRequestException;
|
||||
use MaxMind\Exception\IpAddressNotFoundException;
|
||||
use MaxMind\Exception\PermissionRequiredException;
|
||||
use MaxMind\Exception\WebServiceException;
|
||||
use MaxMind\WebService\Http\RequestFactory;
|
||||
|
||||
/**
|
||||
* This class is not intended to be used directly by an end-user of a
|
||||
* MaxMind web service. Please use the appropriate client API for the service
|
||||
* that you are using.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
const VERSION = '0.2.0';
|
||||
|
||||
private $caBundle;
|
||||
private $connectTimeout;
|
||||
private $host = 'api.maxmind.com';
|
||||
private $httpRequestFactory;
|
||||
private $licenseKey;
|
||||
private $proxy;
|
||||
private $timeout;
|
||||
private $userAgentPrefix;
|
||||
private $accountId;
|
||||
|
||||
/**
|
||||
* @param int $accountId your MaxMind account ID
|
||||
* @param string $licenseKey your MaxMind license key
|
||||
* @param array $options an array of options. Possible keys:
|
||||
* * `host` - The host to use when connecting to the web service.
|
||||
* * `userAgent` - The prefix of the User-Agent to use in the request.
|
||||
* * `caBundle` - The bundle of CA root certificates to use in the request.
|
||||
* * `connectTimeout` - The connect timeout to use for the request.
|
||||
* * `timeout` - The timeout to use for the request.
|
||||
* * `proxy` - The HTTP proxy to use. May include a schema, port,
|
||||
* username, and password, e.g., `http://username:password@127.0.0.1:10`.
|
||||
*/
|
||||
public function __construct(
|
||||
int $accountId,
|
||||
string $licenseKey,
|
||||
array $options = []
|
||||
) {
|
||||
$this->accountId = $accountId;
|
||||
$this->licenseKey = $licenseKey;
|
||||
|
||||
$this->httpRequestFactory = isset($options['httpRequestFactory'])
|
||||
? $options['httpRequestFactory']
|
||||
: new RequestFactory();
|
||||
|
||||
if (isset($options['host'])) {
|
||||
$this->host = $options['host'];
|
||||
}
|
||||
if (isset($options['userAgent'])) {
|
||||
$this->userAgentPrefix = $options['userAgent'] . ' ';
|
||||
}
|
||||
|
||||
$this->caBundle = isset($options['caBundle']) ?
|
||||
$this->caBundle = $options['caBundle'] : $this->getCaBundle();
|
||||
|
||||
if (isset($options['connectTimeout'])) {
|
||||
$this->connectTimeout = $options['connectTimeout'];
|
||||
}
|
||||
if (isset($options['timeout'])) {
|
||||
$this->timeout = $options['timeout'];
|
||||
}
|
||||
|
||||
if (isset($options['proxy'])) {
|
||||
$this->proxy = $options['proxy'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $service name of the service querying
|
||||
* @param string $path the URI path to use
|
||||
* @param array $input the data to be posted as JSON
|
||||
*
|
||||
* @throws InvalidInputException when the request has missing or invalid
|
||||
* data
|
||||
* @throws AuthenticationException when there is an issue authenticating the
|
||||
* request
|
||||
* @throws InsufficientFundsException when your account is out of funds
|
||||
* @throws InvalidRequestException when the request is invalid for some
|
||||
* other reason, e.g., invalid JSON in the POST.
|
||||
* @throws HttpException when an unexpected HTTP error occurs
|
||||
* @throws WebServiceException when some other error occurs. This also
|
||||
* serves as the base class for the above exceptions.
|
||||
*
|
||||
* @return array|null The decoded content of a successful response
|
||||
*/
|
||||
public function post(string $service, string $path, array $input): ?array
|
||||
{
|
||||
$requestBody = json_encode($input);
|
||||
if ($requestBody === false) {
|
||||
throw new InvalidInputException(
|
||||
'Error encoding input as JSON: '
|
||||
. $this->jsonErrorDescription()
|
||||
);
|
||||
}
|
||||
|
||||
$request = $this->createRequest(
|
||||
$path,
|
||||
['Content-Type: application/json']
|
||||
);
|
||||
|
||||
list($statusCode, $contentType, $responseBody) = $request->post($requestBody);
|
||||
|
||||
return $this->handleResponse(
|
||||
$statusCode,
|
||||
$contentType,
|
||||
$responseBody,
|
||||
$service,
|
||||
$path
|
||||
);
|
||||
}
|
||||
|
||||
public function get(string $service, string $path): ?array
|
||||
{
|
||||
$request = $this->createRequest($path);
|
||||
|
||||
list($statusCode, $contentType, $responseBody) = $request->get();
|
||||
|
||||
return $this->handleResponse(
|
||||
$statusCode,
|
||||
$contentType,
|
||||
$responseBody,
|
||||
$service,
|
||||
$path
|
||||
);
|
||||
}
|
||||
|
||||
private function userAgent(): string
|
||||
{
|
||||
$curlVersion = curl_version();
|
||||
|
||||
return $this->userAgentPrefix . 'MaxMind-WS-API/' . self::VERSION . ' PHP/' . PHP_VERSION .
|
||||
' curl/' . $curlVersion['version'];
|
||||
}
|
||||
|
||||
private function createRequest(string $path, array $headers = []): \MaxMind\WebService\Http\Request
|
||||
{
|
||||
array_push(
|
||||
$headers,
|
||||
'Authorization: Basic '
|
||||
. base64_encode($this->accountId . ':' . $this->licenseKey),
|
||||
'Accept: application/json'
|
||||
);
|
||||
|
||||
return $this->httpRequestFactory->request(
|
||||
$this->urlFor($path),
|
||||
[
|
||||
'caBundle' => $this->caBundle,
|
||||
'connectTimeout' => $this->connectTimeout,
|
||||
'headers' => $headers,
|
||||
'proxy' => $this->proxy,
|
||||
'timeout' => $this->timeout,
|
||||
'userAgent' => $this->userAgent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code of the response
|
||||
* @param string|null $contentType the Content-Type of the response
|
||||
* @param string|null $responseBody the response body
|
||||
* @param string $service the name of the service
|
||||
* @param string $path the path used in the request
|
||||
*
|
||||
* @throws AuthenticationException when there is an issue authenticating the
|
||||
* request
|
||||
* @throws InsufficientFundsException when your account is out of funds
|
||||
* @throws InvalidRequestException when the request is invalid for some
|
||||
* other reason, e.g., invalid JSON in the POST.
|
||||
* @throws HttpException when an unexpected HTTP error occurs
|
||||
* @throws WebServiceException when some other error occurs. This also
|
||||
* serves as the base class for the above exceptions
|
||||
*
|
||||
* @return array|null The decoded content of a successful response
|
||||
*/
|
||||
private function handleResponse(
|
||||
int $statusCode,
|
||||
?string $contentType,
|
||||
?string $responseBody,
|
||||
string $service,
|
||||
string $path
|
||||
): ?array {
|
||||
if ($statusCode >= 400 && $statusCode <= 499) {
|
||||
$this->handle4xx($statusCode, $contentType, $responseBody, $service, $path);
|
||||
} elseif ($statusCode >= 500) {
|
||||
$this->handle5xx($statusCode, $service, $path);
|
||||
} elseif ($statusCode !== 200 && $statusCode !== 204) {
|
||||
$this->handleUnexpectedStatus($statusCode, $service, $path);
|
||||
}
|
||||
|
||||
return $this->handleSuccess($statusCode, $responseBody, $service);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string describing the JSON error
|
||||
*/
|
||||
private function jsonErrorDescription(): string
|
||||
{
|
||||
$errno = json_last_error();
|
||||
switch ($errno) {
|
||||
case JSON_ERROR_DEPTH:
|
||||
return 'The maximum stack depth has been exceeded.';
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
return 'Invalid or malformed JSON.';
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
return 'Control character error.';
|
||||
case JSON_ERROR_SYNTAX:
|
||||
return 'Syntax error.';
|
||||
case JSON_ERROR_UTF8:
|
||||
return 'Malformed UTF-8 characters.';
|
||||
default:
|
||||
return "Other JSON error ($errno).";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path the path to use in the URL
|
||||
*
|
||||
* @return string the constructed URL
|
||||
*/
|
||||
private function urlFor(string $path): string
|
||||
{
|
||||
return 'https://' . $this->host . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string|null $contentType the response content-type
|
||||
* @param string|null $body the response body
|
||||
* @param string $service the service name
|
||||
* @param string $path the path used in the request
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws HttpException
|
||||
* @throws InsufficientFundsException
|
||||
* @throws InvalidRequestException
|
||||
*/
|
||||
private function handle4xx(
|
||||
int $statusCode,
|
||||
?string $contentType,
|
||||
?string $body,
|
||||
string $service,
|
||||
string $path
|
||||
): void {
|
||||
if ($body === null || $body === '') {
|
||||
throw new HttpException(
|
||||
"Received a $statusCode error for $service with no body",
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
}
|
||||
if ($contentType === null || !strstr($contentType, 'json')) {
|
||||
throw new HttpException(
|
||||
"Received a $statusCode error for $service with " .
|
||||
'the following body: ' . $body,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
}
|
||||
|
||||
$message = json_decode($body, true);
|
||||
if ($message === null) {
|
||||
throw new HttpException(
|
||||
"Received a $statusCode error for $service but could " .
|
||||
'not decode the response as JSON: '
|
||||
. $this->jsonErrorDescription() . ' Body: ' . $body,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($message['code']) || !isset($message['error'])) {
|
||||
throw new HttpException(
|
||||
'Error response contains JSON but it does not ' .
|
||||
'specify code or error keys: ' . $body,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
}
|
||||
|
||||
$this->handleWebServiceError(
|
||||
$message['error'],
|
||||
$message['code'],
|
||||
$statusCode,
|
||||
$path
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message the error message from the web service
|
||||
* @param string $code the error code from the web service
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string $path the path used in the request
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws InvalidRequestException
|
||||
* @throws InsufficientFundsException
|
||||
*/
|
||||
private function handleWebServiceError(
|
||||
string $message,
|
||||
string $code,
|
||||
int $statusCode,
|
||||
string $path
|
||||
): void {
|
||||
switch ($code) {
|
||||
case 'IP_ADDRESS_NOT_FOUND':
|
||||
case 'IP_ADDRESS_RESERVED':
|
||||
throw new IpAddressNotFoundException(
|
||||
$message,
|
||||
$code,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
case 'ACCOUNT_ID_REQUIRED':
|
||||
case 'ACCOUNT_ID_UNKNOWN':
|
||||
case 'AUTHORIZATION_INVALID':
|
||||
case 'LICENSE_KEY_REQUIRED':
|
||||
case 'USER_ID_REQUIRED':
|
||||
case 'USER_ID_UNKNOWN':
|
||||
throw new AuthenticationException(
|
||||
$message,
|
||||
$code,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
case 'OUT_OF_QUERIES':
|
||||
case 'INSUFFICIENT_FUNDS':
|
||||
throw new InsufficientFundsException(
|
||||
$message,
|
||||
$code,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
case 'PERMISSION_REQUIRED':
|
||||
throw new PermissionRequiredException(
|
||||
$message,
|
||||
$code,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
default:
|
||||
throw new InvalidRequestException(
|
||||
$message,
|
||||
$code,
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string $service the service name
|
||||
* @param string $path the URI path used in the request
|
||||
*
|
||||
* @throws HttpException
|
||||
*/
|
||||
private function handle5xx(int $statusCode, string $service, string $path): void
|
||||
{
|
||||
throw new HttpException(
|
||||
"Received a server error ($statusCode) for $service",
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string $service the service name
|
||||
* @param string $path the URI path used in the request
|
||||
*
|
||||
* @throws HttpException
|
||||
*/
|
||||
private function handleUnexpectedStatus(int $statusCode, string $service, string $path): void
|
||||
{
|
||||
throw new HttpException(
|
||||
'Received an unexpected HTTP status ' .
|
||||
"($statusCode) for $service",
|
||||
$statusCode,
|
||||
$this->urlFor($path)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string|null $body the successful request body
|
||||
* @param string $service the service name
|
||||
*
|
||||
* @throws WebServiceException if a response body is included but not
|
||||
* expected, or is not expected but not
|
||||
* included, or is expected and included
|
||||
* but cannot be decoded as JSON
|
||||
*
|
||||
* @return array|null the decoded request body
|
||||
*/
|
||||
private function handleSuccess(int $statusCode, ?string $body, string $service): ?array
|
||||
{
|
||||
// A 204 should have no response body
|
||||
if ($statusCode === 204) {
|
||||
if ($body !== null && $body !== '') {
|
||||
throw new WebServiceException(
|
||||
"Received a 204 response for $service along with an " .
|
||||
"unexpected HTTP body: $body"
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// A 200 should have a valid JSON body
|
||||
if ($body === null || $body === '') {
|
||||
throw new WebServiceException(
|
||||
"Received a 200 response for $service but did not " .
|
||||
'receive a HTTP body.'
|
||||
);
|
||||
}
|
||||
|
||||
$decodedContent = json_decode($body, true);
|
||||
if ($decodedContent === null) {
|
||||
throw new WebServiceException(
|
||||
"Received a 200 response for $service but could " .
|
||||
'not decode the response as JSON: '
|
||||
. $this->jsonErrorDescription() . ' Body: ' . $body
|
||||
);
|
||||
}
|
||||
|
||||
return $decodedContent;
|
||||
}
|
||||
|
||||
private function getCaBundle()
|
||||
{
|
||||
$curlVersion = curl_version();
|
||||
|
||||
// On OS X, when the SSL version is "SecureTransport", the system's
|
||||
// keychain will be used.
|
||||
if ($curlVersion['ssl_version'] === 'SecureTransport') {
|
||||
return null;
|
||||
}
|
||||
$cert = CaBundle::getSystemCaRootBundlePath();
|
||||
|
||||
// Check if the cert is inside a phar. If so, we need to copy the cert
|
||||
// to a temp file so that curl can see it.
|
||||
if (substr($cert, 0, 7) === 'phar://') {
|
||||
$tempDir = sys_get_temp_dir();
|
||||
$newCert = tempnam($tempDir, 'geoip2-');
|
||||
if ($newCert === false) {
|
||||
throw new \RuntimeException(
|
||||
"Unable to create temporary file in $tempDir"
|
||||
);
|
||||
}
|
||||
if (!copy($cert, $newCert)) {
|
||||
throw new \RuntimeException(
|
||||
"Could not copy $cert to $newCert: "
|
||||
. var_export(error_get_last(), true)
|
||||
);
|
||||
}
|
||||
|
||||
// We use a shutdown function rather than the destructor as the
|
||||
// destructor isn't called on a fatal error such as an uncaught
|
||||
// exception.
|
||||
register_shutdown_function(
|
||||
function () use ($newCert) {
|
||||
unlink($newCert);
|
||||
}
|
||||
);
|
||||
$cert = $newCert;
|
||||
}
|
||||
if (!file_exists($cert)) {
|
||||
throw new \RuntimeException("CA cert does not exist at $cert");
|
||||
}
|
||||
|
||||
return $cert;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Define the YOURLS config
|
||||
*/
|
||||
|
||||
namespace YOURLS\Config;
|
||||
|
||||
use YOURLS\Exceptions\ConfigException;
|
||||
|
||||
class Config {
|
||||
|
||||
/**
|
||||
* @param string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* @param mixed
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @since 1.7.3
|
||||
* @param mixed $config Optional user defined config path
|
||||
*/
|
||||
public function __construct($config = false) {
|
||||
$this->set_root( $this->fix_win32_path( dirname( dirname( __DIR__ ) ) ) );
|
||||
$this->set_config($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert antislashes to slashes
|
||||
*
|
||||
* @since 1.7.3
|
||||
* @param string $path
|
||||
* @return string path with \ converted to /
|
||||
*/
|
||||
public function fix_win32_path($path) {
|
||||
return str_replace('\\', '/', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.7.3
|
||||
* @param string path to config file
|
||||
* @return void
|
||||
*/
|
||||
public function set_config($config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.7.3
|
||||
* @param string path to YOURLS root directory
|
||||
* @return void
|
||||
*/
|
||||
public function set_root($root) {
|
||||
$this->root = $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find config.php, either user defined or from standard location
|
||||
*
|
||||
* @since 1.7.3
|
||||
* @return string path to found config file
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function find_config() {
|
||||
|
||||
$config = $this->fix_win32_path($this->config);
|
||||
|
||||
if (!empty($config) && is_readable($config)) {
|
||||
return $config;
|
||||
}
|
||||
|
||||
if (!empty($config) && !is_readable($config)) {
|
||||
throw new ConfigException("User defined config not found at '$config'");
|
||||
}
|
||||
|
||||
// config.php in /user/
|
||||
if (file_exists($this->root . '/user/config.php')) {
|
||||
return $this->root . '/user/config.php';
|
||||
}
|
||||
|
||||
// config.php in /includes/
|
||||
if (file_exists($this->root . '/includes/config.php')) {
|
||||
return $this->root . '/includes/config.php';
|
||||
}
|
||||
|
||||
// config.php not found :(
|
||||
|
||||
throw new ConfigException('Cannot find config.php. Please read the readme.html to learn how to install YOURLS');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define core constants that have not been user defined in config.php
|
||||
*
|
||||
* @since 1.7.3
|
||||
* @return void
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function define_core_constants() {
|
||||
// Check minimal config job has been properly done
|
||||
$must_haves = array('YOURLS_DB_USER', 'YOURLS_DB_PASS', 'YOURLS_DB_NAME', 'YOURLS_DB_HOST', 'YOURLS_DB_PREFIX', 'YOURLS_SITE');
|
||||
foreach($must_haves as $must_have) {
|
||||
if (!defined($must_have)) {
|
||||
throw new ConfigException('Config is incomplete (missing at least '.$must_have.') Check config-sample.php and edit your config accordingly');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following has an awful CRAP index and it would be much shorter reduced to something like
|
||||
* defining an array of ('YOURLS_SOMETHING' => 'default value') and then a simple loop over the
|
||||
* array, checking if $current is defined as a constant and otherwise define said constant with
|
||||
* its default value. I did not wrote it that way because that would make it difficult for code
|
||||
* parsers to identify which constants are defined and where. So, here it is, that long list of
|
||||
* if (!defined) define(). Ho and by the way, such beautiful comment, much right aligned, wow !
|
||||
*/
|
||||
|
||||
// physical path of YOURLS root
|
||||
if (!defined( 'YOURLS_ABSPATH' ))
|
||||
define('YOURLS_ABSPATH', $this->root);
|
||||
|
||||
// physical path of includes directory
|
||||
if (!defined( 'YOURLS_INC' ))
|
||||
define('YOURLS_INC', YOURLS_ABSPATH.'/includes');
|
||||
|
||||
// physical path of user directory
|
||||
if (!defined( 'YOURLS_USERDIR' ))
|
||||
define( 'YOURLS_USERDIR', YOURLS_ABSPATH.'/user' );
|
||||
|
||||
// URL of user directory
|
||||
if (!defined( 'YOURLS_USERURL' ))
|
||||
define( 'YOURLS_USERURL', trim(YOURLS_SITE, '/').'/user' );
|
||||
|
||||
// physical path of asset directory
|
||||
if( !defined( 'YOURLS_ASSETDIR' ) )
|
||||
define( 'YOURLS_ASSETDIR', YOURLS_ABSPATH.'/assets' );
|
||||
|
||||
// URL of asset directory
|
||||
if( !defined( 'YOURLS_ASSETURL' ) )
|
||||
define( 'YOURLS_ASSETURL', trim(YOURLS_SITE, '/').'/assets' );
|
||||
|
||||
// physical path of translations directory
|
||||
if (!defined( 'YOURLS_LANG_DIR' ))
|
||||
define( 'YOURLS_LANG_DIR', YOURLS_USERDIR.'/languages' );
|
||||
|
||||
// physical path of plugins directory
|
||||
if (!defined( 'YOURLS_PLUGINDIR' ))
|
||||
define( 'YOURLS_PLUGINDIR', YOURLS_USERDIR.'/plugins' );
|
||||
|
||||
// URL of plugins directory
|
||||
if (!defined( 'YOURLS_PLUGINURL' ))
|
||||
define( 'YOURLS_PLUGINURL', YOURLS_USERURL.'/plugins' );
|
||||
|
||||
// physical path of themes directory
|
||||
if( !defined( 'YOURLS_THEMEDIR' ) )
|
||||
define( 'YOURLS_THEMEDIR', YOURLS_USERDIR.'/themes' );
|
||||
|
||||
// URL of themes directory
|
||||
if( !defined( 'YOURLS_THEMEURL' ) )
|
||||
define( 'YOURLS_THEMEURL', YOURLS_USERURL.'/themes' );
|
||||
|
||||
// physical path of pages directory
|
||||
if (!defined( 'YOURLS_PAGEDIR' ))
|
||||
define('YOURLS_PAGEDIR', YOURLS_USERDIR.'/pages' );
|
||||
|
||||
// table to store URLs
|
||||
if (!defined( 'YOURLS_DB_TABLE_URL' ))
|
||||
define( 'YOURLS_DB_TABLE_URL', YOURLS_DB_PREFIX.'url' );
|
||||
|
||||
// table to store options
|
||||
if (!defined( 'YOURLS_DB_TABLE_OPTIONS' ))
|
||||
define( 'YOURLS_DB_TABLE_OPTIONS', YOURLS_DB_PREFIX.'options' );
|
||||
|
||||
// table to store hits, for stats
|
||||
if (!defined( 'YOURLS_DB_TABLE_LOG' ))
|
||||
define( 'YOURLS_DB_TABLE_LOG', YOURLS_DB_PREFIX.'log' );
|
||||
|
||||
// minimum delay in sec before a same IP can add another URL. Note: logged in users are not throttled down.
|
||||
if (!defined( 'YOURLS_FLOOD_DELAY_SECONDS' ))
|
||||
define( 'YOURLS_FLOOD_DELAY_SECONDS', 15 );
|
||||
|
||||
// comma separated list of IPs that can bypass flood check.
|
||||
if (!defined( 'YOURLS_FLOOD_IP_WHITELIST' ))
|
||||
define( 'YOURLS_FLOOD_IP_WHITELIST', '' );
|
||||
|
||||
// life span of an auth cookie in seconds (60*60*24*7 = 7 days)
|
||||
if (!defined( 'YOURLS_COOKIE_LIFE' ))
|
||||
define( 'YOURLS_COOKIE_LIFE', 60*60*24*7 );
|
||||
|
||||
// life span of a nonce in seconds
|
||||
if (!defined( 'YOURLS_NONCE_LIFE' ))
|
||||
define( 'YOURLS_NONCE_LIFE', 43200 ); // 3600 * 12
|
||||
|
||||
// if set to true, disable stat logging (no use for it, too busy servers, ...)
|
||||
if (!defined( 'YOURLS_NOSTATS' ))
|
||||
define( 'YOURLS_NOSTATS', false );
|
||||
|
||||
// if set to true, force https:// in the admin area
|
||||
if (!defined( 'YOURLS_ADMIN_SSL' ))
|
||||
define( 'YOURLS_ADMIN_SSL', false );
|
||||
|
||||
// if set to true, verbose debug infos. Will break things. Don't enable.
|
||||
if (!defined( 'YOURLS_DEBUG' ))
|
||||
define( 'YOURLS_DEBUG', false );
|
||||
|
||||
// Error reporting
|
||||
if (defined( 'YOURLS_DEBUG' ) && YOURLS_DEBUG == true ) {
|
||||
error_reporting( -1 );
|
||||
} else {
|
||||
error_reporting( E_ERROR | E_PARSE );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace YOURLS\Exceptions;
|
||||
|
||||
class ConfigException extends \Exception {}
|
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql;
|
||||
|
||||
/**
|
||||
*
|
||||
* Manages ExtendedPdo instances for default, read, and write connections.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
class ConnectionLocator implements ConnectionLocatorInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* A default ExtendedPdo connection factory/instance.
|
||||
*
|
||||
* @var callable
|
||||
*
|
||||
*/
|
||||
protected $default;
|
||||
|
||||
/**
|
||||
*
|
||||
* A registry of ExtendedPdo "read" factories/instances.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $read = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* A registry of ExtendedPdo "write" factories/instances.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $write = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* Constructor.
|
||||
*
|
||||
* @param callable $default A callable to create a default connection.
|
||||
*
|
||||
* @param array $read An array of callables to create read connections.
|
||||
*
|
||||
* @param array $write An array of callables to create write connections.
|
||||
*
|
||||
*/
|
||||
public function __construct(
|
||||
$default = null,
|
||||
array $read = [],
|
||||
array $write = []
|
||||
) {
|
||||
if ($default) {
|
||||
$this->setDefault($default);
|
||||
}
|
||||
foreach ($read as $name => $callable) {
|
||||
$this->setRead($name, $callable);
|
||||
}
|
||||
foreach ($write as $name => $callable) {
|
||||
$this->setWrite($name, $callable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets the default connection factory.
|
||||
*
|
||||
* @param callable $callable The factory for the connection.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function setDefault(callable $callable)
|
||||
{
|
||||
$this->default = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the default connection object.
|
||||
*
|
||||
* @return ExtendedPdoInterface
|
||||
*
|
||||
*/
|
||||
public function getDefault()
|
||||
{
|
||||
if (! $this->default instanceof ExtendedPdo) {
|
||||
$this->default = call_user_func($this->default);
|
||||
}
|
||||
|
||||
return $this->default;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets a read connection factory by name.
|
||||
*
|
||||
* @param string $name The name of the connection.
|
||||
*
|
||||
* @param callable $callable The factory for the connection.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function setRead($name, callable $callable)
|
||||
{
|
||||
$this->read[$name] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a read connection by name; if no name is given, picks a
|
||||
* random connection; if no read connections are present, returns the
|
||||
* default connection.
|
||||
*
|
||||
* @param string $name The read connection name to return.
|
||||
*
|
||||
* @return ExtendedPdoInterface
|
||||
*
|
||||
*/
|
||||
public function getRead($name = '')
|
||||
{
|
||||
return $this->getConnection('read', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets a write connection factory by name.
|
||||
*
|
||||
* @param string $name The name of the connection.
|
||||
*
|
||||
* @param callable $callable The factory for the connection.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function setWrite($name, callable $callable)
|
||||
{
|
||||
$this->write[$name] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a write connection by name; if no name is given, picks a
|
||||
* random connection; if no write connections are present, returns the
|
||||
* default connection.
|
||||
*
|
||||
* @param string $name The write connection name to return.
|
||||
*
|
||||
* @return ExtendedPdoInterface
|
||||
*
|
||||
*/
|
||||
public function getWrite($name = '')
|
||||
{
|
||||
return $this->getConnection('write', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a connection by name.
|
||||
*
|
||||
* @param string $type The connection type ('read' or 'write').
|
||||
*
|
||||
* @param string $name The name of the connection.
|
||||
*
|
||||
* @return ExtendedPdoInterface
|
||||
*
|
||||
* @throws Exception\ConnectionNotFound
|
||||
*
|
||||
*/
|
||||
protected function getConnection($type, $name)
|
||||
{
|
||||
$conn = &$this->{$type};
|
||||
|
||||
if (empty($conn)) {
|
||||
return $this->getDefault();
|
||||
}
|
||||
|
||||
if ($name === '') {
|
||||
$name = array_rand($conn);
|
||||
}
|
||||
|
||||
if (! isset($conn[$name])) {
|
||||
throw new Exception\ConnectionNotFound("{$type}:{$name}");
|
||||
}
|
||||
|
||||
if (! $conn[$name] instanceof ExtendedPdo) {
|
||||
$conn[$name] = call_user_func($conn[$name]);
|
||||
}
|
||||
|
||||
return $conn[$name];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql;
|
||||
|
||||
/**
|
||||
*
|
||||
* Locates PDO connections for default, read, and write databases.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
interface ConnectionLocatorInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Sets the default connection registry entry.
|
||||
*
|
||||
* @param callable $callable The registry entry.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function setDefault(callable $callable);
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the default connection object.
|
||||
*
|
||||
* @return ExtendedPdoInterface
|
||||
*
|
||||
*/
|
||||
public function getDefault();
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets a read connection registry entry by name.
|
||||
*
|
||||
* @param string $name The name of the registry entry.
|
||||
*
|
||||
* @param callable $callable The registry entry.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function setRead($name, callable $callable);
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a read connection by name; if no name is given, picks a
|
||||
* random connection; if no read connections are present, returns the
|
||||
* default connection.
|
||||
*
|
||||
* @param string $name The read connection name to return.
|
||||
*
|
||||
* @return ExtendedPdoInterface
|
||||
*
|
||||
*/
|
||||
public function getRead($name = '');
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets a write connection registry entry by name.
|
||||
*
|
||||
* @param string $name The name of the registry entry.
|
||||
*
|
||||
* @param callable $callable The registry entry.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function setWrite($name, callable $callable);
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a write connection by name; if no name is given, picks a
|
||||
* random connection; if no write connections are present, returns the
|
||||
* default connection.
|
||||
*
|
||||
* @param string $name The write connection name to return.
|
||||
*
|
||||
* @return ExtendedPdoInterface
|
||||
*
|
||||
*/
|
||||
public function getWrite($name = '');
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql\Exception;
|
||||
|
||||
use Aura\Sql\Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
* Locator could not find a named connection.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
class ConnectionNotFound extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Model;
|
||||
|
||||
use GeoIp2\Util;
|
||||
|
||||
/**
|
||||
* This class provides the GeoIP2 Connection-Type model.
|
||||
*
|
||||
* @property-read string|null $connectionType The connection type may take the
|
||||
* following values: "Dialup", "Cable/DSL", "Corporate", "Cellular".
|
||||
* Additional values may be added in the future.
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class ConnectionType extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $connectionType;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
|
||||
$this->connectionType = $this->get('connection_type');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the continent record associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases.
|
||||
*
|
||||
* @property-read string|null $code A two character continent code like "NA" (North
|
||||
* America) or "OC" (Oceania). This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read int|null $geonameId The GeoName ID for the continent. This
|
||||
* attribute is returned by all location services and databases.
|
||||
* @property-read string|null $name Returns the name of the continent based on the
|
||||
* locales list passed to the constructor. This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read array|null $names An array map where the keys are locale codes
|
||||
* and the values are names. This attribute is returned by all location
|
||||
* services and databases.
|
||||
*/
|
||||
class Continent extends AbstractPlaceRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = [
|
||||
'code',
|
||||
'geonameId',
|
||||
'names',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,500 @@
|
|||
<?php
|
||||
/**
|
||||
* Cookie storage object
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Cookies
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cookie storage object
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Cookies
|
||||
*/
|
||||
class Requests_Cookie {
|
||||
/**
|
||||
* Cookie name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Cookie value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Cookie attributes
|
||||
*
|
||||
* Valid keys are (currently) path, domain, expires, max-age, secure and
|
||||
* httponly.
|
||||
*
|
||||
* @var Requests_Utility_CaseInsensitiveDictionary|array Array-like object
|
||||
*/
|
||||
public $attributes = array();
|
||||
|
||||
/**
|
||||
* Cookie flags
|
||||
*
|
||||
* Valid keys are (currently) creation, last-access, persistent and
|
||||
* host-only.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $flags = array();
|
||||
|
||||
/**
|
||||
* Reference time for relative calculations
|
||||
*
|
||||
* This is used in place of `time()` when calculating Max-Age expiration and
|
||||
* checking time validity.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $reference_time = 0;
|
||||
|
||||
/**
|
||||
* Create a new cookie object
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @param array|Requests_Utility_CaseInsensitiveDictionary $attributes Associative array of attribute data
|
||||
*/
|
||||
public function __construct($name, $value, $attributes = array(), $flags = array(), $reference_time = null) {
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
$this->attributes = $attributes;
|
||||
$default_flags = array(
|
||||
'creation' => time(),
|
||||
'last-access' => time(),
|
||||
'persistent' => false,
|
||||
'host-only' => true,
|
||||
);
|
||||
$this->flags = array_merge($default_flags, $flags);
|
||||
|
||||
$this->reference_time = time();
|
||||
if ($reference_time !== null) {
|
||||
$this->reference_time = $reference_time;
|
||||
}
|
||||
|
||||
$this->normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a cookie is expired.
|
||||
*
|
||||
* Checks the age against $this->reference_time to determine if the cookie
|
||||
* is expired.
|
||||
*
|
||||
* @return boolean True if expired, false if time is valid.
|
||||
*/
|
||||
public function is_expired() {
|
||||
// RFC6265, s. 4.1.2.2:
|
||||
// If a cookie has both the Max-Age and the Expires attribute, the Max-
|
||||
// Age attribute has precedence and controls the expiration date of the
|
||||
// cookie.
|
||||
if (isset($this->attributes['max-age'])) {
|
||||
$max_age = $this->attributes['max-age'];
|
||||
return $max_age < $this->reference_time;
|
||||
}
|
||||
|
||||
if (isset($this->attributes['expires'])) {
|
||||
$expires = $this->attributes['expires'];
|
||||
return $expires < $this->reference_time;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a cookie is valid for a given URI
|
||||
*
|
||||
* @param Requests_IRI $uri URI to check
|
||||
* @return boolean Whether the cookie is valid for the given URI
|
||||
*/
|
||||
public function uri_matches(Requests_IRI $uri) {
|
||||
if (!$this->domain_matches($uri->host)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->path_matches($uri->path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return empty($this->attributes['secure']) || $uri->scheme === 'https';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a cookie is valid for a given domain
|
||||
*
|
||||
* @param string $string Domain to check
|
||||
* @return boolean Whether the cookie is valid for the given domain
|
||||
*/
|
||||
public function domain_matches($string) {
|
||||
if (!isset($this->attributes['domain'])) {
|
||||
// Cookies created manually; cookies created by Requests will set
|
||||
// the domain to the requested domain
|
||||
return true;
|
||||
}
|
||||
|
||||
$domain_string = $this->attributes['domain'];
|
||||
if ($domain_string === $string) {
|
||||
// The domain string and the string are identical.
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the cookie is marked as host-only and we don't have an exact
|
||||
// match, reject the cookie
|
||||
if ($this->flags['host-only'] === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen($string) <= strlen($domain_string)) {
|
||||
// For obvious reasons, the string cannot be a suffix if the domain
|
||||
// is shorter than the domain string
|
||||
return false;
|
||||
}
|
||||
|
||||
if (substr($string, -1 * strlen($domain_string)) !== $domain_string) {
|
||||
// The domain string should be a suffix of the string.
|
||||
return false;
|
||||
}
|
||||
|
||||
$prefix = substr($string, 0, strlen($string) - strlen($domain_string));
|
||||
if (substr($prefix, -1) !== '.') {
|
||||
// The last character of the string that is not included in the
|
||||
// domain string should be a %x2E (".") character.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The string should be a host name (i.e., not an IP address).
|
||||
return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a cookie is valid for a given path
|
||||
*
|
||||
* From the path-match check in RFC 6265 section 5.1.4
|
||||
*
|
||||
* @param string $request_path Path to check
|
||||
* @return boolean Whether the cookie is valid for the given path
|
||||
*/
|
||||
public function path_matches($request_path) {
|
||||
if (empty($request_path)) {
|
||||
// Normalize empty path to root
|
||||
$request_path = '/';
|
||||
}
|
||||
|
||||
if (!isset($this->attributes['path'])) {
|
||||
// Cookies created manually; cookies created by Requests will set
|
||||
// the path to the requested path
|
||||
return true;
|
||||
}
|
||||
|
||||
$cookie_path = $this->attributes['path'];
|
||||
|
||||
if ($cookie_path === $request_path) {
|
||||
// The cookie-path and the request-path are identical.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen($request_path) > strlen($cookie_path) && substr($request_path, 0, strlen($cookie_path)) === $cookie_path) {
|
||||
if (substr($cookie_path, -1) === '/') {
|
||||
// The cookie-path is a prefix of the request-path, and the last
|
||||
// character of the cookie-path is %x2F ("/").
|
||||
return true;
|
||||
}
|
||||
|
||||
if (substr($request_path, strlen($cookie_path), 1) === '/') {
|
||||
// The cookie-path is a prefix of the request-path, and the
|
||||
// first character of the request-path that is not included in
|
||||
// the cookie-path is a %x2F ("/") character.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize cookie and attributes
|
||||
*
|
||||
* @return boolean Whether the cookie was successfully normalized
|
||||
*/
|
||||
public function normalize() {
|
||||
foreach ($this->attributes as $key => $value) {
|
||||
$orig_value = $value;
|
||||
$value = $this->normalize_attribute($key, $value);
|
||||
if ($value === null) {
|
||||
unset($this->attributes[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value !== $orig_value) {
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an individual cookie attribute
|
||||
*
|
||||
* Handles parsing individual attributes from the cookie values.
|
||||
*
|
||||
* @param string $name Attribute name
|
||||
* @param string|boolean $value Attribute value (string value, or true if empty/flag)
|
||||
* @return mixed Value if available, or null if the attribute value is invalid (and should be skipped)
|
||||
*/
|
||||
protected function normalize_attribute($name, $value) {
|
||||
switch (strtolower($name)) {
|
||||
case 'expires':
|
||||
// Expiration parsing, as per RFC 6265 section 5.2.1
|
||||
if (is_int($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$expiry_time = strtotime($value);
|
||||
if ($expiry_time === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $expiry_time;
|
||||
|
||||
case 'max-age':
|
||||
// Expiration parsing, as per RFC 6265 section 5.2.2
|
||||
if (is_int($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Check that we have a valid age
|
||||
if (!preg_match('/^-?\d+$/', $value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$delta_seconds = (int) $value;
|
||||
if ($delta_seconds <= 0) {
|
||||
$expiry_time = 0;
|
||||
}
|
||||
else {
|
||||
$expiry_time = $this->reference_time + $delta_seconds;
|
||||
}
|
||||
|
||||
return $expiry_time;
|
||||
|
||||
case 'domain':
|
||||
// Domain normalization, as per RFC 6265 section 5.2.3
|
||||
if ($value[0] === '.') {
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a cookie for a Cookie header
|
||||
*
|
||||
* This is used when sending cookies to a server.
|
||||
*
|
||||
* @return string Cookie formatted for Cookie header
|
||||
*/
|
||||
public function format_for_header() {
|
||||
return sprintf('%s=%s', $this->name, $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a cookie for a Cookie header
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @deprecated Use {@see Requests_Cookie::format_for_header}
|
||||
* @return string
|
||||
*/
|
||||
public function formatForHeader() {
|
||||
return $this->format_for_header();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a cookie for a Set-Cookie header
|
||||
*
|
||||
* This is used when sending cookies to clients. This isn't really
|
||||
* applicable to client-side usage, but might be handy for debugging.
|
||||
*
|
||||
* @return string Cookie formatted for Set-Cookie header
|
||||
*/
|
||||
public function format_for_set_cookie() {
|
||||
$header_value = $this->format_for_header();
|
||||
if (!empty($this->attributes)) {
|
||||
$parts = array();
|
||||
foreach ($this->attributes as $key => $value) {
|
||||
// Ignore non-associative attributes
|
||||
if (is_numeric($key)) {
|
||||
$parts[] = $value;
|
||||
}
|
||||
else {
|
||||
$parts[] = sprintf('%s=%s', $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$header_value .= '; ' . implode('; ', $parts);
|
||||
}
|
||||
return $header_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a cookie for a Set-Cookie header
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @deprecated Use {@see Requests_Cookie::format_for_set_cookie}
|
||||
* @return string
|
||||
*/
|
||||
public function formatForSetCookie() {
|
||||
return $this->format_for_set_cookie();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cookie value
|
||||
*
|
||||
* Attributes and other data can be accessed via methods.
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a cookie string into a cookie object
|
||||
*
|
||||
* Based on Mozilla's parsing code in Firefox and related projects, which
|
||||
* is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265
|
||||
* specifies some of this handling, but not in a thorough manner.
|
||||
*
|
||||
* @param string Cookie header value (from a Set-Cookie header)
|
||||
* @return Requests_Cookie Parsed cookie object
|
||||
*/
|
||||
public static function parse($string, $name = '', $reference_time = null) {
|
||||
$parts = explode(';', $string);
|
||||
$kvparts = array_shift($parts);
|
||||
|
||||
if (!empty($name)) {
|
||||
$value = $string;
|
||||
}
|
||||
elseif (strpos($kvparts, '=') === false) {
|
||||
// Some sites might only have a value without the equals separator.
|
||||
// Deviate from RFC 6265 and pretend it was actually a blank name
|
||||
// (`=foo`)
|
||||
//
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=169091
|
||||
$name = '';
|
||||
$value = $kvparts;
|
||||
}
|
||||
else {
|
||||
list($name, $value) = explode('=', $kvparts, 2);
|
||||
}
|
||||
$name = trim($name);
|
||||
$value = trim($value);
|
||||
|
||||
// Attribute key are handled case-insensitively
|
||||
$attributes = new Requests_Utility_CaseInsensitiveDictionary();
|
||||
|
||||
if (!empty($parts)) {
|
||||
foreach ($parts as $part) {
|
||||
if (strpos($part, '=') === false) {
|
||||
$part_key = $part;
|
||||
$part_value = true;
|
||||
}
|
||||
else {
|
||||
list($part_key, $part_value) = explode('=', $part, 2);
|
||||
$part_value = trim($part_value);
|
||||
}
|
||||
|
||||
$part_key = trim($part_key);
|
||||
$attributes[$part_key] = $part_value;
|
||||
}
|
||||
}
|
||||
|
||||
return new Requests_Cookie($name, $value, $attributes, array(), $reference_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse all Set-Cookie headers from request headers
|
||||
*
|
||||
* @param Requests_Response_Headers $headers Headers to parse from
|
||||
* @param Requests_IRI|null $origin URI for comparing cookie origins
|
||||
* @param int|null $time Reference time for expiration calculation
|
||||
* @return array
|
||||
*/
|
||||
public static function parse_from_headers(Requests_Response_Headers $headers, Requests_IRI $origin = null, $time = null) {
|
||||
$cookie_headers = $headers->getValues('Set-Cookie');
|
||||
if (empty($cookie_headers)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$cookies = array();
|
||||
foreach ($cookie_headers as $header) {
|
||||
$parsed = self::parse($header, '', $time);
|
||||
|
||||
// Default domain/path attributes
|
||||
if (empty($parsed->attributes['domain']) && !empty($origin)) {
|
||||
$parsed->attributes['domain'] = $origin->host;
|
||||
$parsed->flags['host-only'] = true;
|
||||
}
|
||||
else {
|
||||
$parsed->flags['host-only'] = false;
|
||||
}
|
||||
|
||||
$path_is_valid = (!empty($parsed->attributes['path']) && $parsed->attributes['path'][0] === '/');
|
||||
if (!$path_is_valid && !empty($origin)) {
|
||||
$path = $origin->path;
|
||||
|
||||
// Default path normalization as per RFC 6265 section 5.1.4
|
||||
if (substr($path, 0, 1) !== '/') {
|
||||
// If the uri-path is empty or if the first character of
|
||||
// the uri-path is not a %x2F ("/") character, output
|
||||
// %x2F ("/") and skip the remaining steps.
|
||||
$path = '/';
|
||||
}
|
||||
elseif (substr_count($path, '/') === 1) {
|
||||
// If the uri-path contains no more than one %x2F ("/")
|
||||
// character, output %x2F ("/") and skip the remaining
|
||||
// step.
|
||||
$path = '/';
|
||||
}
|
||||
else {
|
||||
// Output the characters of the uri-path from the first
|
||||
// character up to, but not including, the right-most
|
||||
// %x2F ("/").
|
||||
$path = substr($path, 0, strrpos($path, '/'));
|
||||
}
|
||||
$parsed->attributes['path'] = $path;
|
||||
}
|
||||
|
||||
// Reject invalid cookie domains
|
||||
if (!empty($origin) && !$parsed->domain_matches($origin->host)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cookies[$parsed->name] = $parsed;
|
||||
}
|
||||
|
||||
return $cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse all Set-Cookie headers from request headers
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @deprecated Use {@see Requests_Cookie::parse_from_headers}
|
||||
* @return string
|
||||
*/
|
||||
public static function parseFromHeaders(Requests_Response_Headers $headers) {
|
||||
return self::parse_from_headers($headers);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the country record associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases.
|
||||
*
|
||||
* @property-read int|null $confidence A value from 0-100 indicating MaxMind's
|
||||
* confidence that the country is correct. This attribute is only available
|
||||
* from the Insights service and the GeoIP2 Enterprise database.
|
||||
* @property-read int|null $geonameId The GeoName ID for the country. This
|
||||
* attribute is returned by all location services and databases.
|
||||
* @property-read bool $isInEuropeanUnion This is true if the country is a
|
||||
* member state of the European Union. This attribute is returned by all
|
||||
* location services and databases.
|
||||
* @property-read string|null $isoCode The two-character ISO 3166-1 alpha code
|
||||
* for the country. See https://en.wikipedia.org/wiki/ISO_3166-1. This
|
||||
* attribute is returned by all location services and databases.
|
||||
* @property-read string|null $name The name of the country based on the locales
|
||||
* list passed to the constructor. This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read array|null $names An array map where the keys are locale codes
|
||||
* and the values are names. This attribute is returned by all location
|
||||
* services and databases.
|
||||
*/
|
||||
class Country extends AbstractPlaceRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = [
|
||||
'confidence',
|
||||
'geonameId',
|
||||
'isInEuropeanUnion',
|
||||
'isoCode',
|
||||
'names',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
/**
|
||||
* CURL Transport Exception.
|
||||
*
|
||||
* @package Requests\Exceptions
|
||||
*/
|
||||
|
||||
namespace WpOrg\Requests\Exception\Transport;
|
||||
|
||||
use WpOrg\Requests\Exception\Transport;
|
||||
|
||||
/**
|
||||
* CURL Transport Exception.
|
||||
*
|
||||
* @package Requests\Exceptions
|
||||
*/
|
||||
final class Curl extends Transport {
|
||||
|
||||
const EASY = 'cURLEasy';
|
||||
const MULTI = 'cURLMulti';
|
||||
const SHARE = 'cURLShare';
|
||||
|
||||
/**
|
||||
* cURL error code
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $code = -1;
|
||||
|
||||
/**
|
||||
* Which type of cURL error
|
||||
*
|
||||
* EASY|MULTI|SHARE
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'Unknown';
|
||||
|
||||
/**
|
||||
* Clear text error message
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reason = 'Unknown';
|
||||
|
||||
/**
|
||||
* Create a new exception.
|
||||
*
|
||||
* @param string $message Exception message.
|
||||
* @param string $type Exception type.
|
||||
* @param mixed $data Associated data, if applicable.
|
||||
* @param int $code Exception numerical code, if applicable.
|
||||
*/
|
||||
public function __construct($message, $type, $data = null, $code = 0) {
|
||||
if ($type !== null) {
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
if ($code !== null) {
|
||||
$this->code = (int) $code;
|
||||
}
|
||||
|
||||
if ($message !== null) {
|
||||
$this->reason = $message;
|
||||
}
|
||||
|
||||
$message = sprintf('%d %s', $this->code, $this->reason);
|
||||
parent::__construct($message, $this->type, $data, $this->code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReason() {
|
||||
return $this->reason;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MaxMind\WebService\Http;
|
||||
|
||||
use MaxMind\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* This class is for internal use only. Semantic versioning does not not apply.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CurlRequest implements Request
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $ch;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $options;
|
||||
|
||||
public function __construct(string $url, array $options)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->options = $options;
|
||||
$this->ch = $options['curlHandle'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HttpException
|
||||
*/
|
||||
public function post(string $body): array
|
||||
{
|
||||
$curl = $this->createCurl();
|
||||
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
|
||||
|
||||
return $this->execute($curl);
|
||||
}
|
||||
|
||||
public function get(): array
|
||||
{
|
||||
$curl = $this->createCurl();
|
||||
|
||||
curl_setopt($curl, CURLOPT_HTTPGET, true);
|
||||
|
||||
return $this->execute($curl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return resource
|
||||
*/
|
||||
private function createCurl()
|
||||
{
|
||||
curl_reset($this->ch);
|
||||
|
||||
$opts = [];
|
||||
$opts[CURLOPT_URL] = $this->url;
|
||||
|
||||
if (!empty($this->options['caBundle'])) {
|
||||
$opts[CURLOPT_CAINFO] = $this->options['caBundle'];
|
||||
}
|
||||
|
||||
$opts[CURLOPT_ENCODING] = '';
|
||||
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$opts[CURLOPT_FOLLOWLOCATION] = false;
|
||||
$opts[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$opts[CURLOPT_RETURNTRANSFER] = true;
|
||||
|
||||
$opts[CURLOPT_HTTPHEADER] = $this->options['headers'];
|
||||
$opts[CURLOPT_USERAGENT] = $this->options['userAgent'];
|
||||
$opts[CURLOPT_PROXY] = $this->options['proxy'];
|
||||
|
||||
// The defined()s are here as the *_MS opts are not available on older
|
||||
// cURL versions
|
||||
$connectTimeout = $this->options['connectTimeout'];
|
||||
if (\defined('CURLOPT_CONNECTTIMEOUT_MS')) {
|
||||
$opts[CURLOPT_CONNECTTIMEOUT_MS] = ceil($connectTimeout * 1000);
|
||||
} else {
|
||||
$opts[CURLOPT_CONNECTTIMEOUT] = ceil($connectTimeout);
|
||||
}
|
||||
|
||||
$timeout = $this->options['timeout'];
|
||||
if (\defined('CURLOPT_TIMEOUT_MS')) {
|
||||
$opts[CURLOPT_TIMEOUT_MS] = ceil($timeout * 1000);
|
||||
} else {
|
||||
$opts[CURLOPT_TIMEOUT] = ceil($timeout);
|
||||
}
|
||||
|
||||
curl_setopt_array($this->ch, $opts);
|
||||
|
||||
return $this->ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $curl
|
||||
*
|
||||
* @throws HttpException
|
||||
*/
|
||||
private function execute($curl): array
|
||||
{
|
||||
$body = curl_exec($curl);
|
||||
if ($errno = curl_errno($curl)) {
|
||||
$errorMessage = curl_error($curl);
|
||||
|
||||
throw new HttpException(
|
||||
"cURL error ({$errno}): {$errorMessage}",
|
||||
0,
|
||||
$this->url
|
||||
);
|
||||
}
|
||||
|
||||
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
$contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
|
||||
|
||||
return [
|
||||
$statusCode,
|
||||
// The PHP docs say "Content-Type: of the requested document. NULL
|
||||
// indicates server did not send valid Content-Type: header" for
|
||||
// CURLINFO_CONTENT_TYPE. However, it will return FALSE if no header
|
||||
// is set. To keep our types simple, we return null in this case.
|
||||
($contentType === false ? null : $contentType),
|
||||
$body,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MaxMind\Db\Reader;
|
||||
|
||||
// @codingStandardsIgnoreLine
|
||||
use RuntimeException;
|
||||
|
||||
class Decoder
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $fileStream;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $pointerBase;
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $pointerBaseByteSize;
|
||||
/**
|
||||
* This is only used for unit testing.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $pointerTestHack;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $switchByteOrder;
|
||||
|
||||
private const _EXTENDED = 0;
|
||||
private const _POINTER = 1;
|
||||
private const _UTF8_STRING = 2;
|
||||
private const _DOUBLE = 3;
|
||||
private const _BYTES = 4;
|
||||
private const _UINT16 = 5;
|
||||
private const _UINT32 = 6;
|
||||
private const _MAP = 7;
|
||||
private const _INT32 = 8;
|
||||
private const _UINT64 = 9;
|
||||
private const _UINT128 = 10;
|
||||
private const _ARRAY = 11;
|
||||
private const _CONTAINER = 12;
|
||||
private const _END_MARKER = 13;
|
||||
private const _BOOLEAN = 14;
|
||||
private const _FLOAT = 15;
|
||||
|
||||
/**
|
||||
* @param resource $fileStream
|
||||
*/
|
||||
public function __construct(
|
||||
$fileStream,
|
||||
int $pointerBase = 0,
|
||||
bool $pointerTestHack = false
|
||||
) {
|
||||
$this->fileStream = $fileStream;
|
||||
$this->pointerBase = $pointerBase;
|
||||
|
||||
$this->pointerBaseByteSize = $pointerBase > 0 ? log($pointerBase, 2) / 8 : 0;
|
||||
$this->pointerTestHack = $pointerTestHack;
|
||||
|
||||
$this->switchByteOrder = $this->isPlatformLittleEndian();
|
||||
}
|
||||
|
||||
public function decode(int $offset): array
|
||||
{
|
||||
$ctrlByte = \ord(Util::read($this->fileStream, $offset, 1));
|
||||
++$offset;
|
||||
|
||||
$type = $ctrlByte >> 5;
|
||||
|
||||
// Pointers are a special case, we don't read the next $size bytes, we
|
||||
// use the size to determine the length of the pointer and then follow
|
||||
// it.
|
||||
if ($type === self::_POINTER) {
|
||||
[$pointer, $offset] = $this->decodePointer($ctrlByte, $offset);
|
||||
|
||||
// for unit testing
|
||||
if ($this->pointerTestHack) {
|
||||
return [$pointer];
|
||||
}
|
||||
|
||||
[$result] = $this->decode($pointer);
|
||||
|
||||
return [$result, $offset];
|
||||
}
|
||||
|
||||
if ($type === self::_EXTENDED) {
|
||||
$nextByte = \ord(Util::read($this->fileStream, $offset, 1));
|
||||
|
||||
$type = $nextByte + 7;
|
||||
|
||||
if ($type < 8) {
|
||||
throw new InvalidDatabaseException(
|
||||
'Something went horribly wrong in the decoder. An extended type '
|
||||
. 'resolved to a type number < 8 ('
|
||||
. $type
|
||||
. ')'
|
||||
);
|
||||
}
|
||||
|
||||
++$offset;
|
||||
}
|
||||
|
||||
[$size, $offset] = $this->sizeFromCtrlByte($ctrlByte, $offset);
|
||||
|
||||
return $this->decodeByType($type, $offset, $size);
|
||||
}
|
||||
|
||||
private function decodeByType(int $type, int $offset, int $size): array
|
||||
{
|
||||
switch ($type) {
|
||||
case self::_MAP:
|
||||
return $this->decodeMap($size, $offset);
|
||||
|
||||
case self::_ARRAY:
|
||||
return $this->decodeArray($size, $offset);
|
||||
|
||||
case self::_BOOLEAN:
|
||||
return [$this->decodeBoolean($size), $offset];
|
||||
}
|
||||
|
||||
$newOffset = $offset + $size;
|
||||
$bytes = Util::read($this->fileStream, $offset, $size);
|
||||
|
||||
switch ($type) {
|
||||
case self::_BYTES:
|
||||
case self::_UTF8_STRING:
|
||||
return [$bytes, $newOffset];
|
||||
|
||||
case self::_DOUBLE:
|
||||
$this->verifySize(8, $size);
|
||||
|
||||
return [$this->decodeDouble($bytes), $newOffset];
|
||||
|
||||
case self::_FLOAT:
|
||||
$this->verifySize(4, $size);
|
||||
|
||||
return [$this->decodeFloat($bytes), $newOffset];
|
||||
|
||||
case self::_INT32:
|
||||
return [$this->decodeInt32($bytes, $size), $newOffset];
|
||||
|
||||
case self::_UINT16:
|
||||
case self::_UINT32:
|
||||
case self::_UINT64:
|
||||
case self::_UINT128:
|
||||
return [$this->decodeUint($bytes, $size), $newOffset];
|
||||
|
||||
default:
|
||||
throw new InvalidDatabaseException(
|
||||
'Unknown or unexpected type: ' . $type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function verifySize(int $expected, int $actual): void
|
||||
{
|
||||
if ($expected !== $actual) {
|
||||
throw new InvalidDatabaseException(
|
||||
"The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function decodeArray(int $size, int $offset): array
|
||||
{
|
||||
$array = [];
|
||||
|
||||
for ($i = 0; $i < $size; ++$i) {
|
||||
[$value, $offset] = $this->decode($offset);
|
||||
$array[] = $value;
|
||||
}
|
||||
|
||||
return [$array, $offset];
|
||||
}
|
||||
|
||||
private function decodeBoolean(int $size): bool
|
||||
{
|
||||
return $size !== 0;
|
||||
}
|
||||
|
||||
private function decodeDouble(string $bytes): float
|
||||
{
|
||||
// This assumes IEEE 754 doubles, but most (all?) modern platforms
|
||||
// use them.
|
||||
[, $double] = unpack('E', $bytes);
|
||||
|
||||
return $double;
|
||||
}
|
||||
|
||||
private function decodeFloat(string $bytes): float
|
||||
{
|
||||
// This assumes IEEE 754 floats, but most (all?) modern platforms
|
||||
// use them.
|
||||
[, $float] = unpack('G', $bytes);
|
||||
|
||||
return $float;
|
||||
}
|
||||
|
||||
private function decodeInt32(string $bytes, int $size): int
|
||||
{
|
||||
switch ($size) {
|
||||
case 0:
|
||||
return 0;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
$bytes = str_pad($bytes, 4, "\x00", \STR_PAD_LEFT);
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidDatabaseException(
|
||||
"The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"
|
||||
);
|
||||
}
|
||||
|
||||
[, $int] = unpack('l', $this->maybeSwitchByteOrder($bytes));
|
||||
|
||||
return $int;
|
||||
}
|
||||
|
||||
private function decodeMap(int $size, int $offset): array
|
||||
{
|
||||
$map = [];
|
||||
|
||||
for ($i = 0; $i < $size; ++$i) {
|
||||
[$key, $offset] = $this->decode($offset);
|
||||
[$value, $offset] = $this->decode($offset);
|
||||
$map[$key] = $value;
|
||||
}
|
||||
|
||||
return [$map, $offset];
|
||||
}
|
||||
|
||||
private function decodePointer(int $ctrlByte, int $offset): array
|
||||
{
|
||||
$pointerSize = (($ctrlByte >> 3) & 0x3) + 1;
|
||||
|
||||
$buffer = Util::read($this->fileStream, $offset, $pointerSize);
|
||||
$offset = $offset + $pointerSize;
|
||||
|
||||
switch ($pointerSize) {
|
||||
case 1:
|
||||
$packed = \chr($ctrlByte & 0x7) . $buffer;
|
||||
[, $pointer] = unpack('n', $packed);
|
||||
$pointer += $this->pointerBase;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer;
|
||||
[, $pointer] = unpack('N', $packed);
|
||||
$pointer += $this->pointerBase + 2048;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$packed = \chr($ctrlByte & 0x7) . $buffer;
|
||||
|
||||
// It is safe to use 'N' here, even on 32 bit machines as the
|
||||
// first bit is 0.
|
||||
[, $pointer] = unpack('N', $packed);
|
||||
$pointer += $this->pointerBase + 526336;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// We cannot use unpack here as we might overflow on 32 bit
|
||||
// machines
|
||||
$pointerOffset = $this->decodeUint($buffer, $pointerSize);
|
||||
|
||||
$pointerBase = $this->pointerBase;
|
||||
|
||||
if (\PHP_INT_MAX - $pointerBase >= $pointerOffset) {
|
||||
$pointer = $pointerOffset + $pointerBase;
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
'The database offset is too large to be represented on your platform.'
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidDatabaseException(
|
||||
'Unexpected pointer size ' . $pointerSize
|
||||
);
|
||||
}
|
||||
|
||||
return [$pointer, $offset];
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
private function decodeUint(string $bytes, int $byteLength)
|
||||
{
|
||||
if ($byteLength === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$integer = 0;
|
||||
|
||||
// PHP integers are signed. PHP_INT_SIZE - 1 is the number of
|
||||
// complete bytes that can be converted to an integer. However,
|
||||
// we can convert another byte if the leading bit is zero.
|
||||
$useRealInts = $byteLength <= \PHP_INT_SIZE - 1
|
||||
|| ($byteLength === \PHP_INT_SIZE && (\ord($bytes[0]) & 0x80) === 0);
|
||||
|
||||
for ($i = 0; $i < $byteLength; ++$i) {
|
||||
$part = \ord($bytes[$i]);
|
||||
|
||||
// We only use gmp or bcmath if the final value is too big
|
||||
if ($useRealInts) {
|
||||
$integer = ($integer << 8) + $part;
|
||||
} elseif (\extension_loaded('gmp')) {
|
||||
$integer = gmp_strval(gmp_add(gmp_mul((string) $integer, '256'), $part));
|
||||
} elseif (\extension_loaded('bcmath')) {
|
||||
$integer = bcadd(bcmul((string) $integer, '256'), (string) $part);
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
'The gmp or bcmath extension must be installed to read this database.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $integer;
|
||||
}
|
||||
|
||||
private function sizeFromCtrlByte(int $ctrlByte, int $offset): array
|
||||
{
|
||||
$size = $ctrlByte & 0x1F;
|
||||
|
||||
if ($size < 29) {
|
||||
return [$size, $offset];
|
||||
}
|
||||
|
||||
$bytesToRead = $size - 28;
|
||||
$bytes = Util::read($this->fileStream, $offset, $bytesToRead);
|
||||
|
||||
if ($size === 29) {
|
||||
$size = 29 + \ord($bytes);
|
||||
} elseif ($size === 30) {
|
||||
[, $adjust] = unpack('n', $bytes);
|
||||
$size = 285 + $adjust;
|
||||
} else {
|
||||
[, $adjust] = unpack('N', "\x00" . $bytes);
|
||||
$size = $adjust + 65821;
|
||||
}
|
||||
|
||||
return [$size, $offset + $bytesToRead];
|
||||
}
|
||||
|
||||
private function maybeSwitchByteOrder(string $bytes): string
|
||||
{
|
||||
return $this->switchByteOrder ? strrev($bytes) : $bytes;
|
||||
}
|
||||
|
||||
private function isPlatformLittleEndian(): bool
|
||||
{
|
||||
$testint = 0x00FF;
|
||||
$packed = pack('S', $testint);
|
||||
|
||||
return $testint === current(unpack('v', $packed));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql;
|
||||
|
||||
use Aura\Sql\Profiler\Profiler;
|
||||
use Aura\Sql\Profiler\ProfilerInterface;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
*
|
||||
* Decorates an existing PDO instance with the extended methods.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
class DecoratedPdo extends AbstractExtendedPdo
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Constructor.
|
||||
*
|
||||
* This overrides the parent so that it can take an existing PDO instance
|
||||
* and decorate it with the extended methods.
|
||||
*
|
||||
* @param PDO $pdo An existing PDO instance to decorate.
|
||||
*
|
||||
* @param ProfilerInterface $profiler Tracks and logs query profiles.
|
||||
*
|
||||
*/
|
||||
public function __construct(PDO $pdo, ProfilerInterface $profiler = null)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
|
||||
if ($profiler === null) {
|
||||
$profiler = new Profiler();
|
||||
}
|
||||
$this->setProfiler($profiler);
|
||||
|
||||
$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
$this->setParser($this->newParser($driver));
|
||||
$this->setQuoteName($driver);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Connects to the database.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
// already connected
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Disconnects from the database; disallowed with decorated PDO connections.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$message = "Cannot disconnect a DecoratedPdo instance.";
|
||||
throw new Exception\CannotDisconnect($message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
/**
|
||||
* Backwards compatibility layer for Requests.
|
||||
*
|
||||
* Allows for Composer to autoload the old PSR-0 classes via the custom autoloader.
|
||||
* This prevents issues with _extending final classes_ (which was the previous solution).
|
||||
*
|
||||
* Please see the Changelog for the 2.0.0 release for upgrade notes.
|
||||
*
|
||||
* @package Requests
|
||||
*
|
||||
* @deprecated 2.0.0 Use the PSR-4 class names instead.
|
||||
*/
|
||||
|
||||
if (class_exists('WpOrg\Requests\Autoload') === false) {
|
||||
require_once dirname(__DIR__) . '/src/Autoload.php';
|
||||
}
|
||||
|
||||
WpOrg\Requests\Autoload::register();
|
|
@ -0,0 +1,375 @@
|
|||
<?php
|
||||
|
||||
namespace Symfony\Polyfill\Intl\Idn\Resources\unidata;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DisallowedRanges
|
||||
{
|
||||
/**
|
||||
* @param int $codePoint
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function inRange($codePoint)
|
||||
{
|
||||
if ($codePoint >= 128 && $codePoint <= 159) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 2155 && $codePoint <= 2207) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 3676 && $codePoint <= 3712) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 3808 && $codePoint <= 3839) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 4059 && $codePoint <= 4095) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 4256 && $codePoint <= 4293) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 6849 && $codePoint <= 6911) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 11859 && $codePoint <= 11903) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 42955 && $codePoint <= 42996) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 55296 && $codePoint <= 57343) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 57344 && $codePoint <= 63743) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 64218 && $codePoint <= 64255) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 64976 && $codePoint <= 65007) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 65630 && $codePoint <= 65663) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 65953 && $codePoint <= 65999) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 66046 && $codePoint <= 66175) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 66518 && $codePoint <= 66559) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 66928 && $codePoint <= 67071) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 67432 && $codePoint <= 67583) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 67760 && $codePoint <= 67807) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 67904 && $codePoint <= 67967) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 68256 && $codePoint <= 68287) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 68528 && $codePoint <= 68607) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 68681 && $codePoint <= 68735) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 68922 && $codePoint <= 69215) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 69298 && $codePoint <= 69375) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 69466 && $codePoint <= 69551) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 70207 && $codePoint <= 70271) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 70517 && $codePoint <= 70655) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 70874 && $codePoint <= 71039) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 71134 && $codePoint <= 71167) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 71370 && $codePoint <= 71423) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 71488 && $codePoint <= 71679) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 71740 && $codePoint <= 71839) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 72026 && $codePoint <= 72095) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 72441 && $codePoint <= 72703) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 72887 && $codePoint <= 72959) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 73130 && $codePoint <= 73439) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 73465 && $codePoint <= 73647) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 74650 && $codePoint <= 74751) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 75076 && $codePoint <= 77823) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 78905 && $codePoint <= 82943) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 83527 && $codePoint <= 92159) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 92784 && $codePoint <= 92879) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 93072 && $codePoint <= 93759) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 93851 && $codePoint <= 93951) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 94112 && $codePoint <= 94175) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 101590 && $codePoint <= 101631) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 101641 && $codePoint <= 110591) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 110879 && $codePoint <= 110927) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 111356 && $codePoint <= 113663) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 113828 && $codePoint <= 118783) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 119366 && $codePoint <= 119519) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 119673 && $codePoint <= 119807) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 121520 && $codePoint <= 122879) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 122923 && $codePoint <= 123135) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 123216 && $codePoint <= 123583) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 123648 && $codePoint <= 124927) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 125143 && $codePoint <= 125183) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 125280 && $codePoint <= 126064) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 126133 && $codePoint <= 126208) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 126270 && $codePoint <= 126463) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 126652 && $codePoint <= 126703) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 126706 && $codePoint <= 126975) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 127406 && $codePoint <= 127461) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 127590 && $codePoint <= 127743) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 129202 && $codePoint <= 129279) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 129751 && $codePoint <= 129791) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 129995 && $codePoint <= 130031) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 130042 && $codePoint <= 131069) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 173790 && $codePoint <= 173823) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 191457 && $codePoint <= 194559) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 195102 && $codePoint <= 196605) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 201547 && $codePoint <= 262141) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 262144 && $codePoint <= 327677) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 327680 && $codePoint <= 393213) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 393216 && $codePoint <= 458749) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 458752 && $codePoint <= 524285) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 524288 && $codePoint <= 589821) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 589824 && $codePoint <= 655357) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 655360 && $codePoint <= 720893) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 720896 && $codePoint <= 786429) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 786432 && $codePoint <= 851965) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 851968 && $codePoint <= 917501) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 917536 && $codePoint <= 917631) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 917632 && $codePoint <= 917759) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 918000 && $codePoint <= 983037) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 983040 && $codePoint <= 1048573) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($codePoint >= 1048576 && $codePoint <= 1114109) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Model;
|
||||
|
||||
use GeoIp2\Util;
|
||||
|
||||
/**
|
||||
* This class provides the GeoIP2 Domain model.
|
||||
*
|
||||
* @property-read string|null $domain The second level domain associated with the
|
||||
* IP address. This will be something like "example.com" or
|
||||
* "example.co.uk", not "foo.example.com".
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class Domain extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $domain;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
|
||||
$this->domain = $this->get('domain');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Psr\Log\Test;
|
||||
|
||||
/**
|
||||
* This class is internal and does not follow the BC promise.
|
||||
*
|
||||
* Do NOT use this class in any way.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DummyTest
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return 'DummyTest';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Model;
|
||||
|
||||
/**
|
||||
* Model class for the data returned by GeoIP2 Enterprise database lookups.
|
||||
*
|
||||
* The only difference between the City and Enterprise model classes is which
|
||||
* fields in each record may be populated. See
|
||||
* https://dev.maxmind.com/geoip/docs/web-services?lang=en for more details.
|
||||
*/
|
||||
class Enterprise extends City
|
||||
{
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of the POMO package.
|
||||
*
|
||||
* @copyright 2014 POMO
|
||||
* @license GPL
|
||||
*/
|
||||
|
||||
namespace POMO\Translations;
|
||||
|
||||
/**
|
||||
* Contains EntryTranslations class
|
||||
* EntryTranslations class encapsulates a translatable string.
|
||||
*/
|
||||
class EntryTranslations
|
||||
{
|
||||
/**
|
||||
* Whether the entry contains a string and its plural form, default is false.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $is_plural = false;
|
||||
|
||||
public $context = null;
|
||||
public $singular = null;
|
||||
public $plural = null;
|
||||
public $translations = array();
|
||||
public $translator_comments = '';
|
||||
public $extracted_comments = '';
|
||||
public $references = array();
|
||||
public $flags = array();
|
||||
|
||||
/**
|
||||
* @param array $args associative array, support following keys:
|
||||
* - singular (string) -- the string to translate, if omitted and empty entry will be created
|
||||
* - plural (string) -- the plural form of the string, setting this will set {@link $is_plural} to true
|
||||
* - translations (array) -- translations of the string and possibly -- its plural forms
|
||||
* - context (string) -- a string differentiating two equal strings used in different contexts
|
||||
* - translator_comments (string) -- comments left by translators
|
||||
* - extracted_comments (string) -- comments left by developers
|
||||
* - references (array) -- places in the code this strings is used, in relative_to_root_path/file.php:linenum form
|
||||
* - flags (array) -- flags like php-format
|
||||
*/
|
||||
public function __construct($args = array())
|
||||
{
|
||||
// if no singular -- empty object
|
||||
if (!isset($args['singular'])) {
|
||||
return;
|
||||
}
|
||||
// get member variable values from args hash
|
||||
foreach ($args as $varname => $value) {
|
||||
$this->$varname = $value;
|
||||
}
|
||||
if (isset($args['plural']) && $args['plural']) {
|
||||
$this->is_plural = true;
|
||||
}
|
||||
if (!is_array($this->translations)) {
|
||||
$this->translations = array();
|
||||
}
|
||||
if (!is_array($this->references)) {
|
||||
$this->references = array();
|
||||
}
|
||||
if (!is_array($this->flags)) {
|
||||
$this->flags = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique key for this entry.
|
||||
*
|
||||
* @return string|bool the key or false if the entry is empty
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
if (null === $this->singular || '' === $this->singular) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepend context and EOT, like in MO files
|
||||
$key = !$this->context ? $this->singular : $this->context.chr(4).$this->singular;
|
||||
// Standardize on \n line endings
|
||||
$key = str_replace(array("\r\n", "\r"), "\n", $key);
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $other
|
||||
*/
|
||||
public function merge_with(&$other)
|
||||
{
|
||||
$this->flags = array_unique(array_merge($this->flags, $other->flags));
|
||||
$this->references = array_unique(array_merge($this->references, $other->references));
|
||||
if ($this->extracted_comments != $other->extracted_comments) {
|
||||
$this->extracted_comments .= $other->extracted_comments;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
/**
|
||||
* Exception for HTTP requests
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception for HTTP requests
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Exception extends Exception {
|
||||
/**
|
||||
* Type of exception
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Data associated with the exception
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Create a new exception
|
||||
*
|
||||
* @param string $message Exception message
|
||||
* @param string $type Exception type
|
||||
* @param mixed $data Associated data
|
||||
* @param integer $code Exception numerical code, if applicable
|
||||
*/
|
||||
public function __construct($message, $type, $data = null, $code = 0) {
|
||||
parent::__construct($message, $code);
|
||||
|
||||
$this->type = $type;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@see getCode()}, but a string code.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return string
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives any relevant data
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql;
|
||||
|
||||
use Aura\Sql\Profiler\Profiler;
|
||||
use Aura\Sql\Profiler\ProfilerInterface;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
*
|
||||
* A lazy-connecting PDO with extended methods.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
class ExtendedPdo extends AbstractExtendedPdo
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Constructor arguments for instantiating the PDO connection.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
protected $args = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* Constructor.
|
||||
*
|
||||
* This overrides the parent so that it can take connection attributes as a
|
||||
* constructor parameter, and set them after connection.
|
||||
*
|
||||
* @param string $dsn The data source name for the connection.
|
||||
*
|
||||
* @param string $username The username for the connection.
|
||||
*
|
||||
* @param string $password The password for the connection.
|
||||
*
|
||||
* @param array $options Driver-specific options for the connection.
|
||||
*
|
||||
* @param array $queries Queries to execute after the connection.
|
||||
*
|
||||
* @param ProfilerInterface $profiler Tracks and logs query profiles.
|
||||
*
|
||||
* @see http://php.net/manual/en/pdo.construct.php
|
||||
*
|
||||
*/
|
||||
public function __construct(
|
||||
$dsn,
|
||||
$username = null,
|
||||
$password = null,
|
||||
array $options = [],
|
||||
array $queries = [],
|
||||
ProfilerInterface $profiler = null
|
||||
) {
|
||||
// if no error mode is specified, use exceptions
|
||||
if (! isset($options[PDO::ATTR_ERRMODE])) {
|
||||
$options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
|
||||
}
|
||||
|
||||
// retain the arguments for later
|
||||
$this->args = [
|
||||
$dsn,
|
||||
$username,
|
||||
$password,
|
||||
$options,
|
||||
$queries
|
||||
];
|
||||
|
||||
// retain a profiler, instantiating a default one if needed
|
||||
if ($profiler === null) {
|
||||
$profiler = new Profiler();
|
||||
}
|
||||
$this->setProfiler($profiler);
|
||||
|
||||
// retain a query parser
|
||||
$parts = explode(':', $dsn);
|
||||
$parser = $this->newParser($parts[0]);
|
||||
$this->setParser($parser);
|
||||
|
||||
// set quotes for identifier names
|
||||
$this->setQuoteName($parts[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Connects to the database.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if ($this->pdo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// connect
|
||||
$this->profiler->start(__FUNCTION__);
|
||||
list($dsn, $username, $password, $options, $queries) = $this->args;
|
||||
$this->pdo = new PDO($dsn, $username, $password, $options);
|
||||
$this->profiler->finish();
|
||||
|
||||
// connection-time queries
|
||||
foreach ($queries as $query) {
|
||||
$this->exec($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Disconnects from the database.
|
||||
*
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$this->profiler->start(__FUNCTION__);
|
||||
$this->pdo = null;
|
||||
$this->profiler->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* The purpose of this method is to hide sensitive data from stack traces.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function __debugInfo()
|
||||
{
|
||||
return [
|
||||
'args' => [
|
||||
$this->args[0],
|
||||
'****',
|
||||
'****',
|
||||
$this->args[3],
|
||||
$this->args[4],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Return the inner PDO (if any)
|
||||
*
|
||||
* @return \PDO
|
||||
*
|
||||
*/
|
||||
public function getPdo()
|
||||
{
|
||||
$this->connect();
|
||||
return $this->pdo;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* This file is part of Aura for PHP.
|
||||
*
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
*/
|
||||
namespace Aura\Sql;
|
||||
|
||||
use Aura\Sql\Parser\ParserInterface;
|
||||
use Aura\Sql\Profiler\ProfilerInterface;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
*
|
||||
* An interface to the Aura.Sql extended PDO object.
|
||||
*
|
||||
* @package Aura.Sql
|
||||
*
|
||||
*/
|
||||
interface ExtendedPdoInterface extends PdoInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Connects to the database.
|
||||
*
|
||||
*/
|
||||
public function connect();
|
||||
|
||||
/**
|
||||
*
|
||||
* Disconnects from the database.
|
||||
*
|
||||
*/
|
||||
public function disconnect();
|
||||
|
||||
/**
|
||||
*
|
||||
* Performs a statement and returns the number of affected rows.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
*/
|
||||
public function fetchAffected($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches a sequential array of rows from the database; the rows
|
||||
* are represented as associative arrays.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function fetchAll($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches an associative array of rows from the database; the rows
|
||||
* are represented as associative arrays. The array of rows is keyed
|
||||
* on the first column of each row.
|
||||
*
|
||||
* N.b.: if multiple rows have the same first column value, the last
|
||||
* row with that value will override earlier rows.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function fetchAssoc($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches the first column of rows as a sequential array.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function fetchCol($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches multiple from the database as an associative array.
|
||||
* The first column will be the index
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @param int $style a fetch style defaults to PDO::FETCH_COLUMN for single
|
||||
* values, use PDO::FETCH_NAMED when fetching a multiple columns
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function fetchGroup(
|
||||
$statement,
|
||||
array $values = [],
|
||||
$style = PDO::FETCH_COLUMN
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches one row from the database as an object, mapping column values
|
||||
* to object properties.
|
||||
*
|
||||
* Warning: PDO "injects property-values BEFORE invoking the constructor -
|
||||
* in other words, if your class initializes property-values to defaults
|
||||
* in the constructor, you will be overwriting the values injected by
|
||||
* fetchObject() !"
|
||||
* <http://www.php.net/manual/en/pdostatement.fetchobject.php#111744>
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @param string $class The name of the class to create.
|
||||
*
|
||||
* @param array $args Arguments to pass to the object constructor.
|
||||
*
|
||||
* @return object
|
||||
*
|
||||
*/
|
||||
public function fetchObject(
|
||||
$statement,
|
||||
array $values = [],
|
||||
$class = 'stdClass',
|
||||
array $args = []
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches a sequential array of rows from the database; the rows
|
||||
* are represented as objects, where the column values are mapped to
|
||||
* object properties.
|
||||
*
|
||||
* Warning: PDO "injects property-values BEFORE invoking the constructor -
|
||||
* in other words, if your class initializes property-values to defaults
|
||||
* in the constructor, you will be overwriting the values injected by
|
||||
* fetchObject() !"
|
||||
* <http://www.php.net/manual/en/pdostatement.fetchobject.php#111744>
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @param string $class The name of the class to create from each
|
||||
* row.
|
||||
*
|
||||
* @param array $args Arguments to pass to each object constructor.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function fetchObjects(
|
||||
$statement,
|
||||
array $values = [],
|
||||
$class = 'stdClass',
|
||||
array $args = []
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches one row from the database as an associative array.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function fetchOne($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches an associative array of rows as key-value pairs (first
|
||||
* column is the key, second column is the value).
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function fetchPairs($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Fetches the very first value (i.e., first column of the first row).
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
*/
|
||||
public function fetchValue($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the Parser instance.
|
||||
*
|
||||
* @return ParserInterface
|
||||
*
|
||||
*/
|
||||
public function getParser();
|
||||
|
||||
/**
|
||||
*
|
||||
* Return the inner PDO (if any)
|
||||
*
|
||||
* @return \PDO
|
||||
*
|
||||
*/
|
||||
public function getPdo();
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the Profiler instance.
|
||||
*
|
||||
* @return ProfilerInterface
|
||||
*
|
||||
*/
|
||||
public function getProfiler();
|
||||
|
||||
/**
|
||||
*
|
||||
* Quotes a multi-part (dotted) identifier name.
|
||||
*
|
||||
* @param string $name The multi-part identifier name.
|
||||
*
|
||||
* @return string The multi-part identifier name, quoted.
|
||||
*
|
||||
*/
|
||||
public function quoteName($name);
|
||||
|
||||
/**
|
||||
*
|
||||
* Quotes a single identifier name.
|
||||
*
|
||||
* @param string $name The identifier name.
|
||||
*
|
||||
* @return string The quoted identifier name.
|
||||
*
|
||||
*/
|
||||
public function quoteSingleName($name);
|
||||
|
||||
/**
|
||||
*
|
||||
* Is the PDO connection active?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
*/
|
||||
public function isConnected();
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets the Parser instance.
|
||||
*
|
||||
* @param ParserInterface $parser The Parser instance.
|
||||
*
|
||||
*/
|
||||
public function setParser(ParserInterface $parser);
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets the Profiler instance.
|
||||
*
|
||||
* @param ProfilerInterface $profiler The Profiler instance.
|
||||
*
|
||||
*/
|
||||
public function setProfiler(ProfilerInterface $profiler);
|
||||
|
||||
/**
|
||||
*
|
||||
* Yields rows from the database
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return \Generator
|
||||
*
|
||||
*/
|
||||
public function yieldAll($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Yields rows from the database keyed on the first column of each row.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return \Generator
|
||||
*
|
||||
*/
|
||||
public function yieldAssoc($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Yields the first column of all rows
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return \Generator
|
||||
*
|
||||
*/
|
||||
public function yieldCol($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Yields objects where the column values are mapped to object properties.
|
||||
*
|
||||
* Warning: PDO "injects property-values BEFORE invoking the constructor -
|
||||
* in other words, if your class initializes property-values to defaults
|
||||
* in the constructor, you will be overwriting the values injected by
|
||||
* fetchObject() !"
|
||||
* <http://www.php.net/manual/en/pdostatement.fetchobject.php#111744>
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @param string $class The name of the class to create from each
|
||||
* row.
|
||||
*
|
||||
* @param array $args Arguments to pass to each object constructor.
|
||||
*
|
||||
* @return \Generator
|
||||
*
|
||||
*/
|
||||
public function yieldObjects(
|
||||
$statement,
|
||||
array $values = [],
|
||||
$class = 'stdClass',
|
||||
array $args = []
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Yields key-value pairs (first column is the key, second column is the
|
||||
* value).
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return \Generator
|
||||
*
|
||||
*/
|
||||
public function yieldPairs($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Performs a query after preparing the statement with bound values, then
|
||||
* returns the result as a PDOStatement.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare and execute.
|
||||
*
|
||||
* @param array $values Values to bind to the query.
|
||||
*
|
||||
* @return \PDOStatement
|
||||
*
|
||||
*/
|
||||
public function perform($statement, array $values = []);
|
||||
|
||||
/**
|
||||
*
|
||||
* Prepares an SQL statement with bound values.
|
||||
*
|
||||
* This method only binds values that have placeholders in the
|
||||
* statement, thereby avoiding errors from PDO regarding too many bound
|
||||
* values. It also binds all sequential (question-mark) placeholders.
|
||||
*
|
||||
* If a placeholder value is an array, the array is converted to a string
|
||||
* of comma-separated quoted values; e.g., for an `IN (...)` condition.
|
||||
* The quoted string is replaced directly into the statement instead of
|
||||
* using `PDOStatement::bindValue()` proper.
|
||||
*
|
||||
* @param string $statement The SQL statement to prepare for execution.
|
||||
*
|
||||
* @param array $values The values to bind to the statement, if any.
|
||||
*
|
||||
* @return \PDOStatement
|
||||
*
|
||||
* @see http://php.net/manual/en/pdo.prepare.php
|
||||
*
|
||||
*/
|
||||
public function prepareWithValues($statement, array $values = []);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of the POMO package.
|
||||
*/
|
||||
|
||||
namespace POMO\Streams;
|
||||
|
||||
/**
|
||||
* Classes, which help reading streams of data from files.
|
||||
*
|
||||
* @property bool|resource $_f
|
||||
*
|
||||
* @author Danilo Segan <danilo@kvota.net>
|
||||
*/
|
||||
class FileReader extends Reader implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* @param string $filename
|
||||
*/
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->_f = fopen($filename, 'rb');
|
||||
}
|
||||
|
||||
public function read($bytes)
|
||||
{
|
||||
return fread($this->_f, $bytes);
|
||||
}
|
||||
|
||||
public function seekto($pos)
|
||||
{
|
||||
if (-1 == fseek($this->_f, $pos, SEEK_SET)) {
|
||||
return false;
|
||||
}
|
||||
$this->_pos = $pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function is_resource()
|
||||
{
|
||||
return is_resource($this->_f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function feof()
|
||||
{
|
||||
return feof($this->_f);
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
return fclose($this->_f);
|
||||
}
|
||||
|
||||
public function read_all()
|
||||
{
|
||||
$all = '';
|
||||
while (!$this->feof()) {
|
||||
$all .= $this->read(4096);
|
||||
}
|
||||
|
||||
return $all;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
/**
|
||||
* Iterator for arrays requiring filtered values
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Iterator for arrays requiring filtered values
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
class Requests_Utility_FilteredIterator extends ArrayIterator {
|
||||
/**
|
||||
* Callback to run as a filter
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* Create a new iterator
|
||||
*
|
||||
* @param array $data
|
||||
* @param callable $callback Callback to be called on each value
|
||||
*/
|
||||
public function __construct($data, $callback) {
|
||||
parent::__construct($data);
|
||||
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current item's value after filtering
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function current() {
|
||||
$value = parent::current();
|
||||
$value = call_user_func($this->callback, $value);
|
||||
return $value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,505 @@
|
|||
<?php
|
||||
/**
|
||||
* fsockopen HTTP transport
|
||||
*
|
||||
* @package Requests\Transport
|
||||
*/
|
||||
|
||||
namespace WpOrg\Requests\Transport;
|
||||
|
||||
use WpOrg\Requests\Capability;
|
||||
use WpOrg\Requests\Exception;
|
||||
use WpOrg\Requests\Exception\InvalidArgument;
|
||||
use WpOrg\Requests\Port;
|
||||
use WpOrg\Requests\Requests;
|
||||
use WpOrg\Requests\Ssl;
|
||||
use WpOrg\Requests\Transport;
|
||||
use WpOrg\Requests\Utility\CaseInsensitiveDictionary;
|
||||
use WpOrg\Requests\Utility\InputValidator;
|
||||
|
||||
/**
|
||||
* fsockopen HTTP transport
|
||||
*
|
||||
* @package Requests\Transport
|
||||
*/
|
||||
final class Fsockopen implements Transport {
|
||||
/**
|
||||
* Second to microsecond conversion
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const SECOND_IN_MICROSECONDS = 1000000;
|
||||
|
||||
/**
|
||||
* Raw HTTP data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $headers = '';
|
||||
|
||||
/**
|
||||
* Stream metadata
|
||||
*
|
||||
* @var array Associative array of properties, see {@link https://www.php.net/stream_get_meta_data}
|
||||
*/
|
||||
public $info;
|
||||
|
||||
/**
|
||||
* What's the maximum number of bytes we should keep?
|
||||
*
|
||||
* @var int|bool Byte count, or false if no limit.
|
||||
*/
|
||||
private $max_bytes = false;
|
||||
|
||||
private $connect_error = '';
|
||||
|
||||
/**
|
||||
* Perform a request
|
||||
*
|
||||
* @param string|Stringable $url URL to request
|
||||
* @param array $headers Associative array of request headers
|
||||
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
|
||||
* @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||
* @return string Raw HTTP result
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data parameter is not an array or string.
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||
* @throws \WpOrg\Requests\Exception On failure to connect to socket (`fsockopenerror`)
|
||||
* @throws \WpOrg\Requests\Exception On socket timeout (`timeout`)
|
||||
*/
|
||||
public function request($url, $headers = [], $data = [], $options = []) {
|
||||
if (InputValidator::is_string_or_stringable($url) === false) {
|
||||
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
|
||||
}
|
||||
|
||||
if (is_array($headers) === false) {
|
||||
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
|
||||
}
|
||||
|
||||
if (!is_array($data) && !is_string($data)) {
|
||||
if ($data === null) {
|
||||
$data = '';
|
||||
} else {
|
||||
throw InvalidArgument::create(3, '$data', 'array|string', gettype($data));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($options) === false) {
|
||||
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
|
||||
}
|
||||
|
||||
$options['hooks']->dispatch('fsockopen.before_request');
|
||||
|
||||
$url_parts = parse_url($url);
|
||||
if (empty($url_parts)) {
|
||||
throw new Exception('Invalid URL.', 'invalidurl', $url);
|
||||
}
|
||||
|
||||
$host = $url_parts['host'];
|
||||
$context = stream_context_create();
|
||||
$verifyname = false;
|
||||
$case_insensitive_headers = new CaseInsensitiveDictionary($headers);
|
||||
|
||||
// HTTPS support
|
||||
if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
|
||||
$remote_socket = 'ssl://' . $host;
|
||||
if (!isset($url_parts['port'])) {
|
||||
$url_parts['port'] = Port::HTTPS;
|
||||
}
|
||||
|
||||
$context_options = [
|
||||
'verify_peer' => true,
|
||||
'capture_peer_cert' => true,
|
||||
];
|
||||
$verifyname = true;
|
||||
|
||||
// SNI, if enabled (OpenSSL >=0.9.8j)
|
||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.openssl_tlsext_server_nameFound
|
||||
if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) {
|
||||
$context_options['SNI_enabled'] = true;
|
||||
if (isset($options['verifyname']) && $options['verifyname'] === false) {
|
||||
$context_options['SNI_enabled'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['verify'])) {
|
||||
if ($options['verify'] === false) {
|
||||
$context_options['verify_peer'] = false;
|
||||
$context_options['verify_peer_name'] = false;
|
||||
$verifyname = false;
|
||||
} elseif (is_string($options['verify'])) {
|
||||
$context_options['cafile'] = $options['verify'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['verifyname']) && $options['verifyname'] === false) {
|
||||
$context_options['verify_peer_name'] = false;
|
||||
$verifyname = false;
|
||||
}
|
||||
|
||||
stream_context_set_option($context, ['ssl' => $context_options]);
|
||||
} else {
|
||||
$remote_socket = 'tcp://' . $host;
|
||||
}
|
||||
|
||||
$this->max_bytes = $options['max_bytes'];
|
||||
|
||||
if (!isset($url_parts['port'])) {
|
||||
$url_parts['port'] = Port::HTTP;
|
||||
}
|
||||
|
||||
$remote_socket .= ':' . $url_parts['port'];
|
||||
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
|
||||
set_error_handler([$this, 'connect_error_handler'], E_WARNING | E_NOTICE);
|
||||
|
||||
$options['hooks']->dispatch('fsockopen.remote_socket', [&$remote_socket]);
|
||||
|
||||
$socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context);
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if ($verifyname && !$this->verify_certificate_from_context($host, $context)) {
|
||||
throw new Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
|
||||
}
|
||||
|
||||
if (!$socket) {
|
||||
if ($errno === 0) {
|
||||
// Connection issue
|
||||
throw new Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
|
||||
}
|
||||
|
||||
throw new Exception($errstr, 'fsockopenerror', null, $errno);
|
||||
}
|
||||
|
||||
$data_format = $options['data_format'];
|
||||
|
||||
if ($data_format === 'query') {
|
||||
$path = self::format_get($url_parts, $data);
|
||||
$data = '';
|
||||
} else {
|
||||
$path = self::format_get($url_parts, []);
|
||||
}
|
||||
|
||||
$options['hooks']->dispatch('fsockopen.remote_host_path', [&$path, $url]);
|
||||
|
||||
$request_body = '';
|
||||
$out = sprintf("%s %s HTTP/%.1F\r\n", $options['type'], $path, $options['protocol_version']);
|
||||
|
||||
if ($options['type'] !== Requests::TRACE) {
|
||||
if (is_array($data)) {
|
||||
$request_body = http_build_query($data, '', '&');
|
||||
} else {
|
||||
$request_body = $data;
|
||||
}
|
||||
|
||||
// Always include Content-length on POST requests to prevent
|
||||
// 411 errors from some servers when the body is empty.
|
||||
if (!empty($data) || $options['type'] === Requests::POST) {
|
||||
if (!isset($case_insensitive_headers['Content-Length'])) {
|
||||
$headers['Content-Length'] = strlen($request_body);
|
||||
}
|
||||
|
||||
if (!isset($case_insensitive_headers['Content-Type'])) {
|
||||
$headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($case_insensitive_headers['Host'])) {
|
||||
$out .= sprintf('Host: %s', $url_parts['host']);
|
||||
$scheme_lower = strtolower($url_parts['scheme']);
|
||||
|
||||
if (($scheme_lower === 'http' && $url_parts['port'] !== Port::HTTP) || ($scheme_lower === 'https' && $url_parts['port'] !== Port::HTTPS)) {
|
||||
$out .= ':' . $url_parts['port'];
|
||||
}
|
||||
|
||||
$out .= "\r\n";
|
||||
}
|
||||
|
||||
if (!isset($case_insensitive_headers['User-Agent'])) {
|
||||
$out .= sprintf("User-Agent: %s\r\n", $options['useragent']);
|
||||
}
|
||||
|
||||
$accept_encoding = $this->accept_encoding();
|
||||
if (!isset($case_insensitive_headers['Accept-Encoding']) && !empty($accept_encoding)) {
|
||||
$out .= sprintf("Accept-Encoding: %s\r\n", $accept_encoding);
|
||||
}
|
||||
|
||||
$headers = Requests::flatten($headers);
|
||||
|
||||
if (!empty($headers)) {
|
||||
$out .= implode("\r\n", $headers) . "\r\n";
|
||||
}
|
||||
|
||||
$options['hooks']->dispatch('fsockopen.after_headers', [&$out]);
|
||||
|
||||
if (substr($out, -2) !== "\r\n") {
|
||||
$out .= "\r\n";
|
||||
}
|
||||
|
||||
if (!isset($case_insensitive_headers['Connection'])) {
|
||||
$out .= "Connection: Close\r\n";
|
||||
}
|
||||
|
||||
$out .= "\r\n" . $request_body;
|
||||
|
||||
$options['hooks']->dispatch('fsockopen.before_send', [&$out]);
|
||||
|
||||
fwrite($socket, $out);
|
||||
$options['hooks']->dispatch('fsockopen.after_send', [$out]);
|
||||
|
||||
if (!$options['blocking']) {
|
||||
fclose($socket);
|
||||
$fake_headers = '';
|
||||
$options['hooks']->dispatch('fsockopen.after_request', [&$fake_headers]);
|
||||
return '';
|
||||
}
|
||||
|
||||
$timeout_sec = (int) floor($options['timeout']);
|
||||
if ($timeout_sec === $options['timeout']) {
|
||||
$timeout_msec = 0;
|
||||
} else {
|
||||
$timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS;
|
||||
}
|
||||
|
||||
stream_set_timeout($socket, $timeout_sec, $timeout_msec);
|
||||
|
||||
$response = '';
|
||||
$body = '';
|
||||
$headers = '';
|
||||
$this->info = stream_get_meta_data($socket);
|
||||
$size = 0;
|
||||
$doingbody = false;
|
||||
$download = false;
|
||||
if ($options['filename']) {
|
||||
// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception.
|
||||
$download = @fopen($options['filename'], 'wb');
|
||||
if ($download === false) {
|
||||
$error = error_get_last();
|
||||
throw new Exception($error['message'], 'fopen');
|
||||
}
|
||||
}
|
||||
|
||||
while (!feof($socket)) {
|
||||
$this->info = stream_get_meta_data($socket);
|
||||
if ($this->info['timed_out']) {
|
||||
throw new Exception('fsocket timed out', 'timeout');
|
||||
}
|
||||
|
||||
$block = fread($socket, Requests::BUFFER_SIZE);
|
||||
if (!$doingbody) {
|
||||
$response .= $block;
|
||||
if (strpos($response, "\r\n\r\n")) {
|
||||
list($headers, $block) = explode("\r\n\r\n", $response, 2);
|
||||
$doingbody = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Are we in body mode now?
|
||||
if ($doingbody) {
|
||||
$options['hooks']->dispatch('request.progress', [$block, $size, $this->max_bytes]);
|
||||
$data_length = strlen($block);
|
||||
if ($this->max_bytes) {
|
||||
// Have we already hit a limit?
|
||||
if ($size === $this->max_bytes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($size + $data_length) > $this->max_bytes) {
|
||||
// Limit the length
|
||||
$limited_length = ($this->max_bytes - $size);
|
||||
$block = substr($block, 0, $limited_length);
|
||||
}
|
||||
}
|
||||
|
||||
$size += strlen($block);
|
||||
if ($download) {
|
||||
fwrite($download, $block);
|
||||
} else {
|
||||
$body .= $block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->headers = $headers;
|
||||
|
||||
if ($download) {
|
||||
fclose($download);
|
||||
} else {
|
||||
$this->headers .= "\r\n\r\n" . $body;
|
||||
}
|
||||
|
||||
fclose($socket);
|
||||
|
||||
$options['hooks']->dispatch('fsockopen.after_request', [&$this->headers, &$this->info]);
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send multiple requests simultaneously
|
||||
*
|
||||
* @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see \WpOrg\Requests\Transport::request()}
|
||||
* @param array $options Global options, see {@see \WpOrg\Requests\Requests::response()} for documentation
|
||||
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
|
||||
*/
|
||||
public function request_multiple($requests, $options) {
|
||||
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
|
||||
if (empty($requests)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
|
||||
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
|
||||
}
|
||||
|
||||
if (is_array($options) === false) {
|
||||
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
|
||||
}
|
||||
|
||||
$responses = [];
|
||||
$class = get_class($this);
|
||||
foreach ($requests as $id => $request) {
|
||||
try {
|
||||
$handler = new $class();
|
||||
$responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']);
|
||||
|
||||
$request['options']['hooks']->dispatch('transport.internal.parse_response', [&$responses[$id], $request]);
|
||||
} catch (Exception $e) {
|
||||
$responses[$id] = $e;
|
||||
}
|
||||
|
||||
if (!is_string($responses[$id])) {
|
||||
$request['options']['hooks']->dispatch('multiple.request.complete', [&$responses[$id], $id]);
|
||||
}
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the encodings we can accept
|
||||
*
|
||||
* @return string Accept-Encoding header value
|
||||
*/
|
||||
private static function accept_encoding() {
|
||||
$type = [];
|
||||
if (function_exists('gzinflate')) {
|
||||
$type[] = 'deflate;q=1.0';
|
||||
}
|
||||
|
||||
if (function_exists('gzuncompress')) {
|
||||
$type[] = 'compress;q=0.5';
|
||||
}
|
||||
|
||||
$type[] = 'gzip;q=0.5';
|
||||
|
||||
return implode(', ', $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a URL given GET data
|
||||
*
|
||||
* @param array $url_parts
|
||||
* @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query}
|
||||
* @return string URL with data
|
||||
*/
|
||||
private static function format_get($url_parts, $data) {
|
||||
if (!empty($data)) {
|
||||
if (empty($url_parts['query'])) {
|
||||
$url_parts['query'] = '';
|
||||
}
|
||||
|
||||
$url_parts['query'] .= '&' . http_build_query($data, '', '&');
|
||||
$url_parts['query'] = trim($url_parts['query'], '&');
|
||||
}
|
||||
|
||||
if (isset($url_parts['path'])) {
|
||||
if (isset($url_parts['query'])) {
|
||||
$get = $url_parts['path'] . '?' . $url_parts['query'];
|
||||
} else {
|
||||
$get = $url_parts['path'];
|
||||
}
|
||||
} else {
|
||||
$get = '/';
|
||||
}
|
||||
|
||||
return $get;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler for stream_socket_client()
|
||||
*
|
||||
* @param int $errno Error number (e.g. E_WARNING)
|
||||
* @param string $errstr Error message
|
||||
*/
|
||||
public function connect_error_handler($errno, $errstr) {
|
||||
// Double-check we can handle it
|
||||
if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) {
|
||||
// Return false to indicate the default error handler should engage
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->connect_error .= $errstr . "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the certificate against common name and subject alternative names
|
||||
*
|
||||
* Unfortunately, PHP doesn't check the certificate against the alternative
|
||||
* names, leading things like 'https://www.github.com/' to be invalid.
|
||||
* Instead
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
|
||||
*
|
||||
* @param string $host Host name to verify against
|
||||
* @param resource $context Stream context
|
||||
* @return bool
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
|
||||
* @throws \WpOrg\Requests\Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
|
||||
*/
|
||||
public function verify_certificate_from_context($host, $context) {
|
||||
$meta = stream_context_get_options($context);
|
||||
|
||||
// If we don't have SSL options, then we couldn't make the connection at
|
||||
// all
|
||||
if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) {
|
||||
throw new Exception(rtrim($this->connect_error), 'ssl.connect_error');
|
||||
}
|
||||
|
||||
$cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
|
||||
|
||||
return Ssl::verify_certificate($host, $cert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Self-test whether the transport can be used.
|
||||
*
|
||||
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
|
||||
* @return bool Whether the transport can be used.
|
||||
*/
|
||||
public static function test($capabilities = []) {
|
||||
if (!function_exists('fsockopen')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If needed, check that streams support SSL
|
||||
if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) {
|
||||
if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents a generic error.
|
||||
*/
|
||||
class GeoIp2Exception extends \Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of the POMO package.
|
||||
*
|
||||
* @copyright 2014 POMO
|
||||
* @license GPL
|
||||
*/
|
||||
|
||||
namespace POMO\Translations;
|
||||
|
||||
use POMO\Parser\PluralForms;
|
||||
|
||||
/**
|
||||
* Class for a set of entries for translation and their associated headers.
|
||||
*
|
||||
* @property mixed $_nplurals
|
||||
* @property callable $_gettext_select_plural_form
|
||||
*/
|
||||
class GettextTranslations extends Translations implements TranslationsInterface
|
||||
{
|
||||
/**
|
||||
* The gettext implementation of select_plural_form.
|
||||
*
|
||||
* It lives in this class, because there are more than one descendand,
|
||||
* which will use it and they can't share it effectively.
|
||||
*
|
||||
* @param int $count Items count
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function gettext_select_plural_form($count)
|
||||
{
|
||||
if (!isset($this->_gettext_select_plural_form)
|
||||
|| is_null($this->_gettext_select_plural_form)) {
|
||||
list($nplurals, $expression) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
|
||||
$this->_nplurals = $nplurals;
|
||||
$this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
|
||||
}
|
||||
|
||||
return call_user_func($this->_gettext_select_plural_form, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $header
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function nplurals_and_expression_from_header($header)
|
||||
{
|
||||
if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches)) {
|
||||
$nplurals = (int) $matches[1];
|
||||
$expression = trim($matches[2]);
|
||||
|
||||
return array($nplurals, $expression);
|
||||
} else {
|
||||
return array(2, 'n != 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a function, which will return the right translation index,
|
||||
* according to the plural forms header.
|
||||
*
|
||||
* @param int $nplurals
|
||||
* @param string $expression
|
||||
*
|
||||
* @return callable The right translation index
|
||||
*/
|
||||
public function make_plural_form_function($nplurals, $expression)
|
||||
{
|
||||
try {
|
||||
$handler = new PluralForms(rtrim($expression, ';'));
|
||||
|
||||
return array($handler, 'get');
|
||||
} catch (\Exception $e) {
|
||||
// Fall back to default plural-form function.
|
||||
return $this->make_plural_form_function(2, 'n != 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds parentheses to the inner parts of ternary operators in
|
||||
* plural expressions, because PHP evaluates ternary operators
|
||||
* from left to right.
|
||||
*
|
||||
* @param string $expression the expression without parentheses
|
||||
*
|
||||
* @return string the expression with parentheses added
|
||||
*/
|
||||
public function parenthesize_plural_exression($expression)
|
||||
{
|
||||
$expression .= ';';
|
||||
$res = '';
|
||||
$depth = 0;
|
||||
for ($i = 0; $i < strlen($expression); ++$i) {
|
||||
$char = $expression[$i];
|
||||
switch ($char) {
|
||||
case '?':
|
||||
$res .= ' ? (';
|
||||
$depth++;
|
||||
break;
|
||||
case ':':
|
||||
$res .= ') : (';
|
||||
break;
|
||||
case ';':
|
||||
$res .= str_repeat(')', $depth).';';
|
||||
$depth = 0;
|
||||
break;
|
||||
default:
|
||||
$res .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
return rtrim($res, ';');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $translation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function make_headers($translation)
|
||||
{
|
||||
$headers = array();
|
||||
// sometimes \ns are used instead of real new lines
|
||||
$translation = str_replace('\n', "\n", $translation);
|
||||
$lines = explode("\n", $translation);
|
||||
foreach ($lines as $line) {
|
||||
$parts = explode(':', $line, 2);
|
||||
if (!isset($parts[1])) {
|
||||
continue;
|
||||
}
|
||||
$headers[trim($parts[0])] = trim($parts[1]);
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
public function set_header($header, $value)
|
||||
{
|
||||
parent::set_header($header, $value);
|
||||
if ('Plural-Forms' == $header) {
|
||||
list($nplurals, $expression) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms'));
|
||||
$this->_nplurals = $nplurals;
|
||||
$this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
<?php
|
||||
/**
|
||||
* HTTP Proxy connection interface
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Proxy
|
||||
* @since 1.6
|
||||
*/
|
||||
|
||||
/**
|
||||
* HTTP Proxy connection interface
|
||||
*
|
||||
* Provides a handler for connection via an HTTP proxy
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Proxy
|
||||
* @since 1.6
|
||||
*/
|
||||
class Requests_Proxy_HTTP implements Requests_Proxy {
|
||||
/**
|
||||
* Proxy host and port
|
||||
*
|
||||
* Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $proxy;
|
||||
|
||||
/**
|
||||
* Username
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Password
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pass;
|
||||
|
||||
/**
|
||||
* Do we need to authenticate? (ie username & password have been provided)
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_authentication;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.6
|
||||
* @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`)
|
||||
* @param array|null $args Array of user and password. Must have exactly two elements
|
||||
*/
|
||||
public function __construct($args = null) {
|
||||
if (is_string($args)) {
|
||||
$this->proxy = $args;
|
||||
}
|
||||
elseif (is_array($args)) {
|
||||
if (count($args) == 1) {
|
||||
list($this->proxy) = $args;
|
||||
}
|
||||
elseif (count($args) == 3) {
|
||||
list($this->proxy, $this->user, $this->pass) = $args;
|
||||
$this->use_authentication = true;
|
||||
}
|
||||
else {
|
||||
throw new Requests_Exception('Invalid number of arguments', 'proxyhttpbadargs');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the necessary callbacks
|
||||
*
|
||||
* @since 1.6
|
||||
* @see curl_before_send
|
||||
* @see fsockopen_remote_socket
|
||||
* @see fsockopen_remote_host_path
|
||||
* @see fsockopen_header
|
||||
* @param Requests_Hooks $hooks Hook system
|
||||
*/
|
||||
public function register(Requests_Hooks &$hooks) {
|
||||
$hooks->register('curl.before_send', array(&$this, 'curl_before_send'));
|
||||
|
||||
$hooks->register('fsockopen.remote_socket', array(&$this, 'fsockopen_remote_socket'));
|
||||
$hooks->register('fsockopen.remote_host_path', array(&$this, 'fsockopen_remote_host_path'));
|
||||
if ($this->use_authentication) {
|
||||
$hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cURL parameters before the data is sent
|
||||
*
|
||||
* @since 1.6
|
||||
* @param resource $handle cURL resource
|
||||
*/
|
||||
public function curl_before_send(&$handle) {
|
||||
curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
||||
curl_setopt($handle, CURLOPT_PROXY, $this->proxy);
|
||||
|
||||
if ($this->use_authentication) {
|
||||
curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
|
||||
curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter remote socket information before opening socket connection
|
||||
*
|
||||
* @since 1.6
|
||||
* @param string $remote_socket Socket connection string
|
||||
*/
|
||||
public function fsockopen_remote_socket(&$remote_socket) {
|
||||
$remote_socket = $this->proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter remote path before getting stream data
|
||||
*
|
||||
* @since 1.6
|
||||
* @param string $path Path to send in HTTP request string ("GET ...")
|
||||
* @param string $url Full URL we're requesting
|
||||
*/
|
||||
public function fsockopen_remote_host_path(&$path, $url) {
|
||||
$path = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra headers to the request before sending
|
||||
*
|
||||
* @since 1.6
|
||||
* @param string $out HTTP header string
|
||||
*/
|
||||
public function fsockopen_header(&$out) {
|
||||
$out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authentication string (user:pass)
|
||||
*
|
||||
* @since 1.6
|
||||
* @return string
|
||||
*/
|
||||
public function get_auth_string() {
|
||||
return $this->user . ':' . $this->pass;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
/**
|
||||
* Case-insensitive dictionary, suitable for HTTP headers
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Case-insensitive dictionary, suitable for HTTP headers
|
||||
*
|
||||
* @package Requests
|
||||
*/
|
||||
class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictionary {
|
||||
/**
|
||||
* Get the given header
|
||||
*
|
||||
* Unlike {@see self::getValues()}, this returns a string. If there are
|
||||
* multiple values, it concatenates them with a comma as per RFC2616.
|
||||
*
|
||||
* Avoid using this where commas may be used unquoted in values, such as
|
||||
* Set-Cookie headers.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string Header value
|
||||
*/
|
||||
public function offsetGet($key) {
|
||||
$key = strtolower($key);
|
||||
if (!isset($this->data[$key])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->flatten($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given item
|
||||
*
|
||||
* @throws Requests_Exception On attempting to use dictionary as list (`invalidset`)
|
||||
*
|
||||
* @param string $key Item name
|
||||
* @param string $value Item value
|
||||
*/
|
||||
public function offsetSet($key, $value) {
|
||||
if ($key === null) {
|
||||
throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset');
|
||||
}
|
||||
|
||||
$key = strtolower($key);
|
||||
|
||||
if (!isset($this->data[$key])) {
|
||||
$this->data[$key] = array();
|
||||
}
|
||||
|
||||
$this->data[$key][] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all values for a given header
|
||||
*
|
||||
* @param string $key
|
||||
* @return array Header values
|
||||
*/
|
||||
public function getValues($key) {
|
||||
$key = strtolower($key);
|
||||
if (!isset($this->data[$key])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->data[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens a value into a string
|
||||
*
|
||||
* Converts an array into a string by imploding values with a comma, as per
|
||||
* RFC2616's rules for folding headers.
|
||||
*
|
||||
* @param string|array $value Value to flatten
|
||||
* @return string Flattened value
|
||||
*/
|
||||
public function flatten($value) {
|
||||
if (is_array($value)) {
|
||||
$value = implode(',', $value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator for the data
|
||||
*
|
||||
* Converts the internal
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/**
|
||||
* Event dispatcher
|
||||
*
|
||||
* @package Requests\EventDispatcher
|
||||
*/
|
||||
|
||||
namespace WpOrg\Requests;
|
||||
|
||||
/**
|
||||
* Event dispatcher
|
||||
*
|
||||
* @package Requests\EventDispatcher
|
||||
*/
|
||||
interface HookManager {
|
||||
/**
|
||||
* Register a callback for a hook
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param callable $callback Function/method to call on event
|
||||
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
|
||||
*/
|
||||
public function register($hook, $callback, $priority = 0);
|
||||
|
||||
/**
|
||||
* Dispatch a message
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param array $parameters Parameters to pass to callbacks
|
||||
* @return boolean Successfulness
|
||||
*/
|
||||
public function dispatch($hook, $parameters = []);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/**
|
||||
* Event dispatcher
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event dispatcher
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
interface Requests_Hooker {
|
||||
/**
|
||||
* Register a callback for a hook
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param callback $callback Function/method to call on event
|
||||
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
|
||||
*/
|
||||
public function register($hook, $callback, $priority = 0);
|
||||
|
||||
/**
|
||||
* Dispatch a message
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param array $parameters Parameters to pass to callbacks
|
||||
* @return boolean Successfulness
|
||||
*/
|
||||
public function dispatch($hook, $parameters = array());
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles adding and dispatching events
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles adding and dispatching events
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
class Requests_Hooks implements Requests_Hooker {
|
||||
/**
|
||||
* Registered callbacks for each hook
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hooks = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for a hook
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param callback $callback Function/method to call on event
|
||||
* @param int $priority Priority number. <0 is executed earlier, >0 is executed later
|
||||
*/
|
||||
public function register($hook, $callback, $priority = 0) {
|
||||
if (!isset($this->hooks[$hook])) {
|
||||
$this->hooks[$hook] = array();
|
||||
}
|
||||
if (!isset($this->hooks[$hook][$priority])) {
|
||||
$this->hooks[$hook][$priority] = array();
|
||||
}
|
||||
|
||||
$this->hooks[$hook][$priority][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a message
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param array $parameters Parameters to pass to callbacks
|
||||
* @return boolean Successfulness
|
||||
*/
|
||||
public function dispatch($hook, $parameters = array()) {
|
||||
if (empty($this->hooks[$hook])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->hooks[$hook] as $priority => $hooked) {
|
||||
foreach ($hooked as $callback) {
|
||||
call_user_func_array($callback, $parameters);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
/**
|
||||
* HTTP Proxy connection interface
|
||||
*
|
||||
* @package Requests\Proxy
|
||||
* @since 1.6
|
||||
*/
|
||||
|
||||
namespace WpOrg\Requests\Proxy;
|
||||
|
||||
use WpOrg\Requests\Exception\ArgumentCount;
|
||||
use WpOrg\Requests\Exception\InvalidArgument;
|
||||
use WpOrg\Requests\Hooks;
|
||||
use WpOrg\Requests\Proxy;
|
||||
|
||||
/**
|
||||
* HTTP Proxy connection interface
|
||||
*
|
||||
* Provides a handler for connection via an HTTP proxy
|
||||
*
|
||||
* @package Requests\Proxy
|
||||
* @since 1.6
|
||||
*/
|
||||
final class Http implements Proxy {
|
||||
/**
|
||||
* Proxy host and port
|
||||
*
|
||||
* Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $proxy;
|
||||
|
||||
/**
|
||||
* Username
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Password
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pass;
|
||||
|
||||
/**
|
||||
* Do we need to authenticate? (ie username & password have been provided)
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_authentication;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param array|string|null $args Proxy as a string or an array of proxy, user and password.
|
||||
* When passed as an array, must have exactly one (proxy)
|
||||
* or three elements (proxy, user, password).
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array, a string or null.
|
||||
* @throws \WpOrg\Requests\Exception\ArgumentCount On incorrect number of arguments (`proxyhttpbadargs`)
|
||||
*/
|
||||
public function __construct($args = null) {
|
||||
if (is_string($args)) {
|
||||
$this->proxy = $args;
|
||||
} elseif (is_array($args)) {
|
||||
if (count($args) === 1) {
|
||||
list($this->proxy) = $args;
|
||||
} elseif (count($args) === 3) {
|
||||
list($this->proxy, $this->user, $this->pass) = $args;
|
||||
$this->use_authentication = true;
|
||||
} else {
|
||||
throw ArgumentCount::create(
|
||||
'an array with exactly one element or exactly three elements',
|
||||
count($args),
|
||||
'proxyhttpbadargs'
|
||||
);
|
||||
}
|
||||
} elseif ($args !== null) {
|
||||
throw InvalidArgument::create(1, '$args', 'array|string|null', gettype($args));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the necessary callbacks
|
||||
*
|
||||
* @since 1.6
|
||||
* @see \WpOrg\Requests\Proxy\Http::curl_before_send()
|
||||
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_socket()
|
||||
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_host_path()
|
||||
* @see \WpOrg\Requests\Proxy\Http::fsockopen_header()
|
||||
* @param \WpOrg\Requests\Hooks $hooks Hook system
|
||||
*/
|
||||
public function register(Hooks $hooks) {
|
||||
$hooks->register('curl.before_send', [$this, 'curl_before_send']);
|
||||
|
||||
$hooks->register('fsockopen.remote_socket', [$this, 'fsockopen_remote_socket']);
|
||||
$hooks->register('fsockopen.remote_host_path', [$this, 'fsockopen_remote_host_path']);
|
||||
if ($this->use_authentication) {
|
||||
$hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cURL parameters before the data is sent
|
||||
*
|
||||
* @since 1.6
|
||||
* @param resource|\CurlHandle $handle cURL handle
|
||||
*/
|
||||
public function curl_before_send(&$handle) {
|
||||
curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
||||
curl_setopt($handle, CURLOPT_PROXY, $this->proxy);
|
||||
|
||||
if ($this->use_authentication) {
|
||||
curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
|
||||
curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter remote socket information before opening socket connection
|
||||
*
|
||||
* @since 1.6
|
||||
* @param string $remote_socket Socket connection string
|
||||
*/
|
||||
public function fsockopen_remote_socket(&$remote_socket) {
|
||||
$remote_socket = $this->proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter remote path before getting stream data
|
||||
*
|
||||
* @since 1.6
|
||||
* @param string $path Path to send in HTTP request string ("GET ...")
|
||||
* @param string $url Full URL we're requesting
|
||||
*/
|
||||
public function fsockopen_remote_host_path(&$path, $url) {
|
||||
$path = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra headers to the request before sending
|
||||
*
|
||||
* @since 1.6
|
||||
* @param string $out HTTP header string
|
||||
*/
|
||||
public function fsockopen_header(&$out) {
|
||||
$out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authentication string (user:pass)
|
||||
*
|
||||
* @since 1.6
|
||||
* @return string
|
||||
*/
|
||||
public function get_auth_string() {
|
||||
return $this->user . ':' . $this->pass;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* This class represents an HTTP transport error.
|
||||
*/
|
||||
class HttpException extends WebServiceException
|
||||
{
|
||||
/**
|
||||
* The URI queried.
|
||||
*/
|
||||
private $uri;
|
||||
|
||||
/**
|
||||
* @param string $message a message describing the error
|
||||
* @param int $httpStatus the HTTP status code of the response
|
||||
* @param string $uri the URI used in the request
|
||||
* @param \Exception $previous the previous exception, if any
|
||||
*/
|
||||
public function __construct(
|
||||
string $message,
|
||||
int $httpStatus,
|
||||
string $uri,
|
||||
\Exception $previous = null
|
||||
) {
|
||||
$this->uri = $uri;
|
||||
parent::__construct($message, $httpStatus, $previous);
|
||||
}
|
||||
|
||||
public function getUri(): string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function getStatusCode(): int
|
||||
{
|
||||
return $this->getCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,388 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* IDNA URL encoder
|
||||
*
|
||||
* Note: Not fully compliant, as nameprep does nothing yet.
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
* @see https://tools.ietf.org/html/rfc3490 IDNA specification
|
||||
* @see https://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification
|
||||
*/
|
||||
class Requests_IDNAEncoder {
|
||||
/**
|
||||
* ACE prefix used for IDNA
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3490#section-5
|
||||
* @var string
|
||||
*/
|
||||
const ACE_PREFIX = 'xn--';
|
||||
|
||||
/**#@+
|
||||
* Bootstrap constant for Punycode
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3492#section-5
|
||||
* @var int
|
||||
*/
|
||||
const BOOTSTRAP_BASE = 36;
|
||||
const BOOTSTRAP_TMIN = 1;
|
||||
const BOOTSTRAP_TMAX = 26;
|
||||
const BOOTSTRAP_SKEW = 38;
|
||||
const BOOTSTRAP_DAMP = 700;
|
||||
const BOOTSTRAP_INITIAL_BIAS = 72;
|
||||
const BOOTSTRAP_INITIAL_N = 128;
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Encode a hostname using Punycode
|
||||
*
|
||||
* @param string $string Hostname
|
||||
* @return string Punycode-encoded hostname
|
||||
*/
|
||||
public static function encode($string) {
|
||||
$parts = explode('.', $string);
|
||||
foreach ($parts as &$part) {
|
||||
$part = self::to_ascii($part);
|
||||
}
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a UTF-8 string to an ASCII string using Punycode
|
||||
*
|
||||
* @throws Requests_Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`)
|
||||
* @throws Requests_Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`)
|
||||
* @throws Requests_Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`)
|
||||
* @throws Requests_Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`)
|
||||
*
|
||||
* @param string $string ASCII or UTF-8 string (max length 64 characters)
|
||||
* @return string ASCII string
|
||||
*/
|
||||
public static function to_ascii($string) {
|
||||
// Step 1: Check if the string is already ASCII
|
||||
if (self::is_ascii($string)) {
|
||||
// Skip to step 7
|
||||
if (strlen($string) < 64) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
throw new Requests_Exception('Provided string is too long', 'idna.provided_too_long', $string);
|
||||
}
|
||||
|
||||
// Step 2: nameprep
|
||||
$string = self::nameprep($string);
|
||||
|
||||
// Step 3: UseSTD3ASCIIRules is false, continue
|
||||
// Step 4: Check if it's ASCII now
|
||||
if (self::is_ascii($string)) {
|
||||
// Skip to step 7
|
||||
if (strlen($string) < 64) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
throw new Requests_Exception('Prepared string is too long', 'idna.prepared_too_long', $string);
|
||||
}
|
||||
|
||||
// Step 5: Check ACE prefix
|
||||
if (strpos($string, self::ACE_PREFIX) === 0) {
|
||||
throw new Requests_Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $string);
|
||||
}
|
||||
|
||||
// Step 6: Encode with Punycode
|
||||
$string = self::punycode_encode($string);
|
||||
|
||||
// Step 7: Prepend ACE prefix
|
||||
$string = self::ACE_PREFIX . $string;
|
||||
|
||||
// Step 8: Check size
|
||||
if (strlen($string) < 64) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
throw new Requests_Exception('Encoded string is too long', 'idna.encoded_too_long', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given string contains only ASCII characters
|
||||
*
|
||||
* @internal (Testing found regex was the fastest implementation)
|
||||
*
|
||||
* @param string $string
|
||||
* @return bool Is the string ASCII-only?
|
||||
*/
|
||||
protected static function is_ascii($string) {
|
||||
return (preg_match('/(?:[^\x00-\x7F])/', $string) !== 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a string for use as an IDNA name
|
||||
*
|
||||
* @todo Implement this based on RFC 3491 and the newer 5891
|
||||
* @param string $string
|
||||
* @return string Prepared string
|
||||
*/
|
||||
protected static function nameprep($string) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a UTF-8 string to a UCS-4 codepoint array
|
||||
*
|
||||
* Based on Requests_IRI::replace_invalid_with_pct_encoding()
|
||||
*
|
||||
* @throws Requests_Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`)
|
||||
* @param string $input
|
||||
* @return array Unicode code points
|
||||
*/
|
||||
protected static function utf8_to_codepoints($input) {
|
||||
$codepoints = array();
|
||||
|
||||
// Get number of bytes
|
||||
$strlen = strlen($input);
|
||||
|
||||
for ($position = 0; $position < $strlen; $position++) {
|
||||
$value = ord($input[$position]);
|
||||
|
||||
// One byte sequence:
|
||||
if ((~$value & 0x80) === 0x80) {
|
||||
$character = $value;
|
||||
$length = 1;
|
||||
$remaining = 0;
|
||||
}
|
||||
// Two byte sequence:
|
||||
elseif (($value & 0xE0) === 0xC0) {
|
||||
$character = ($value & 0x1F) << 6;
|
||||
$length = 2;
|
||||
$remaining = 1;
|
||||
}
|
||||
// Three byte sequence:
|
||||
elseif (($value & 0xF0) === 0xE0) {
|
||||
$character = ($value & 0x0F) << 12;
|
||||
$length = 3;
|
||||
$remaining = 2;
|
||||
}
|
||||
// Four byte sequence:
|
||||
elseif (($value & 0xF8) === 0xF0) {
|
||||
$character = ($value & 0x07) << 18;
|
||||
$length = 4;
|
||||
$remaining = 3;
|
||||
}
|
||||
// Invalid byte:
|
||||
else {
|
||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value);
|
||||
}
|
||||
|
||||
if ($remaining > 0) {
|
||||
if ($position + $length > $strlen) {
|
||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||
}
|
||||
for ($position++; $remaining > 0; $position++) {
|
||||
$value = ord($input[$position]);
|
||||
|
||||
// If it is invalid, count the sequence as invalid and reprocess the current byte:
|
||||
if (($value & 0xC0) !== 0x80) {
|
||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||
}
|
||||
|
||||
$character |= ($value & 0x3F) << (--$remaining * 6);
|
||||
}
|
||||
$position--;
|
||||
}
|
||||
|
||||
if (
|
||||
// Non-shortest form sequences are invalid
|
||||
$length > 1 && $character <= 0x7F
|
||||
|| $length > 2 && $character <= 0x7FF
|
||||
|| $length > 3 && $character <= 0xFFFF
|
||||
// Outside of range of ucschar codepoints
|
||||
// Noncharacters
|
||||
|| ($character & 0xFFFE) === 0xFFFE
|
||||
|| $character >= 0xFDD0 && $character <= 0xFDEF
|
||||
|| (
|
||||
// Everything else not in ucschar
|
||||
$character > 0xD7FF && $character < 0xF900
|
||||
|| $character < 0x20
|
||||
|| $character > 0x7E && $character < 0xA0
|
||||
|| $character > 0xEFFFD
|
||||
)
|
||||
) {
|
||||
throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||
}
|
||||
|
||||
$codepoints[] = $character;
|
||||
}
|
||||
|
||||
return $codepoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC3492-compliant encoder
|
||||
*
|
||||
* @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code
|
||||
* @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
|
||||
*
|
||||
* @param string $input UTF-8 encoded string to encode
|
||||
* @return string Punycode-encoded string
|
||||
*/
|
||||
public static function punycode_encode($input) {
|
||||
$output = '';
|
||||
# let n = initial_n
|
||||
$n = self::BOOTSTRAP_INITIAL_N;
|
||||
# let delta = 0
|
||||
$delta = 0;
|
||||
# let bias = initial_bias
|
||||
$bias = self::BOOTSTRAP_INITIAL_BIAS;
|
||||
# let h = b = the number of basic code points in the input
|
||||
$h = $b = 0; // see loop
|
||||
# copy them to the output in order
|
||||
$codepoints = self::utf8_to_codepoints($input);
|
||||
$extended = array();
|
||||
|
||||
foreach ($codepoints as $char) {
|
||||
if ($char < 128) {
|
||||
// Character is valid ASCII
|
||||
// TODO: this should also check if it's valid for a URL
|
||||
$output .= chr($char);
|
||||
$h++;
|
||||
}
|
||||
// Check if the character is non-ASCII, but below initial n
|
||||
// This never occurs for Punycode, so ignore in coverage
|
||||
// @codeCoverageIgnoreStart
|
||||
elseif ($char < $n) {
|
||||
throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
else {
|
||||
$extended[$char] = true;
|
||||
}
|
||||
}
|
||||
$extended = array_keys($extended);
|
||||
sort($extended);
|
||||
$b = $h;
|
||||
# [copy them] followed by a delimiter if b > 0
|
||||
if (strlen($output) > 0) {
|
||||
$output .= '-';
|
||||
}
|
||||
# {if the input contains a non-basic code point < n then fail}
|
||||
# while h < length(input) do begin
|
||||
while ($h < count($codepoints)) {
|
||||
# let m = the minimum code point >= n in the input
|
||||
$m = array_shift($extended);
|
||||
//printf('next code point to insert is %s' . PHP_EOL, dechex($m));
|
||||
# let delta = delta + (m - n) * (h + 1), fail on overflow
|
||||
$delta += ($m - $n) * ($h + 1);
|
||||
# let n = m
|
||||
$n = $m;
|
||||
# for each code point c in the input (in order) do begin
|
||||
for ($num = 0; $num < count($codepoints); $num++) {
|
||||
$c = $codepoints[$num];
|
||||
# if c < n then increment delta, fail on overflow
|
||||
if ($c < $n) {
|
||||
$delta++;
|
||||
}
|
||||
# if c == n then begin
|
||||
elseif ($c === $n) {
|
||||
# let q = delta
|
||||
$q = $delta;
|
||||
# for k = base to infinity in steps of base do begin
|
||||
for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) {
|
||||
# let t = tmin if k <= bias {+ tmin}, or
|
||||
# tmax if k >= bias + tmax, or k - bias otherwise
|
||||
if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
|
||||
$t = self::BOOTSTRAP_TMIN;
|
||||
}
|
||||
elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
|
||||
$t = self::BOOTSTRAP_TMAX;
|
||||
}
|
||||
else {
|
||||
$t = $k - $bias;
|
||||
}
|
||||
# if q < t then break
|
||||
if ($q < $t) {
|
||||
break;
|
||||
}
|
||||
# output the code point for digit t + ((q - t) mod (base - t))
|
||||
$digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t));
|
||||
$output .= self::digit_to_char($digit);
|
||||
# let q = (q - t) div (base - t)
|
||||
$q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t));
|
||||
# end
|
||||
}
|
||||
# output the code point for digit q
|
||||
$output .= self::digit_to_char($q);
|
||||
# let bias = adapt(delta, h + 1, test h equals b?)
|
||||
$bias = self::adapt($delta, $h + 1, $h === $b);
|
||||
# let delta = 0
|
||||
$delta = 0;
|
||||
# increment h
|
||||
$h++;
|
||||
# end
|
||||
}
|
||||
# end
|
||||
}
|
||||
# increment delta and n
|
||||
$delta++;
|
||||
$n++;
|
||||
# end
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a digit to its respective character
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3492#section-5
|
||||
* @throws Requests_Exception On invalid digit (`idna.invalid_digit`)
|
||||
*
|
||||
* @param int $digit Digit in the range 0-35
|
||||
* @return string Single character corresponding to digit
|
||||
*/
|
||||
protected static function digit_to_char($digit) {
|
||||
// @codeCoverageIgnoreStart
|
||||
// As far as I know, this never happens, but still good to be sure.
|
||||
if ($digit < 0 || $digit > 35) {
|
||||
throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
$digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
return substr($digits, $digit, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the bias
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3492#section-6.1
|
||||
* @param int $delta
|
||||
* @param int $numpoints
|
||||
* @param bool $firsttime
|
||||
* @return int New bias
|
||||
*/
|
||||
protected static function adapt($delta, $numpoints, $firsttime) {
|
||||
# function adapt(delta,numpoints,firsttime):
|
||||
# if firsttime then let delta = delta div damp
|
||||
if ($firsttime) {
|
||||
$delta = floor($delta / self::BOOTSTRAP_DAMP);
|
||||
}
|
||||
# else let delta = delta div 2
|
||||
else {
|
||||
$delta = floor($delta / 2);
|
||||
}
|
||||
# let delta = delta + (delta div numpoints)
|
||||
$delta += floor($delta / $numpoints);
|
||||
# let k = 0
|
||||
$k = 0;
|
||||
# while delta > ((base - tmin) * tmax) div 2 do begin
|
||||
$max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2);
|
||||
while ($delta > $max) {
|
||||
# let delta = delta div (base - tmin)
|
||||
$delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN));
|
||||
# let k = k + base
|
||||
$k += self::BOOTSTRAP_BASE;
|
||||
# end
|
||||
}
|
||||
# return k + (((base - tmin + 1) * delta) div (delta + skew))
|
||||
return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
/**
|
||||
* Class to validate and to work with IPv6 addresses
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class to validate and to work with IPv6 addresses
|
||||
*
|
||||
* This was originally based on the PEAR class of the same name, but has been
|
||||
* entirely rewritten.
|
||||
*
|
||||
* @package Requests
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
class Requests_IPv6 {
|
||||
/**
|
||||
* Uncompresses an IPv6 address
|
||||
*
|
||||
* RFC 4291 allows you to compress consecutive zero pieces in an address to
|
||||
* '::'. This method expects a valid IPv6 address and expands the '::' to
|
||||
* the required number of zero pieces.
|
||||
*
|
||||
* Example: FF01::101 -> FF01:0:0:0:0:0:0:101
|
||||
* ::1 -> 0:0:0:0:0:0:0:1
|
||||
*
|
||||
* @author Alexander Merz <alexander.merz@web.de>
|
||||
* @author elfrink at introweb dot nl
|
||||
* @author Josh Peck <jmp at joshpeck dot org>
|
||||
* @copyright 2003-2005 The PHP Group
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php
|
||||
* @param string $ip An IPv6 address
|
||||
* @return string The uncompressed IPv6 address
|
||||
*/
|
||||
public static function uncompress($ip) {
|
||||
if (substr_count($ip, '::') !== 1) {
|
||||
return $ip;
|
||||
}
|
||||
|
||||
list($ip1, $ip2) = explode('::', $ip);
|
||||
$c1 = ($ip1 === '') ? -1 : substr_count($ip1, ':');
|
||||
$c2 = ($ip2 === '') ? -1 : substr_count($ip2, ':');
|
||||
|
||||
if (strpos($ip2, '.') !== false) {
|
||||
$c2++;
|
||||
}
|
||||
// ::
|
||||
if ($c1 === -1 && $c2 === -1) {
|
||||
$ip = '0:0:0:0:0:0:0:0';
|
||||
}
|
||||
// ::xxx
|
||||
else if ($c1 === -1) {
|
||||
$fill = str_repeat('0:', 7 - $c2);
|
||||
$ip = str_replace('::', $fill, $ip);
|
||||
}
|
||||
// xxx::
|
||||
else if ($c2 === -1) {
|
||||
$fill = str_repeat(':0', 7 - $c1);
|
||||
$ip = str_replace('::', $fill, $ip);
|
||||
}
|
||||
// xxx::xxx
|
||||
else {
|
||||
$fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
|
||||
$ip = str_replace('::', $fill, $ip);
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses an IPv6 address
|
||||
*
|
||||
* RFC 4291 allows you to compress consecutive zero pieces in an address to
|
||||
* '::'. This method expects a valid IPv6 address and compresses consecutive
|
||||
* zero pieces to '::'.
|
||||
*
|
||||
* Example: FF01:0:0:0:0:0:0:101 -> FF01::101
|
||||
* 0:0:0:0:0:0:0:1 -> ::1
|
||||
*
|
||||
* @see uncompress()
|
||||
* @param string $ip An IPv6 address
|
||||
* @return string The compressed IPv6 address
|
||||
*/
|
||||
public static function compress($ip) {
|
||||
// Prepare the IP to be compressed
|
||||
$ip = self::uncompress($ip);
|
||||
$ip_parts = self::split_v6_v4($ip);
|
||||
|
||||
// Replace all leading zeros
|
||||
$ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
|
||||
|
||||
// Find bunches of zeros
|
||||
if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
|
||||
$max = 0;
|
||||
$pos = null;
|
||||
foreach ($matches[0] as $match) {
|
||||
if (strlen($match[0]) > $max) {
|
||||
$max = strlen($match[0]);
|
||||
$pos = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
$ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
|
||||
}
|
||||
|
||||
if ($ip_parts[1] !== '') {
|
||||
return implode(':', $ip_parts);
|
||||
}
|
||||
else {
|
||||
return $ip_parts[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits an IPv6 address into the IPv6 and IPv4 representation parts
|
||||
*
|
||||
* RFC 4291 allows you to represent the last two parts of an IPv6 address
|
||||
* using the standard IPv4 representation
|
||||
*
|
||||
* Example: 0:0:0:0:0:0:13.1.68.3
|
||||
* 0:0:0:0:0:FFFF:129.144.52.38
|
||||
*
|
||||
* @param string $ip An IPv6 address
|
||||
* @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part
|
||||
*/
|
||||
protected static function split_v6_v4($ip) {
|
||||
if (strpos($ip, '.') !== false) {
|
||||
$pos = strrpos($ip, ':');
|
||||
$ipv6_part = substr($ip, 0, $pos);
|
||||
$ipv4_part = substr($ip, $pos + 1);
|
||||
return array($ipv6_part, $ipv4_part);
|
||||
}
|
||||
else {
|
||||
return array($ip, '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks an IPv6 address
|
||||
*
|
||||
* Checks if the given IP is a valid IPv6 address
|
||||
*
|
||||
* @param string $ip An IPv6 address
|
||||
* @return bool true if $ip is a valid IPv6 address
|
||||
*/
|
||||
public static function check_ipv6($ip) {
|
||||
$ip = self::uncompress($ip);
|
||||
list($ipv6, $ipv4) = self::split_v6_v4($ip);
|
||||
$ipv6 = explode(':', $ipv6);
|
||||
$ipv4 = explode('.', $ipv4);
|
||||
if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
|
||||
foreach ($ipv6 as $ipv6_part) {
|
||||
// The section can't be empty
|
||||
if ($ipv6_part === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nor can it be over four characters
|
||||
if (strlen($ipv6_part) > 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove leading zeros (this is safe because of the above)
|
||||
$ipv6_part = ltrim($ipv6_part, '0');
|
||||
if ($ipv6_part === '') {
|
||||
$ipv6_part = '0';
|
||||
}
|
||||
|
||||
// Check the value is valid
|
||||
$value = hexdec($ipv6_part);
|
||||
if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (count($ipv4) === 4) {
|
||||
foreach ($ipv4 as $ipv4_part) {
|
||||
$value = (int) $ipv4_part;
|
||||
if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,925 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com> and Trevor Rowbotham <trevor.rowbotham@pm.me>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Polyfill\Intl\Idn;
|
||||
|
||||
use Exception;
|
||||
use Normalizer;
|
||||
use Symfony\Polyfill\Intl\Idn\Resources\unidata\DisallowedRanges;
|
||||
use Symfony\Polyfill\Intl\Idn\Resources\unidata\Regex;
|
||||
|
||||
/**
|
||||
* @see https://www.unicode.org/reports/tr46/
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Idn
|
||||
{
|
||||
public const ERROR_EMPTY_LABEL = 1;
|
||||
public const ERROR_LABEL_TOO_LONG = 2;
|
||||
public const ERROR_DOMAIN_NAME_TOO_LONG = 4;
|
||||
public const ERROR_LEADING_HYPHEN = 8;
|
||||
public const ERROR_TRAILING_HYPHEN = 0x10;
|
||||
public const ERROR_HYPHEN_3_4 = 0x20;
|
||||
public const ERROR_LEADING_COMBINING_MARK = 0x40;
|
||||
public const ERROR_DISALLOWED = 0x80;
|
||||
public const ERROR_PUNYCODE = 0x100;
|
||||
public const ERROR_LABEL_HAS_DOT = 0x200;
|
||||
public const ERROR_INVALID_ACE_LABEL = 0x400;
|
||||
public const ERROR_BIDI = 0x800;
|
||||
public const ERROR_CONTEXTJ = 0x1000;
|
||||
public const ERROR_CONTEXTO_PUNCTUATION = 0x2000;
|
||||
public const ERROR_CONTEXTO_DIGITS = 0x4000;
|
||||
|
||||
public const INTL_IDNA_VARIANT_2003 = 0;
|
||||
public const INTL_IDNA_VARIANT_UTS46 = 1;
|
||||
|
||||
public const IDNA_DEFAULT = 0;
|
||||
public const IDNA_ALLOW_UNASSIGNED = 1;
|
||||
public const IDNA_USE_STD3_RULES = 2;
|
||||
public const IDNA_CHECK_BIDI = 4;
|
||||
public const IDNA_CHECK_CONTEXTJ = 8;
|
||||
public const IDNA_NONTRANSITIONAL_TO_ASCII = 16;
|
||||
public const IDNA_NONTRANSITIONAL_TO_UNICODE = 32;
|
||||
|
||||
public const MAX_DOMAIN_SIZE = 253;
|
||||
public const MAX_LABEL_SIZE = 63;
|
||||
|
||||
public const BASE = 36;
|
||||
public const TMIN = 1;
|
||||
public const TMAX = 26;
|
||||
public const SKEW = 38;
|
||||
public const DAMP = 700;
|
||||
public const INITIAL_BIAS = 72;
|
||||
public const INITIAL_N = 128;
|
||||
public const DELIMITER = '-';
|
||||
public const MAX_INT = 2147483647;
|
||||
|
||||
/**
|
||||
* Contains the numeric value of a basic code point (for use in representing integers) in the
|
||||
* range 0 to BASE-1, or -1 if b is does not represent a value.
|
||||
*
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private static $basicToDigit = [
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
|
||||
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private static $virama;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $mapped;
|
||||
|
||||
/**
|
||||
* @var array<int, bool>
|
||||
*/
|
||||
private static $ignored;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $deviation;
|
||||
|
||||
/**
|
||||
* @var array<int, bool>
|
||||
*/
|
||||
private static $disallowed;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $disallowed_STD3_mapped;
|
||||
|
||||
/**
|
||||
* @var array<int, bool>
|
||||
*/
|
||||
private static $disallowed_STD3_valid;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $mappingTableLoaded = false;
|
||||
|
||||
/**
|
||||
* @see https://www.unicode.org/reports/tr46/#ToASCII
|
||||
*
|
||||
* @param string $domainName
|
||||
* @param int $options
|
||||
* @param int $variant
|
||||
* @param array $idna_info
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public static function idn_to_ascii($domainName, $options = self::IDNA_DEFAULT, $variant = self::INTL_IDNA_VARIANT_UTS46, &$idna_info = [])
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) {
|
||||
@trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$options = [
|
||||
'CheckHyphens' => true,
|
||||
'CheckBidi' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 !== ($options & self::IDNA_CHECK_BIDI),
|
||||
'CheckJoiners' => self::INTL_IDNA_VARIANT_UTS46 === $variant && 0 !== ($options & self::IDNA_CHECK_CONTEXTJ),
|
||||
'UseSTD3ASCIIRules' => 0 !== ($options & self::IDNA_USE_STD3_RULES),
|
||||
'Transitional_Processing' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 === ($options & self::IDNA_NONTRANSITIONAL_TO_ASCII),
|
||||
'VerifyDnsLength' => true,
|
||||
];
|
||||
$info = new Info();
|
||||
$labels = self::process((string) $domainName, $options, $info);
|
||||
|
||||
foreach ($labels as $i => $label) {
|
||||
// Only convert labels to punycode that contain non-ASCII code points
|
||||
if (1 === preg_match('/[^\x00-\x7F]/', $label)) {
|
||||
try {
|
||||
$label = 'xn--'.self::punycodeEncode($label);
|
||||
} catch (Exception $e) {
|
||||
$info->errors |= self::ERROR_PUNYCODE;
|
||||
}
|
||||
|
||||
$labels[$i] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['VerifyDnsLength']) {
|
||||
self::validateDomainAndLabelLength($labels, $info);
|
||||
}
|
||||
|
||||
$idna_info = [
|
||||
'result' => implode('.', $labels),
|
||||
'isTransitionalDifferent' => $info->transitionalDifferent,
|
||||
'errors' => $info->errors,
|
||||
];
|
||||
|
||||
return 0 === $info->errors ? $idna_info['result'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.unicode.org/reports/tr46/#ToUnicode
|
||||
*
|
||||
* @param string $domainName
|
||||
* @param int $options
|
||||
* @param int $variant
|
||||
* @param array $idna_info
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public static function idn_to_utf8($domainName, $options = self::IDNA_DEFAULT, $variant = self::INTL_IDNA_VARIANT_UTS46, &$idna_info = [])
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) {
|
||||
@trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$info = new Info();
|
||||
$labels = self::process((string) $domainName, [
|
||||
'CheckHyphens' => true,
|
||||
'CheckBidi' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 !== ($options & self::IDNA_CHECK_BIDI),
|
||||
'CheckJoiners' => self::INTL_IDNA_VARIANT_UTS46 === $variant && 0 !== ($options & self::IDNA_CHECK_CONTEXTJ),
|
||||
'UseSTD3ASCIIRules' => 0 !== ($options & self::IDNA_USE_STD3_RULES),
|
||||
'Transitional_Processing' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 === ($options & self::IDNA_NONTRANSITIONAL_TO_UNICODE),
|
||||
], $info);
|
||||
$idna_info = [
|
||||
'result' => implode('.', $labels),
|
||||
'isTransitionalDifferent' => $info->transitionalDifferent,
|
||||
'errors' => $info->errors,
|
||||
];
|
||||
|
||||
return 0 === $info->errors ? $idna_info['result'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function isValidContextJ(array $codePoints, $label)
|
||||
{
|
||||
if (!isset(self::$virama)) {
|
||||
self::$virama = require __DIR__.\DIRECTORY_SEPARATOR.'Resources'.\DIRECTORY_SEPARATOR.'unidata'.\DIRECTORY_SEPARATOR.'virama.php';
|
||||
}
|
||||
|
||||
$offset = 0;
|
||||
|
||||
foreach ($codePoints as $i => $codePoint) {
|
||||
if (0x200C !== $codePoint && 0x200D !== $codePoint) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($codePoints[$i - 1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If Canonical_Combining_Class(Before(cp)) .eq. Virama Then True;
|
||||
if (isset(self::$virama[$codePoints[$i - 1]])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If RegExpMatch((Joining_Type:{L,D})(Joining_Type:T)*\u200C(Joining_Type:T)*(Joining_Type:{R,D})) Then
|
||||
// True;
|
||||
// Generated RegExp = ([Joining_Type:{L,D}][Joining_Type:T]*\u200C[Joining_Type:T]*)[Joining_Type:{R,D}]
|
||||
if (0x200C === $codePoint && 1 === preg_match(Regex::ZWNJ, $label, $matches, \PREG_OFFSET_CAPTURE, $offset)) {
|
||||
$offset += \strlen($matches[1][0]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.unicode.org/reports/tr46/#ProcessingStepMap
|
||||
*
|
||||
* @param string $input
|
||||
* @param array<string, bool> $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function mapCodePoints($input, array $options, Info $info)
|
||||
{
|
||||
$str = '';
|
||||
$useSTD3ASCIIRules = $options['UseSTD3ASCIIRules'];
|
||||
$transitional = $options['Transitional_Processing'];
|
||||
|
||||
foreach (self::utf8Decode($input) as $codePoint) {
|
||||
$data = self::lookupCodePointStatus($codePoint, $useSTD3ASCIIRules);
|
||||
|
||||
switch ($data['status']) {
|
||||
case 'disallowed':
|
||||
$info->errors |= self::ERROR_DISALLOWED;
|
||||
|
||||
// no break.
|
||||
|
||||
case 'valid':
|
||||
$str .= mb_chr($codePoint, 'utf-8');
|
||||
|
||||
break;
|
||||
|
||||
case 'ignored':
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
case 'mapped':
|
||||
$str .= $data['mapping'];
|
||||
|
||||
break;
|
||||
|
||||
case 'deviation':
|
||||
$info->transitionalDifferent = true;
|
||||
$str .= ($transitional ? $data['mapping'] : mb_chr($codePoint, 'utf-8'));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.unicode.org/reports/tr46/#Processing
|
||||
*
|
||||
* @param string $domain
|
||||
* @param array<string, bool> $options
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private static function process($domain, array $options, Info $info)
|
||||
{
|
||||
// If VerifyDnsLength is not set, we are doing ToUnicode otherwise we are doing ToASCII and
|
||||
// we need to respect the VerifyDnsLength option.
|
||||
$checkForEmptyLabels = !isset($options['VerifyDnsLength']) || $options['VerifyDnsLength'];
|
||||
|
||||
if ($checkForEmptyLabels && '' === $domain) {
|
||||
$info->errors |= self::ERROR_EMPTY_LABEL;
|
||||
|
||||
return [$domain];
|
||||
}
|
||||
|
||||
// Step 1. Map each code point in the domain name string
|
||||
$domain = self::mapCodePoints($domain, $options, $info);
|
||||
|
||||
// Step 2. Normalize the domain name string to Unicode Normalization Form C.
|
||||
if (!Normalizer::isNormalized($domain, Normalizer::FORM_C)) {
|
||||
$domain = Normalizer::normalize($domain, Normalizer::FORM_C);
|
||||
}
|
||||
|
||||
// Step 3. Break the string into labels at U+002E (.) FULL STOP.
|
||||
$labels = explode('.', $domain);
|
||||
$lastLabelIndex = \count($labels) - 1;
|
||||
|
||||
// Step 4. Convert and validate each label in the domain name string.
|
||||
foreach ($labels as $i => $label) {
|
||||
$validationOptions = $options;
|
||||
|
||||
if ('xn--' === substr($label, 0, 4)) {
|
||||
try {
|
||||
$label = self::punycodeDecode(substr($label, 4));
|
||||
} catch (Exception $e) {
|
||||
$info->errors |= self::ERROR_PUNYCODE;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$validationOptions['Transitional_Processing'] = false;
|
||||
$labels[$i] = $label;
|
||||
}
|
||||
|
||||
self::validateLabel($label, $info, $validationOptions, $i > 0 && $i === $lastLabelIndex);
|
||||
}
|
||||
|
||||
if ($info->bidiDomain && !$info->validBidiDomain) {
|
||||
$info->errors |= self::ERROR_BIDI;
|
||||
}
|
||||
|
||||
// Any input domain name string that does not record an error has been successfully
|
||||
// processed according to this specification. Conversely, if an input domain_name string
|
||||
// causes an error, then the processing of the input domain_name string fails. Determining
|
||||
// what to do with error input is up to the caller, and not in the scope of this document.
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5893#section-2
|
||||
*
|
||||
* @param string $label
|
||||
*/
|
||||
private static function validateBidiLabel($label, Info $info)
|
||||
{
|
||||
if (1 === preg_match(Regex::RTL_LABEL, $label)) {
|
||||
$info->bidiDomain = true;
|
||||
|
||||
// Step 1. The first character must be a character with Bidi property L, R, or AL.
|
||||
// If it has the R or AL property, it is an RTL label
|
||||
if (1 !== preg_match(Regex::BIDI_STEP_1_RTL, $label)) {
|
||||
$info->validBidiDomain = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES,
|
||||
// CS, ET, ON, BN, or NSM are allowed.
|
||||
if (1 === preg_match(Regex::BIDI_STEP_2, $label)) {
|
||||
$info->validBidiDomain = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3. In an RTL label, the end of the label must be a character with Bidi property
|
||||
// R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM.
|
||||
if (1 !== preg_match(Regex::BIDI_STEP_3, $label)) {
|
||||
$info->validBidiDomain = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 4. In an RTL label, if an EN is present, no AN may be present, and vice versa.
|
||||
if (1 === preg_match(Regex::BIDI_STEP_4_AN, $label) && 1 === preg_match(Regex::BIDI_STEP_4_EN, $label)) {
|
||||
$info->validBidiDomain = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We are a LTR label
|
||||
// Step 1. The first character must be a character with Bidi property L, R, or AL.
|
||||
// If it has the L property, it is an LTR label.
|
||||
if (1 !== preg_match(Regex::BIDI_STEP_1_LTR, $label)) {
|
||||
$info->validBidiDomain = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 5. In an LTR label, only characters with the Bidi properties L, EN,
|
||||
// ES, CS, ET, ON, BN, or NSM are allowed.
|
||||
if (1 === preg_match(Regex::BIDI_STEP_5, $label)) {
|
||||
$info->validBidiDomain = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 6.In an LTR label, the end of the label must be a character with Bidi property L or
|
||||
// EN, followed by zero or more characters with Bidi property NSM.
|
||||
if (1 !== preg_match(Regex::BIDI_STEP_6, $label)) {
|
||||
$info->validBidiDomain = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $labels
|
||||
*/
|
||||
private static function validateDomainAndLabelLength(array $labels, Info $info)
|
||||
{
|
||||
$maxDomainSize = self::MAX_DOMAIN_SIZE;
|
||||
$length = \count($labels);
|
||||
|
||||
// Number of "." delimiters.
|
||||
$domainLength = $length - 1;
|
||||
|
||||
// If the last label is empty and it is not the first label, then it is the root label.
|
||||
// Increase the max size by 1, making it 254, to account for the root label's "."
|
||||
// delimiter. This also means we don't need to check the last label's length for being too
|
||||
// long.
|
||||
if ($length > 1 && '' === $labels[$length - 1]) {
|
||||
++$maxDomainSize;
|
||||
--$length;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$bytes = \strlen($labels[$i]);
|
||||
$domainLength += $bytes;
|
||||
|
||||
if ($bytes > self::MAX_LABEL_SIZE) {
|
||||
$info->errors |= self::ERROR_LABEL_TOO_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
if ($domainLength > $maxDomainSize) {
|
||||
$info->errors |= self::ERROR_DOMAIN_NAME_TOO_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://www.unicode.org/reports/tr46/#Validity_Criteria
|
||||
*
|
||||
* @param string $label
|
||||
* @param array<string, bool> $options
|
||||
* @param bool $canBeEmpty
|
||||
*/
|
||||
private static function validateLabel($label, Info $info, array $options, $canBeEmpty)
|
||||
{
|
||||
if ('' === $label) {
|
||||
if (!$canBeEmpty && (!isset($options['VerifyDnsLength']) || $options['VerifyDnsLength'])) {
|
||||
$info->errors |= self::ERROR_EMPTY_LABEL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1. The label must be in Unicode Normalization Form C.
|
||||
if (!Normalizer::isNormalized($label, Normalizer::FORM_C)) {
|
||||
$info->errors |= self::ERROR_INVALID_ACE_LABEL;
|
||||
}
|
||||
|
||||
$codePoints = self::utf8Decode($label);
|
||||
|
||||
if ($options['CheckHyphens']) {
|
||||
// Step 2. If CheckHyphens, the label must not contain a U+002D HYPHEN-MINUS character
|
||||
// in both the thrid and fourth positions.
|
||||
if (isset($codePoints[2], $codePoints[3]) && 0x002D === $codePoints[2] && 0x002D === $codePoints[3]) {
|
||||
$info->errors |= self::ERROR_HYPHEN_3_4;
|
||||
}
|
||||
|
||||
// Step 3. If CheckHyphens, the label must neither begin nor end with a U+002D
|
||||
// HYPHEN-MINUS character.
|
||||
if ('-' === substr($label, 0, 1)) {
|
||||
$info->errors |= self::ERROR_LEADING_HYPHEN;
|
||||
}
|
||||
|
||||
if ('-' === substr($label, -1, 1)) {
|
||||
$info->errors |= self::ERROR_TRAILING_HYPHEN;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4. The label must not contain a U+002E (.) FULL STOP.
|
||||
if (false !== strpos($label, '.')) {
|
||||
$info->errors |= self::ERROR_LABEL_HAS_DOT;
|
||||
}
|
||||
|
||||
// Step 5. The label must not begin with a combining mark, that is: General_Category=Mark.
|
||||
if (1 === preg_match(Regex::COMBINING_MARK, $label)) {
|
||||
$info->errors |= self::ERROR_LEADING_COMBINING_MARK;
|
||||
}
|
||||
|
||||
// Step 6. Each code point in the label must only have certain status values according to
|
||||
// Section 5, IDNA Mapping Table:
|
||||
$transitional = $options['Transitional_Processing'];
|
||||
$useSTD3ASCIIRules = $options['UseSTD3ASCIIRules'];
|
||||
|
||||
foreach ($codePoints as $codePoint) {
|
||||
$data = self::lookupCodePointStatus($codePoint, $useSTD3ASCIIRules);
|
||||
$status = $data['status'];
|
||||
|
||||
if ('valid' === $status || (!$transitional && 'deviation' === $status)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$info->errors |= self::ERROR_DISALLOWED;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Step 7. If CheckJoiners, the label must satisify the ContextJ rules from Appendix A, in
|
||||
// The Unicode Code Points and Internationalized Domain Names for Applications (IDNA)
|
||||
// [IDNA2008].
|
||||
if ($options['CheckJoiners'] && !self::isValidContextJ($codePoints, $label)) {
|
||||
$info->errors |= self::ERROR_CONTEXTJ;
|
||||
}
|
||||
|
||||
// Step 8. If CheckBidi, and if the domain name is a Bidi domain name, then the label must
|
||||
// satisfy all six of the numbered conditions in [IDNA2008] RFC 5893, Section 2.
|
||||
if ($options['CheckBidi'] && (!$info->bidiDomain || $info->validBidiDomain)) {
|
||||
self::validateBidiLabel($label, $info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc3492#section-6.2
|
||||
*
|
||||
* @param string $input
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function punycodeDecode($input)
|
||||
{
|
||||
$n = self::INITIAL_N;
|
||||
$out = 0;
|
||||
$i = 0;
|
||||
$bias = self::INITIAL_BIAS;
|
||||
$lastDelimIndex = strrpos($input, self::DELIMITER);
|
||||
$b = false === $lastDelimIndex ? 0 : $lastDelimIndex;
|
||||
$inputLength = \strlen($input);
|
||||
$output = [];
|
||||
$bytes = array_map('ord', str_split($input));
|
||||
|
||||
for ($j = 0; $j < $b; ++$j) {
|
||||
if ($bytes[$j] > 0x7F) {
|
||||
throw new Exception('Invalid input');
|
||||
}
|
||||
|
||||
$output[$out++] = $input[$j];
|
||||
}
|
||||
|
||||
if ($b > 0) {
|
||||
++$b;
|
||||
}
|
||||
|
||||
for ($in = $b; $in < $inputLength; ++$out) {
|
||||
$oldi = $i;
|
||||
$w = 1;
|
||||
|
||||
for ($k = self::BASE; /* no condition */; $k += self::BASE) {
|
||||
if ($in >= $inputLength) {
|
||||
throw new Exception('Invalid input');
|
||||
}
|
||||
|
||||
$digit = self::$basicToDigit[$bytes[$in++] & 0xFF];
|
||||
|
||||
if ($digit < 0) {
|
||||
throw new Exception('Invalid input');
|
||||
}
|
||||
|
||||
if ($digit > intdiv(self::MAX_INT - $i, $w)) {
|
||||
throw new Exception('Integer overflow');
|
||||
}
|
||||
|
||||
$i += $digit * $w;
|
||||
|
||||
if ($k <= $bias) {
|
||||
$t = self::TMIN;
|
||||
} elseif ($k >= $bias + self::TMAX) {
|
||||
$t = self::TMAX;
|
||||
} else {
|
||||
$t = $k - $bias;
|
||||
}
|
||||
|
||||
if ($digit < $t) {
|
||||
break;
|
||||
}
|
||||
|
||||
$baseMinusT = self::BASE - $t;
|
||||
|
||||
if ($w > intdiv(self::MAX_INT, $baseMinusT)) {
|
||||
throw new Exception('Integer overflow');
|
||||
}
|
||||
|
||||
$w *= $baseMinusT;
|
||||
}
|
||||
|
||||
$outPlusOne = $out + 1;
|
||||
$bias = self::adaptBias($i - $oldi, $outPlusOne, 0 === $oldi);
|
||||
|
||||
if (intdiv($i, $outPlusOne) > self::MAX_INT - $n) {
|
||||
throw new Exception('Integer overflow');
|
||||
}
|
||||
|
||||
$n += intdiv($i, $outPlusOne);
|
||||
$i %= $outPlusOne;
|
||||
array_splice($output, $i++, 0, [mb_chr($n, 'utf-8')]);
|
||||
}
|
||||
|
||||
return implode('', $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc3492#section-6.3
|
||||
*
|
||||
* @param string $input
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function punycodeEncode($input)
|
||||
{
|
||||
$n = self::INITIAL_N;
|
||||
$delta = 0;
|
||||
$out = 0;
|
||||
$bias = self::INITIAL_BIAS;
|
||||
$inputLength = 0;
|
||||
$output = '';
|
||||
$iter = self::utf8Decode($input);
|
||||
|
||||
foreach ($iter as $codePoint) {
|
||||
++$inputLength;
|
||||
|
||||
if ($codePoint < 0x80) {
|
||||
$output .= \chr($codePoint);
|
||||
++$out;
|
||||
}
|
||||
}
|
||||
|
||||
$h = $out;
|
||||
$b = $out;
|
||||
|
||||
if ($b > 0) {
|
||||
$output .= self::DELIMITER;
|
||||
++$out;
|
||||
}
|
||||
|
||||
while ($h < $inputLength) {
|
||||
$m = self::MAX_INT;
|
||||
|
||||
foreach ($iter as $codePoint) {
|
||||
if ($codePoint >= $n && $codePoint < $m) {
|
||||
$m = $codePoint;
|
||||
}
|
||||
}
|
||||
|
||||
if ($m - $n > intdiv(self::MAX_INT - $delta, $h + 1)) {
|
||||
throw new Exception('Integer overflow');
|
||||
}
|
||||
|
||||
$delta += ($m - $n) * ($h + 1);
|
||||
$n = $m;
|
||||
|
||||
foreach ($iter as $codePoint) {
|
||||
if ($codePoint < $n && 0 === ++$delta) {
|
||||
throw new Exception('Integer overflow');
|
||||
}
|
||||
|
||||
if ($codePoint === $n) {
|
||||
$q = $delta;
|
||||
|
||||
for ($k = self::BASE; /* no condition */; $k += self::BASE) {
|
||||
if ($k <= $bias) {
|
||||
$t = self::TMIN;
|
||||
} elseif ($k >= $bias + self::TMAX) {
|
||||
$t = self::TMAX;
|
||||
} else {
|
||||
$t = $k - $bias;
|
||||
}
|
||||
|
||||
if ($q < $t) {
|
||||
break;
|
||||
}
|
||||
|
||||
$qMinusT = $q - $t;
|
||||
$baseMinusT = self::BASE - $t;
|
||||
$output .= self::encodeDigit($t + ($qMinusT) % ($baseMinusT), false);
|
||||
++$out;
|
||||
$q = intdiv($qMinusT, $baseMinusT);
|
||||
}
|
||||
|
||||
$output .= self::encodeDigit($q, false);
|
||||
++$out;
|
||||
$bias = self::adaptBias($delta, $h + 1, $h === $b);
|
||||
$delta = 0;
|
||||
++$h;
|
||||
}
|
||||
}
|
||||
|
||||
++$delta;
|
||||
++$n;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc3492#section-6.1
|
||||
*
|
||||
* @param int $delta
|
||||
* @param int $numPoints
|
||||
* @param bool $firstTime
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private static function adaptBias($delta, $numPoints, $firstTime)
|
||||
{
|
||||
// xxx >> 1 is a faster way of doing intdiv(xxx, 2)
|
||||
$delta = $firstTime ? intdiv($delta, self::DAMP) : $delta >> 1;
|
||||
$delta += intdiv($delta, $numPoints);
|
||||
$k = 0;
|
||||
|
||||
while ($delta > ((self::BASE - self::TMIN) * self::TMAX) >> 1) {
|
||||
$delta = intdiv($delta, self::BASE - self::TMIN);
|
||||
$k += self::BASE;
|
||||
}
|
||||
|
||||
return $k + intdiv((self::BASE - self::TMIN + 1) * $delta, $delta + self::SKEW);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $d
|
||||
* @param bool $flag
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function encodeDigit($d, $flag)
|
||||
{
|
||||
return \chr($d + 22 + 75 * ($d < 26 ? 1 : 0) - (($flag ? 1 : 0) << 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a UTF-8 encoded string and converts it into a series of integer code points. Any
|
||||
* invalid byte sequences will be replaced by a U+FFFD replacement code point.
|
||||
*
|
||||
* @see https://encoding.spec.whatwg.org/#utf-8-decoder
|
||||
*
|
||||
* @param string $input
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private static function utf8Decode($input)
|
||||
{
|
||||
$bytesSeen = 0;
|
||||
$bytesNeeded = 0;
|
||||
$lowerBoundary = 0x80;
|
||||
$upperBoundary = 0xBF;
|
||||
$codePoint = 0;
|
||||
$codePoints = [];
|
||||
$length = \strlen($input);
|
||||
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$byte = \ord($input[$i]);
|
||||
|
||||
if (0 === $bytesNeeded) {
|
||||
if ($byte >= 0x00 && $byte <= 0x7F) {
|
||||
$codePoints[] = $byte;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($byte >= 0xC2 && $byte <= 0xDF) {
|
||||
$bytesNeeded = 1;
|
||||
$codePoint = $byte & 0x1F;
|
||||
} elseif ($byte >= 0xE0 && $byte <= 0xEF) {
|
||||
if (0xE0 === $byte) {
|
||||
$lowerBoundary = 0xA0;
|
||||
} elseif (0xED === $byte) {
|
||||
$upperBoundary = 0x9F;
|
||||
}
|
||||
|
||||
$bytesNeeded = 2;
|
||||
$codePoint = $byte & 0xF;
|
||||
} elseif ($byte >= 0xF0 && $byte <= 0xF4) {
|
||||
if (0xF0 === $byte) {
|
||||
$lowerBoundary = 0x90;
|
||||
} elseif (0xF4 === $byte) {
|
||||
$upperBoundary = 0x8F;
|
||||
}
|
||||
|
||||
$bytesNeeded = 3;
|
||||
$codePoint = $byte & 0x7;
|
||||
} else {
|
||||
$codePoints[] = 0xFFFD;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($byte < $lowerBoundary || $byte > $upperBoundary) {
|
||||
$codePoint = 0;
|
||||
$bytesNeeded = 0;
|
||||
$bytesSeen = 0;
|
||||
$lowerBoundary = 0x80;
|
||||
$upperBoundary = 0xBF;
|
||||
--$i;
|
||||
$codePoints[] = 0xFFFD;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$lowerBoundary = 0x80;
|
||||
$upperBoundary = 0xBF;
|
||||
$codePoint = ($codePoint << 6) | ($byte & 0x3F);
|
||||
|
||||
if (++$bytesSeen !== $bytesNeeded) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$codePoints[] = $codePoint;
|
||||
$codePoint = 0;
|
||||
$bytesNeeded = 0;
|
||||
$bytesSeen = 0;
|
||||
}
|
||||
|
||||
// String unexpectedly ended, so append a U+FFFD code point.
|
||||
if (0 !== $bytesNeeded) {
|
||||
$codePoints[] = 0xFFFD;
|
||||
}
|
||||
|
||||
return $codePoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $codePoint
|
||||
* @param bool $useSTD3ASCIIRules
|
||||
*
|
||||
* @return array{status: string, mapping?: string}
|
||||
*/
|
||||
private static function lookupCodePointStatus($codePoint, $useSTD3ASCIIRules)
|
||||
{
|
||||
if (!self::$mappingTableLoaded) {
|
||||
self::$mappingTableLoaded = true;
|
||||
self::$mapped = require __DIR__.'/Resources/unidata/mapped.php';
|
||||
self::$ignored = require __DIR__.'/Resources/unidata/ignored.php';
|
||||
self::$deviation = require __DIR__.'/Resources/unidata/deviation.php';
|
||||
self::$disallowed = require __DIR__.'/Resources/unidata/disallowed.php';
|
||||
self::$disallowed_STD3_mapped = require __DIR__.'/Resources/unidata/disallowed_STD3_mapped.php';
|
||||
self::$disallowed_STD3_valid = require __DIR__.'/Resources/unidata/disallowed_STD3_valid.php';
|
||||
}
|
||||
|
||||
if (isset(self::$mapped[$codePoint])) {
|
||||
return ['status' => 'mapped', 'mapping' => self::$mapped[$codePoint]];
|
||||
}
|
||||
|
||||
if (isset(self::$ignored[$codePoint])) {
|
||||
return ['status' => 'ignored'];
|
||||
}
|
||||
|
||||
if (isset(self::$deviation[$codePoint])) {
|
||||
return ['status' => 'deviation', 'mapping' => self::$deviation[$codePoint]];
|
||||
}
|
||||
|
||||
if (isset(self::$disallowed[$codePoint]) || DisallowedRanges::inRange($codePoint)) {
|
||||
return ['status' => 'disallowed'];
|
||||
}
|
||||
|
||||
$isDisallowedMapped = isset(self::$disallowed_STD3_mapped[$codePoint]);
|
||||
|
||||
if ($isDisallowedMapped || isset(self::$disallowed_STD3_valid[$codePoint])) {
|
||||
$status = 'disallowed';
|
||||
|
||||
if (!$useSTD3ASCIIRules) {
|
||||
$status = $isDisallowedMapped ? 'mapped' : 'valid';
|
||||
}
|
||||
|
||||
if ($isDisallowedMapped) {
|
||||
return ['status' => $status, 'mapping' => self::$disallowed_STD3_mapped[$codePoint]];
|
||||
}
|
||||
|
||||
return ['status' => $status];
|
||||
}
|
||||
|
||||
return ['status' => 'valid'];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
<?php
|
||||
|
||||
namespace WpOrg\Requests;
|
||||
|
||||
use WpOrg\Requests\Exception;
|
||||
use WpOrg\Requests\Exception\InvalidArgument;
|
||||
use WpOrg\Requests\Utility\InputValidator;
|
||||
|
||||
/**
|
||||
* IDNA URL encoder
|
||||
*
|
||||
* Note: Not fully compliant, as nameprep does nothing yet.
|
||||
*
|
||||
* @package Requests\Utilities
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3490 IDNA specification
|
||||
* @link https://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification
|
||||
*/
|
||||
class IdnaEncoder {
|
||||
/**
|
||||
* ACE prefix used for IDNA
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3490#section-5
|
||||
* @var string
|
||||
*/
|
||||
const ACE_PREFIX = 'xn--';
|
||||
|
||||
/**
|
||||
* Maximum length of a IDNA URL in ASCII.
|
||||
*
|
||||
* @see \WpOrg\Requests\IdnaEncoder::to_ascii()
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAX_LENGTH = 64;
|
||||
|
||||
/**#@+
|
||||
* Bootstrap constant for Punycode
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3492#section-5
|
||||
* @var int
|
||||
*/
|
||||
const BOOTSTRAP_BASE = 36;
|
||||
const BOOTSTRAP_TMIN = 1;
|
||||
const BOOTSTRAP_TMAX = 26;
|
||||
const BOOTSTRAP_SKEW = 38;
|
||||
const BOOTSTRAP_DAMP = 700;
|
||||
const BOOTSTRAP_INITIAL_BIAS = 72;
|
||||
const BOOTSTRAP_INITIAL_N = 128;
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Encode a hostname using Punycode
|
||||
*
|
||||
* @param string|Stringable $hostname Hostname
|
||||
* @return string Punycode-encoded hostname
|
||||
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
|
||||
*/
|
||||
public static function encode($hostname) {
|
||||
if (InputValidator::is_string_or_stringable($hostname) === false) {
|
||||
throw InvalidArgument::create(1, '$hostname', 'string|Stringable', gettype($hostname));
|
||||
}
|
||||
|
||||
$parts = explode('.', $hostname);
|
||||
foreach ($parts as &$part) {
|
||||
$part = self::to_ascii($part);
|
||||
}
|
||||
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a UTF-8 text string to an ASCII string using Punycode
|
||||
*
|
||||
* @param string $text ASCII or UTF-8 string (max length 64 characters)
|
||||
* @return string ASCII string
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`)
|
||||
* @throws \WpOrg\Requests\Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`)
|
||||
* @throws \WpOrg\Requests\Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`)
|
||||
* @throws \WpOrg\Requests\Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`)
|
||||
*/
|
||||
public static function to_ascii($text) {
|
||||
// Step 1: Check if the text is already ASCII
|
||||
if (self::is_ascii($text)) {
|
||||
// Skip to step 7
|
||||
if (strlen($text) < self::MAX_LENGTH) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
throw new Exception('Provided string is too long', 'idna.provided_too_long', $text);
|
||||
}
|
||||
|
||||
// Step 2: nameprep
|
||||
$text = self::nameprep($text);
|
||||
|
||||
// Step 3: UseSTD3ASCIIRules is false, continue
|
||||
// Step 4: Check if it's ASCII now
|
||||
if (self::is_ascii($text)) {
|
||||
// Skip to step 7
|
||||
/*
|
||||
* As the `nameprep()` method returns the original string, this code will never be reached until
|
||||
* that method is properly implemented.
|
||||
*/
|
||||
// @codeCoverageIgnoreStart
|
||||
if (strlen($text) < self::MAX_LENGTH) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
throw new Exception('Prepared string is too long', 'idna.prepared_too_long', $text);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// Step 5: Check ACE prefix
|
||||
if (strpos($text, self::ACE_PREFIX) === 0) {
|
||||
throw new Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $text);
|
||||
}
|
||||
|
||||
// Step 6: Encode with Punycode
|
||||
$text = self::punycode_encode($text);
|
||||
|
||||
// Step 7: Prepend ACE prefix
|
||||
$text = self::ACE_PREFIX . $text;
|
||||
|
||||
// Step 8: Check size
|
||||
if (strlen($text) < self::MAX_LENGTH) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
throw new Exception('Encoded string is too long', 'idna.encoded_too_long', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given text string contains only ASCII characters
|
||||
*
|
||||
* @internal (Testing found regex was the fastest implementation)
|
||||
*
|
||||
* @param string $text
|
||||
* @return bool Is the text string ASCII-only?
|
||||
*/
|
||||
protected static function is_ascii($text) {
|
||||
return (preg_match('/(?:[^\x00-\x7F])/', $text) !== 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a text string for use as an IDNA name
|
||||
*
|
||||
* @todo Implement this based on RFC 3491 and the newer 5891
|
||||
* @param string $text
|
||||
* @return string Prepared string
|
||||
*/
|
||||
protected static function nameprep($text) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a UTF-8 string to a UCS-4 codepoint array
|
||||
*
|
||||
* Based on \WpOrg\Requests\Iri::replace_invalid_with_pct_encoding()
|
||||
*
|
||||
* @param string $input
|
||||
* @return array Unicode code points
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`)
|
||||
*/
|
||||
protected static function utf8_to_codepoints($input) {
|
||||
$codepoints = [];
|
||||
|
||||
// Get number of bytes
|
||||
$strlen = strlen($input);
|
||||
|
||||
// phpcs:ignore Generic.CodeAnalysis.JumbledIncrementer -- This is a deliberate choice.
|
||||
for ($position = 0; $position < $strlen; $position++) {
|
||||
$value = ord($input[$position]);
|
||||
|
||||
if ((~$value & 0x80) === 0x80) { // One byte sequence:
|
||||
$character = $value;
|
||||
$length = 1;
|
||||
$remaining = 0;
|
||||
} elseif (($value & 0xE0) === 0xC0) { // Two byte sequence:
|
||||
$character = ($value & 0x1F) << 6;
|
||||
$length = 2;
|
||||
$remaining = 1;
|
||||
} elseif (($value & 0xF0) === 0xE0) { // Three byte sequence:
|
||||
$character = ($value & 0x0F) << 12;
|
||||
$length = 3;
|
||||
$remaining = 2;
|
||||
} elseif (($value & 0xF8) === 0xF0) { // Four byte sequence:
|
||||
$character = ($value & 0x07) << 18;
|
||||
$length = 4;
|
||||
$remaining = 3;
|
||||
} else { // Invalid byte:
|
||||
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value);
|
||||
}
|
||||
|
||||
if ($remaining > 0) {
|
||||
if ($position + $length > $strlen) {
|
||||
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||
}
|
||||
|
||||
for ($position++; $remaining > 0; $position++) {
|
||||
$value = ord($input[$position]);
|
||||
|
||||
// If it is invalid, count the sequence as invalid and reprocess the current byte:
|
||||
if (($value & 0xC0) !== 0x80) {
|
||||
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||
}
|
||||
|
||||
--$remaining;
|
||||
$character |= ($value & 0x3F) << ($remaining * 6);
|
||||
}
|
||||
|
||||
$position--;
|
||||
}
|
||||
|
||||
if (// Non-shortest form sequences are invalid
|
||||
$length > 1 && $character <= 0x7F
|
||||
|| $length > 2 && $character <= 0x7FF
|
||||
|| $length > 3 && $character <= 0xFFFF
|
||||
// Outside of range of ucschar codepoints
|
||||
// Noncharacters
|
||||
|| ($character & 0xFFFE) === 0xFFFE
|
||||
|| $character >= 0xFDD0 && $character <= 0xFDEF
|
||||
|| (
|
||||
// Everything else not in ucschar
|
||||
$character > 0xD7FF && $character < 0xF900
|
||||
|| $character < 0x20
|
||||
|| $character > 0x7E && $character < 0xA0
|
||||
|| $character > 0xEFFFD
|
||||
)
|
||||
) {
|
||||
throw new Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
|
||||
}
|
||||
|
||||
$codepoints[] = $character;
|
||||
}
|
||||
|
||||
return $codepoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC3492-compliant encoder
|
||||
*
|
||||
* @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code
|
||||
*
|
||||
* @param string $input UTF-8 encoded string to encode
|
||||
* @return string Punycode-encoded string
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
|
||||
*/
|
||||
public static function punycode_encode($input) {
|
||||
$output = '';
|
||||
// let n = initial_n
|
||||
$n = self::BOOTSTRAP_INITIAL_N;
|
||||
// let delta = 0
|
||||
$delta = 0;
|
||||
// let bias = initial_bias
|
||||
$bias = self::BOOTSTRAP_INITIAL_BIAS;
|
||||
// let h = b = the number of basic code points in the input
|
||||
$h = 0;
|
||||
$b = 0; // see loop
|
||||
// copy them to the output in order
|
||||
$codepoints = self::utf8_to_codepoints($input);
|
||||
$extended = [];
|
||||
|
||||
foreach ($codepoints as $char) {
|
||||
if ($char < 128) {
|
||||
// Character is valid ASCII
|
||||
// TODO: this should also check if it's valid for a URL
|
||||
$output .= chr($char);
|
||||
$h++;
|
||||
|
||||
// Check if the character is non-ASCII, but below initial n
|
||||
// This never occurs for Punycode, so ignore in coverage
|
||||
// @codeCoverageIgnoreStart
|
||||
} elseif ($char < $n) {
|
||||
throw new Exception('Invalid character', 'idna.character_outside_domain', $char);
|
||||
// @codeCoverageIgnoreEnd
|
||||
} else {
|
||||
$extended[$char] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$extended = array_keys($extended);
|
||||
sort($extended);
|
||||
$b = $h;
|
||||
// [copy them] followed by a delimiter if b > 0
|
||||
if (strlen($output) > 0) {
|
||||
$output .= '-';
|
||||
}
|
||||
|
||||
// {if the input contains a non-basic code point < n then fail}
|
||||
// while h < length(input) do begin
|
||||
$codepointcount = count($codepoints);
|
||||
while ($h < $codepointcount) {
|
||||
// let m = the minimum code point >= n in the input
|
||||
$m = array_shift($extended);
|
||||
//printf('next code point to insert is %s' . PHP_EOL, dechex($m));
|
||||
// let delta = delta + (m - n) * (h + 1), fail on overflow
|
||||
$delta += ($m - $n) * ($h + 1);
|
||||
// let n = m
|
||||
$n = $m;
|
||||
// for each code point c in the input (in order) do begin
|
||||
for ($num = 0; $num < $codepointcount; $num++) {
|
||||
$c = $codepoints[$num];
|
||||
// if c < n then increment delta, fail on overflow
|
||||
if ($c < $n) {
|
||||
$delta++;
|
||||
} elseif ($c === $n) { // if c == n then begin
|
||||
// let q = delta
|
||||
$q = $delta;
|
||||
// for k = base to infinity in steps of base do begin
|
||||
for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) {
|
||||
// let t = tmin if k <= bias {+ tmin}, or
|
||||
// tmax if k >= bias + tmax, or k - bias otherwise
|
||||
if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
|
||||
$t = self::BOOTSTRAP_TMIN;
|
||||
} elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
|
||||
$t = self::BOOTSTRAP_TMAX;
|
||||
} else {
|
||||
$t = $k - $bias;
|
||||
}
|
||||
|
||||
// if q < t then break
|
||||
if ($q < $t) {
|
||||
break;
|
||||
}
|
||||
|
||||
// output the code point for digit t + ((q - t) mod (base - t))
|
||||
$digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t));
|
||||
$output .= self::digit_to_char($digit);
|
||||
// let q = (q - t) div (base - t)
|
||||
$q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t));
|
||||
} // end
|
||||
// output the code point for digit q
|
||||
$output .= self::digit_to_char($q);
|
||||
// let bias = adapt(delta, h + 1, test h equals b?)
|
||||
$bias = self::adapt($delta, $h + 1, $h === $b);
|
||||
// let delta = 0
|
||||
$delta = 0;
|
||||
// increment h
|
||||
$h++;
|
||||
} // end
|
||||
} // end
|
||||
// increment delta and n
|
||||
$delta++;
|
||||
$n++;
|
||||
} // end
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a digit to its respective character
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3492#section-5
|
||||
*
|
||||
* @param int $digit Digit in the range 0-35
|
||||
* @return string Single character corresponding to digit
|
||||
*
|
||||
* @throws \WpOrg\Requests\Exception On invalid digit (`idna.invalid_digit`)
|
||||
*/
|
||||
protected static function digit_to_char($digit) {
|
||||
// @codeCoverageIgnoreStart
|
||||
// As far as I know, this never happens, but still good to be sure.
|
||||
if ($digit < 0 || $digit > 35) {
|
||||
throw new Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
$digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
return substr($digits, $digit, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the bias
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc3492#section-6.1
|
||||
* @param int $delta
|
||||
* @param int $numpoints
|
||||
* @param bool $firsttime
|
||||
* @return int New bias
|
||||
*
|
||||
* function adapt(delta,numpoints,firsttime):
|
||||
*/
|
||||
protected static function adapt($delta, $numpoints, $firsttime) {
|
||||
// if firsttime then let delta = delta div damp
|
||||
if ($firsttime) {
|
||||
$delta = floor($delta / self::BOOTSTRAP_DAMP);
|
||||
} else {
|
||||
// else let delta = delta div 2
|
||||
$delta = floor($delta / 2);
|
||||
}
|
||||
|
||||
// let delta = delta + (delta div numpoints)
|
||||
$delta += floor($delta / $numpoints);
|
||||
// let k = 0
|
||||
$k = 0;
|
||||
// while delta > ((base - tmin) * tmax) div 2 do begin
|
||||
$max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2);
|
||||
while ($delta > $max) {
|
||||
// let delta = delta div (base - tmin)
|
||||
$delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN));
|
||||
// let k = k + base
|
||||
$k += self::BOOTSTRAP_BASE;
|
||||
} // end
|
||||
// return k + (((base - tmin + 1) * delta) div (delta + skew))
|
||||
return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com> and Trevor Rowbotham <trevor.rowbotham@pm.me>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Polyfill\Intl\Idn;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class Info
|
||||
{
|
||||
public $bidiDomain = false;
|
||||
public $errors = 0;
|
||||
public $validBidiDomain = true;
|
||||
public $transitionalDifferent = false;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue