update v 1.0.7.5

This commit is contained in:
Sujit Prasad
2016-06-13 20:41:55 +05:30
parent aa9786d829
commit 283d97e3ea
5078 changed files with 339851 additions and 175995 deletions

View File

@@ -0,0 +1,10 @@
<?php
namespace MaxMind\Exception;
/**
* This class represents an error authenticating
*/
class AuthenticationException extends InvalidRequestException
{
}

View File

@@ -0,0 +1,40 @@
<?php
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(
$message,
$httpStatus,
$uri,
\Exception $previous = null
) {
$this->uri = $uri;
parent::__construct($message, $httpStatus, $previous);
}
public function getUri()
{
return $this->uri;
}
public function getStatusCode()
{
return $this->getCode();
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace MaxMind\Exception;
/**
* Thrown when the account is out of credits.
*/
class InsufficientFundsException extends InvalidRequestException
{
}

View File

@@ -0,0 +1,12 @@
<?php
namespace MaxMind\Exception;
/**
* This class represents an error in creating the request to be sent to the
* web service. For example, if the array cannot be encoded as JSON or if there
* is a missing or invalid field.
*/
class InvalidInputException extends WebServiceException
{
}

View File

@@ -0,0 +1,37 @@
<?php
namespace MaxMind\Exception;
/**
* Thrown when a MaxMind web service returns an error relating to the request.
*/
class InvalidRequestException extends HttpException
{
/**
* The code returned by the MaxMind web service
*/
private $error;
/**
* @param string $message The exception message
* @param int $error The error code returned by the MaxMind web service
* @param int $httpStatus The HTTP status code of the response
* @param string $uri The URI queries
* @param \Exception $previous The previous exception, if any.
*/
public function __construct(
$message,
$error,
$httpStatus,
$uri,
\Exception $previous = null
) {
$this->error = $error;
parent::__construct($message, $httpStatus, $uri, $previous);
}
public function getErrorCode()
{
return $this->error;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace MaxMind\Exception;
class IpAddressNotFoundException extends InvalidRequestException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace MaxMind\Exception;
/**
* This exception is thrown when the service requires permission to access.
*/
class PermissionRequiredException extends InvalidRequestException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace MaxMind\Exception;
/**
* This class represents a generic web service error.
*/
class WebServiceException extends \Exception
{
}

View File

@@ -0,0 +1,450 @@
<?php
namespace MaxMind\WebService;
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.
* @package MaxMind\WebService
* @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 $userId;
/**
* @param int $userId Your MaxMind user 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(
$userId,
$licenseKey,
$options = array()
) {
$this->userId = $userId;
$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
* @return array The decoded content of a successful response
* @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.
*/
public function post($service, $path, $input)
{
$body = json_encode($input);
if ($body === false) {
throw new InvalidInputException(
'Error encoding input as JSON: '
. $this->jsonErrorDescription()
);
}
$request = $this->createRequest(
$path,
array('Content-Type: application/json')
);
list($statusCode, $contentType, $body) = $request->post($body);
return $this->handleResponse(
$statusCode,
$contentType,
$body,
$service,
$path
);
}
public function get($service, $path)
{
$request = $this->createRequest($path);
list($statusCode, $contentType, $body) = $request->get();
return $this->handleResponse(
$statusCode,
$contentType,
$body,
$service,
$path
);
}
private function userAgent()
{
$curlVersion = curl_version();
return $this->userAgentPrefix . 'MaxMind-WS-API/' . Client::VERSION . ' PHP/' . PHP_VERSION .
' curl/' . $curlVersion['version'];
}
private function createRequest($path, $headers = array())
{
array_push(
$headers,
'Authorization: Basic '
. base64_encode($this->userId . ':' . $this->licenseKey),
'Accept: application/json'
);
return $this->httpRequestFactory->request(
$this->urlFor($path),
array(
'caBundle' => $this->caBundle,
'connectTimeout' => $this->connectTimeout,
'headers' => $headers,
'proxy' => $this->proxy,
'timeout' => $this->timeout,
'userAgent' => $this->userAgent(),
)
);
}
/**
* @param integer $statusCode the HTTP status code of the response
* @param string $contentType the Content-Type of the response
* @param string $body the response body
* @param string $service the name of the service
* @param string $path the path used in the request
* @return array The decoded content of a successful response
* @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
*/
private function handleResponse(
$statusCode,
$contentType,
$body,
$service,
$path
) {
if ($statusCode >= 400 && $statusCode <= 499) {
$this->handle4xx($statusCode, $contentType, $body, $service, $path);
} elseif ($statusCode >= 500) {
$this->handle5xx($statusCode, $service, $path);
} elseif ($statusCode != 200) {
$this->handleUnexpectedStatus($statusCode, $service, $path);
}
return $this->handleSuccess($body, $service);
}
/**
* @return string describing the JSON error
*/
private function jsonErrorDescription()
{
$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($path)
{
return 'https://' . $this->host . $path;
}
/**
* @param int $statusCode The HTTP status code
* @param string $contentType The response content-type
* @param string $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(
$statusCode,
$contentType,
$body,
$service,
$path
) {
if (strlen($body) === 0) {
throw new HttpException(
"Received a $statusCode error for $service with no body",
$statusCode,
$this->urlFor($path)
);
}
if (!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(
$message,
$code,
$statusCode,
$path
) {
switch ($code) {
case 'IP_ADDRESS_NOT_FOUND':
case 'IP_ADDRESS_RESERVED':
throw new IpAddressNotFoundException(
$message,
$code,
$statusCode,
$this->urlFor($path)
);
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($statusCode, $service, $path)
{
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($statusCode, $service, $path)
{
throw new HttpException(
'Received an unexpected HTTP status ' .
"($statusCode) for $service",
$statusCode,
$this->urlFor($path)
);
}
/**
* @param string $body The successful request body
* @param string $service The service name
* @return array The decoded request body
* @throws WebServiceException if the request body cannot be decoded as
* JSON
*/
private function handleSuccess($body, $service)
{
if (strlen($body) == 0) {
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()
{
$cert = __DIR__ . '/cacert.pem';
// Check if we are 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 ($newCart === 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;
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace MaxMind\WebService\Http;
use MaxMind\Exception\HttpException;
/**
* This class is for internal use only. Semantic versioning does not not apply.
* @package MaxMind\WebService\Http
* @internal
*/
class CurlRequest implements Request
{
private $url;
private $options;
/**
* @param $url
* @param $options
*/
public function __construct($url, $options)
{
$this->url = $url;
$this->options = $options;
}
/**
* @param $body
* @return array
*/
public function post($body)
{
$curl = $this->createCurl();
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
return $this->execute($curl);
}
public function get()
{
$curl = $this->createCurl();
curl_setopt($curl, CURLOPT_HTTPGET, true);
return $this->execute($curl);
}
/**
* @return resource
*/
private function createCurl()
{
$curl = curl_init($this->url);
$opts[CURLOPT_CAINFO] = $this->options['caBundle'];
$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($curl, $opts);
return $curl;
}
private function execute($curl)
{
$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);
curl_close($curl);
return array($statusCode, $contentType, $body);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace MaxMind\WebService\Http;
/**
* Interface Request
* @package MaxMind\WebService\Http
* @internal
*/
interface Request
{
/**
* @param $url
* @param $options
*/
public function __construct($url, $options);
/**
* @param $body
* @return mixed
*/
public function post($body);
/**
* @return mixed
*/
public function get();
}

View File

@@ -0,0 +1,25 @@
<?php
namespace MaxMind\WebService\Http;
/**
* Class RequestFactory
* @package MaxMind\WebService\Http
* @internal
*/
class RequestFactory
{
public function __construct()
{
}
/**
* @param $url
* @param $options
* @return CurlRequest
*/
public function request($url, $options)
{
return new CurlRequest($url, $options);
}
}

File diff suppressed because it is too large Load Diff