同步SDK文件夹,本地数据库mmb文件
This commit is contained in:
@@ -0,0 +1,500 @@
|
||||
<?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
|
||||
{
|
||||
public const VERSION = '0.2.0';
|
||||
|
||||
private readonly ?string $caBundle;
|
||||
private readonly ?float $connectTimeout;
|
||||
private readonly string $host;
|
||||
private readonly bool $useHttps;
|
||||
private readonly RequestFactory $httpRequestFactory;
|
||||
private readonly string $licenseKey;
|
||||
private readonly ?string $proxy;
|
||||
private readonly ?float $timeout;
|
||||
private readonly string $userAgentPrefix;
|
||||
private readonly int $accountId;
|
||||
|
||||
/**
|
||||
* @param int $accountId your MaxMind account ID
|
||||
* @param string $licenseKey your MaxMind license key
|
||||
* @param array<string, mixed> $options an array of options. Possible keys:
|
||||
* * `host` - The host to use when connecting to the web service.
|
||||
* * `useHttps` - Set to false to disable HTTPS.
|
||||
* * `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 = $options['httpRequestFactory'] ?? new RequestFactory();
|
||||
$this->host = $options['host'] ?? 'api.maxmind.com';
|
||||
$this->useHttps = $options['useHttps'] ?? true;
|
||||
$this->userAgentPrefix = isset($options['userAgent']) ? $options['userAgent'] . ' ' : '';
|
||||
$this->caBundle = $options['caBundle'] ?? $this->getCaBundle();
|
||||
$this->connectTimeout = $options['connectTimeout'] ?? null;
|
||||
$this->timeout = $options['timeout'] ?? null;
|
||||
$this->proxy = $options['proxy'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $service name of the service querying
|
||||
* @param string $path the URI path to use
|
||||
* @param array<mixed> $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<mixed>|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']
|
||||
);
|
||||
|
||||
[$statusCode, $contentType, $responseBody] = $request->post($requestBody);
|
||||
|
||||
return $this->handleResponse(
|
||||
$statusCode,
|
||||
$contentType,
|
||||
$responseBody,
|
||||
$service,
|
||||
$path
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>|null
|
||||
*/
|
||||
public function get(string $service, string $path): ?array
|
||||
{
|
||||
$request = $this->createRequest(
|
||||
$path
|
||||
);
|
||||
|
||||
[$statusCode, $contentType, $responseBody] = $request->get();
|
||||
|
||||
return $this->handleResponse(
|
||||
$statusCode,
|
||||
$contentType,
|
||||
$responseBody,
|
||||
$service,
|
||||
$path
|
||||
);
|
||||
}
|
||||
|
||||
private function userAgent(): string
|
||||
{
|
||||
$curlVersion = curl_version();
|
||||
if ($curlVersion === false) {
|
||||
throw new \RuntimeException('curl_version() returned false');
|
||||
}
|
||||
|
||||
return $this->userAgentPrefix . 'MaxMind-WS-API/' . self::VERSION . ' PHP/' . \PHP_VERSION
|
||||
. ' curl/' . $curlVersion['version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $headers
|
||||
*/
|
||||
private function createRequest(string $path, array $headers = []): Http\Request
|
||||
{
|
||||
$headers = [
|
||||
...$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<mixed>|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 ($this->useHttps ? 'https://' : 'http://') . $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 || !str_contains($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<mixed>|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(): ?string
|
||||
{
|
||||
$curlVersion = curl_version();
|
||||
if ($curlVersion === false) {
|
||||
throw new \RuntimeException('curl_version() returned false');
|
||||
}
|
||||
|
||||
// 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 (str_starts_with($cert, '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,146 @@
|
||||
<?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
|
||||
{
|
||||
private readonly \CurlHandle $ch;
|
||||
private readonly string $url;
|
||||
|
||||
/**
|
||||
* @var array{
|
||||
* caBundle?: string,
|
||||
* connectTimeout: float|int,
|
||||
* curlHandle: \CurlHandle,
|
||||
* headers: array<int, string>,
|
||||
* proxy: string|null,
|
||||
* timeout: float|int,
|
||||
* userAgent: string
|
||||
* }
|
||||
*/
|
||||
private readonly array $options;
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* caBundle?: string,
|
||||
* connectTimeout: float|int,
|
||||
* curlHandle: \CurlHandle,
|
||||
* headers: array<int, string>,
|
||||
* proxy: string|null,
|
||||
* timeout: float|int,
|
||||
* userAgent: string
|
||||
* } $options
|
||||
*/
|
||||
public function __construct(string $url, array $options)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->options = $options;
|
||||
$this->ch = $options['curlHandle'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HttpException
|
||||
*
|
||||
* @return array{0:int, 1:string|null, 2:string|null}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0:int, 1:string|null, 2:string|null}
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
$curl = $this->createCurl();
|
||||
|
||||
curl_setopt($curl, \CURLOPT_HTTPGET, true);
|
||||
|
||||
return $this->execute($curl);
|
||||
}
|
||||
|
||||
private function createCurl(): \CurlHandle
|
||||
{
|
||||
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'];
|
||||
if ($this->options['proxy'] !== null) {
|
||||
$opts[\CURLOPT_PROXY] = $this->options['proxy'];
|
||||
}
|
||||
|
||||
$connectTimeout = $this->options['connectTimeout'];
|
||||
$opts[\CURLOPT_CONNECTTIMEOUT_MS] = (int) ceil($connectTimeout * 1000);
|
||||
|
||||
$timeout = $this->options['timeout'];
|
||||
$opts[\CURLOPT_TIMEOUT_MS] = (int) ceil($timeout * 1000);
|
||||
|
||||
// @phpstan-ignore argument.type (PHPStan's curl stubs require non-empty-string for URL/userAgent)
|
||||
curl_setopt_array($this->ch, $opts);
|
||||
|
||||
return $this->ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HttpException
|
||||
*
|
||||
* @return array{0:int, 1:string|null, 2:string|null}
|
||||
*/
|
||||
private function execute(\CurlHandle $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,
|
||||
// curl_exec returns false on failure, but we've already checked
|
||||
// for errors above and thrown an exception. Cast to string to
|
||||
// satisfy PHPStan since curl_exec technically returns string|bool.
|
||||
$body === false ? null : (string) $body,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MaxMind\WebService\Http;
|
||||
|
||||
/**
|
||||
* Interface Request.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface Request
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function __construct(string $url, array $options);
|
||||
|
||||
/**
|
||||
* @return array{0:int, 1:string|null, 2:string|null}
|
||||
*/
|
||||
public function post(string $body): array;
|
||||
|
||||
/**
|
||||
* @return array{0:int, 1:string|null, 2:string|null}
|
||||
*/
|
||||
public function get(): array;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MaxMind\WebService\Http;
|
||||
|
||||
/**
|
||||
* Class RequestFactory.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RequestFactory
|
||||
{
|
||||
/**
|
||||
* Keep the cURL resource here, so that if there are multiple API requests
|
||||
* done the connection is kept alive, SSL resumption can be used
|
||||
* etcetera.
|
||||
*/
|
||||
private ?\CurlHandle $ch = null;
|
||||
|
||||
private function getCurlHandle(): \CurlHandle
|
||||
{
|
||||
if ($this->ch === null) {
|
||||
$ch = curl_init();
|
||||
if ($ch === false) {
|
||||
throw new \RuntimeException('Unable to initialize cURL handle');
|
||||
}
|
||||
$this->ch = $ch;
|
||||
}
|
||||
|
||||
return $this->ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function request(string $url, array $options): Request
|
||||
{
|
||||
$options['curlHandle'] = $this->getCurlHandle();
|
||||
|
||||
// @phpstan-ignore argument.type (options array is built dynamically by Client)
|
||||
return new CurlRequest($url, $options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user