Compare commits

...

2 Commits

Author SHA1 Message Date
Clemens Schwaighofer
1653e6b684 Allow http_errors unset/set on each call
If set or not set, on each call this option can be set.
If set to null on call, the original value or default config value is used
2024-11-06 13:29:19 +09:00
Clemens Schwaighofer
c8bc0062ad URL Requests change error response
Instead of just throwing exception on 401 auth, throw exception for any
error code from 400 on
This can be turned off with the option "http_errors" set to false

Also updaed the exception content to match 400 or 500 error type with
more information attached

General Exception error codes:
Cnnn: Curl errors (FAILURE)
Rnnn: general class errors (ERROR)
Hnnn: http response errors (ERROR)
2024-11-06 12:48:01 +09:00
6 changed files with 199 additions and 106 deletions

View File

@@ -12,7 +12,7 @@ declare(strict_types=1);
/**
* build return json
*
* @param array $http_headers
* @param array<string,mixed> $http_headers
* @param string $body
* @return string
*/
@@ -34,15 +34,18 @@ $http_headers = array_filter($_SERVER, function ($value, $key) {
header("Content-Type: application/json; charset=UTF-8");
// if the header has Authorization and RunAuthTest then exit with 401
if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) {
header("HTTP/1.1 401 Unauthorized");
print buildContent($http_headers, '["code": 401, "content": {"Error" => "Not Authorized"}]');
print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}');
exit;
}
print buildContent(
$http_headers,
file_get_contents('php://input') ?: '["code": 500, "content": {"Error" => "file_get_contents failed"}]'
);
if (($file_get = file_get_contents('php://input')) === false) {
header("HTTP/1.1 404 Not Found");
print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}');
exit;
}
print buildContent($http_headers, $file_get);
// __END__

View File

@@ -21,7 +21,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
private string $url_basic_start = '';
private string $url_basic_end = '';
private array $default_config = [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -109,7 +109,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'no config' => [
'config' => null,
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -125,7 +125,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'setup all possible configs' => [
'config' => [
'auth' => ['user', 'passowrd', 'Basic'],
'exception_on_not_authorized' => true,
'http_errors' => false,
'base_uri' => 'http://foo.bar.com',
'headers' => [
'something' => 'other',
@@ -138,7 +138,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
'expected_set' => [
'auth' => ['user', 'passowrd', 'Basic'],
'exception_on_not_authorized' => true,
'http_errors' => false,
'base_uri' => 'http://foo.bar.com',
'headers' => [
'something' => 'other',
@@ -161,7 +161,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'base_uri' => 'http://bar.foo.com'
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => 'http://bar.foo.com',
'query' => [],
'headers' => [],
@@ -179,7 +179,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'base_uri' => 'http://bar.foo.com'
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => 'http://bar.foo.com',
'query' => [],
'headers' => [],
@@ -191,7 +191,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'set_header_add' => null,
'remove_header' => null,
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => 'http://bar.baz.com',
'query' => [],
'headers' => [],
@@ -203,7 +203,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'set header new' => [
'config' => null,
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -217,7 +217,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'set_header_add' => false,
'remove_header' => null,
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -234,7 +234,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -250,7 +250,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'set_header_add' => false,
'remove_header' => null,
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -267,7 +267,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -283,7 +283,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'set_header_add' => true,
'remove_header' => null,
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -301,7 +301,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -317,7 +317,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'remove-entry' => 'foo'
],
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -332,7 +332,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -348,7 +348,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'remove-entry' => null
],
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -363,7 +363,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -379,7 +379,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'remove-entry' => null
],
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -394,7 +394,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -410,7 +410,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'remove-entry' => 'foo'
],
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -427,7 +427,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
],
],
'expected_set' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -443,7 +443,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
'remove-entry' => ['foo', 'bar',]
],
'expected_change' => [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [
@@ -990,7 +990,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
$curl = new \CoreLibs\UrlRequests\Curl();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessageMatches("/InvalidRequestType/");
$response = $curl->request('wrong', 'http://foo.bar.com');
$curl->request('wrong', 'http://foo.bar.com');
}
/**
@@ -1016,32 +1016,89 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase
{
$curl = new \CoreLibs\UrlRequests\Curl();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessageMatches("/CurlError/");
$this->expectExceptionMessageMatches("/CurlExecError/");
// invalid yrl
$response = $curl->request('get', 'as-4939345!#$%');
$curl->request('get', 'as-4939345!#$%');
}
/**
* TODO: Exception:UnauthorizedRequest
* Exception:ClientError
*
* @testdox UrlRequests\Curl Exception:UnauthorizedRequest
* @testdox UrlRequests\Curl Exception:ClientError
*
* @return void
*/
public function testExceptionUnauthorizedRequest(): void
public function testExceptionBadRequest(): void
{
$curl = new \CoreLibs\UrlRequests\Curl(["exception_on_not_authorized" => true]);
$curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => true]);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessageMatches("/UnauthorizedRequest/");
$this->expectExceptionMessageMatches("/ClientError/");
$curl->get($this->url_basic, [
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
]
]);
}
/**
* Exception:ClientError
*
* @testdox UrlRequests\Curl Exception:ClientError on call enable
*
* @return void
*/
public function testExceptionBadRequestEnable(): void
{
$curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => false]);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessageMatches("/ClientError/");
$curl->get($this->url_basic, [
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
],
"http_errors" => true
]);
}
/**
* Exception:ClientError
*
* @testdox UrlRequests\Curl Exception:ClientError unset on call
*
* @return void
*/
public function testExceptionBadRequestUnset(): void
{
// if true, with false it has to be off
$curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => true]);
$response = $curl->get($this->url_basic, [
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
]
"RunAuthTest" => "yes",
],
"http_errors" => false,
]);
// $response = $curl->get('https://httpbin.org/bearer', [
// "headers" => ["Authorization" => "schmalztiegel"]
// ]);
$this->assertEquals(
"401",
$response['code'],
'Unset Exception failed with false'
);
// if false, null should not change it
$curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => false]);
$response = $curl->get($this->url_basic, [
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
],
"http_errors" => null,
]);
$this->assertEquals(
"401",
$response['code'],
'Unset Exception failed with null'
);
}
/**

View File

@@ -15,7 +15,7 @@ $log = new CoreLibs\Logging\Logging([
/**
* build return json
*
* @param array<string,mixed> $http_headers
* @param array<string,mixed> $http_headers
* @param string $body
* @return string
*/
@@ -25,7 +25,8 @@ function buildContent(array $http_headers, string $body): string
'HEADERS' => $http_headers,
"REQUEST_TYPE" => $_SERVER['REQUEST_METHOD'],
"PARAMS" => $_GET,
"BODY" => Json::jsonConvertToArray($body)
"BODY" => Json::jsonConvertToArray($body),
// "STRING_BODY" => $body,
]);
}
@@ -40,11 +41,15 @@ header("Content-Type: application/json; charset=UTF-8");
// if the header has Authorization and RunAuthTest then exit with 401
if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) {
header("HTTP/1.1 401 Unauthorized");
print buildContent($http_headers, '["code": 401, "content": {"Error" => "Not Authorized"}]');
print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}');
exit;
}
$file_get = file_get_contents('php://input') ?: '{"Error" => "file_get_contents failed"}';
if (($file_get = file_get_contents('php://input')) === false) {
header("HTTP/1.1 404 Not Found");
print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}');
exit;
}
// str_replace('\"', '"', trim($file_get, '"'));
$log->debug('SERVER', $log->prAr($_SERVER));

View File

@@ -244,7 +244,7 @@ print "<hr>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"exception_on_not_authorized" => false,
"http_errors" => false,
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
@@ -262,7 +262,7 @@ print "AUTH REQUEST WITH EXCEPTION:<br>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"exception_on_not_authorized" => true,
"http_errors" => true,
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
@@ -276,6 +276,24 @@ try {
} catch (Exception $e) {
print "Exception: <pre>" . print_r(json_decode($e->getMessage(), true), true) . "</pre><br>";
}
print "AUTH REQUEST WITH EXCEPTION (UNSET):<br>";
try {
$uc = new Curl([
"base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/',
"http_errors" => true,
"headers" => [
"Authorization" => "schmalztiegel",
"RunAuthTest" => "yes",
]
]);
$response = $uc->get('UrlRequests.target.php', ['http_errors' => false]);
print "AUTH REQUEST (UNSET): <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";
} catch (Exception $e) {
print "Exception: <pre>" . print_r(json_decode($e->getMessage(), true), true) . "</pre><br>";
}
print "<hr>";
$uc = new Curl([
@@ -285,7 +303,7 @@ $uc = new Curl([
]
]);
$response = $uc->get('UrlRequests.target.php', ["headers" => null, "query" => ["test" => "one-test"]]);
print "AUTH REQUEST: <pre>" . print_r($response, true) . "</pre>";
print "HEADER RESET REQUEST: <pre>" . print_r($response, true) . "</pre>";
print "[uc] SENT URL: " . $uc->getUrlSent() . "<br>";
print "[uc] SENT URL PARSED: <pre>" . print_r($uc->getUrlParsedSent(), true) . "</pre>";
print "[uc] SENT HEADERS: <pre>" . print_r($uc->getHeadersSent(), true) . "</pre>";

View File

@@ -10,7 +10,7 @@
* https://docs.guzzlephp.org/en/stable/index.html
*
* Requests are guzzleHttp compatible
* Config for setup is guzzleHttp compatible (except the exception_on_not_authorized)
* Config for setup is guzzleHttp compatible (except the http_errors)
* Any setters and getters are only for this class
*/
@@ -35,6 +35,12 @@ class Curl implements Interface\RequestsInterface
private const HAVE_POST_FIELDS = ["post", "put", "patch", "delete"];
/** @var array<string> list of requests that must have a body */
private const MANDATORY_POST_FIELDS = ["post", "put", "patch"];
/** @var int http ok request */
public const HTTP_OK = 200;
/** @var int http ok creted response */
public const HTTP_CREATED = 201;
/** @var int http ok no content */
public const HTTP_NO_CONTENT = 204;
/** @var int error bad request */
public const HTTP_BAD_REQUEST = 400;
/** @var int error not authorized Request */
@@ -47,18 +53,12 @@ class Curl implements Interface\RequestsInterface
public const HTTP_CONFLICT = 409;
/** @var int error unprocessable entity */
public const HTTP_UNPROCESSABLE_ENTITY = 422;
/** @var int http ok request */
public const HTTP_OK = 200;
/** @var int http ok creted response */
public const HTTP_CREATED = 201;
/** @var int http ok no content */
public const HTTP_NO_CONTENT = 204;
/** @var int major version for user agent */
public const MAJOR_VERSION = 1;
// the config is set to be as much compatible to guzzelHttp as possible
// phpcs:disable Generic.Files.LineLength
/** @var array{auth?:array{0:string,1:string,2:string},exception_on_not_authorized:bool,base_uri:string,headers:array<string,string|array<string>>,query:array<string,string>,timeout:float,connection_timeout:float} config settings as
/** @var array{auth?:array{0:string,1:string,2:string},http_errors:bool,base_uri:string,headers:array<string,string|array<string>>,query:array<string,string>,timeout:float,connection_timeout:float} config settings as
*phpcs:enable Generic.Files.LineLength
* auth: [0: user, 1: password, 2: auth type]
* base_uri: base url to set, will prefix all urls given in calls
@@ -66,10 +66,10 @@ class Curl implements Interface\RequestsInterface
* timeout: default 0, in seconds (CURLOPT_TIMEOUT_MS)
* connect_timeout: default 300, in seconds (CURLOPT_CONNECTTIMEOUT_MS)
* : below is not a guzzleHttp config
* exception_on_not_authorized: bool true/false for throwing exception on auth error
* http_errors: default true, bool true/false for throwing exception on >= 400 HTTP errors
*/
private array $config = [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -115,14 +115,14 @@ class Curl implements Interface\RequestsInterface
* Set the main configuration
*
* phpcs:disable Generic.Files.LineLength
* @param array{auth?:array{0:string,1:string,2:string},exception_on_not_authorized?:bool,base_uri?:string,headers?:array<string,string|array<string>>,query?:array<string,string>,timeout?:float,connection_timeout?:float} $config
* @param array{auth?:array{0:string,1:string,2:string},http_errors?:bool,base_uri?:string,headers?:array<string,string|array<string>>,query?:array<string,string>,timeout?:float,connection_timeout?:float} $config
* @return void
* phpcs:enable Generic.Files.LineLength
*/
private function setConfiguration(array $config)
{
$default_config = [
'exception_on_not_authorized' => false,
'http_errors' => true,
'base_uri' => '',
'query' => [],
'headers' => [],
@@ -157,10 +157,10 @@ class Curl implements Interface\RequestsInterface
}
// only set if bool
if (
!isset($config['exception_on_not_authorized']) ||
!is_bool($config['exception_on_not_authorized'])
!isset($config['http_errors']) ||
!is_bool($config['http_errors'])
) {
$config['exception_on_not_authorized'] = false;
$config['http_errors'] = true;
}
if (!empty($config['base_uri'])) {
if (($parsed_base_uri = $this->parseUrl($config['base_uri'])) !== false) {
@@ -458,30 +458,33 @@ class Curl implements Interface\RequestsInterface
/**
* Overall request call
*
* @param string $type get, post, pathc, put, delete:
* if not set or invalid throw error
* @param string $url The URL being requested,
* including domain and protocol
* @param null|array<string,string|array<string>> $headers [default=[]] Headers to be used in the request
* @param null|array<string,string> $query [default=null] Optinal query parameters
* @param null|string|array<string,mixed> $body [default=null] Data body, converted to JSON
* @param string $type get, post, pathc, put, delete:
* if not set or invalid throw error
* @param string $url The URL being requested,
* including domain and protocol
* @param null|array<string,string|array<string>> $headers Headers to be used in the request
* @param null|array<string,string> $query Optinal query parameters
* @param null|string|array<string,mixed> $body Data body, converted to JSON
* @param null|bool $http_errors Throw exception on http response
* 400 or higher if set to true
* @return array{code:string,headers:array<string,array<string>>,content:string}
* @throws \RuntimeException if type param is not valid
*/
private function curlRequest(
string $type,
string $url,
null|array $headers = [],
null|array $query = null,
null|string|array $body = null
null|array $headers,
null|array $query,
null|string|array $body,
null|bool $http_errors,
): array {
$this->url = $this->buildQuery($url, $query);
$this->headers = $this->convertHeaders($this->buildHeaders($headers));
if (!in_array($type, self::VALID_REQUEST_TYPES)) {
throw new RuntimeException(
json_encode([
'status' => 'FAILURE',
'code' => 'C003',
'status' => 'ERROR',
'code' => 'R002',
'type' => 'InvalidRequestType',
'message' => 'Invalid request type set: ' . $type,
'context' => [
@@ -514,7 +517,7 @@ class Curl implements Interface\RequestsInterface
// for debug
// print "CURLINFO_HEADER_OUT: <pre>" . curl_getinfo($handle, CURLINFO_HEADER_OUT) . "</pre>";
// get response code and bail on not authorized
$http_response = $this->handleCurlResponse($http_result, $handle);
$http_response = $this->handleCurlResponse($http_result, $http_errors, $handle);
// close handler
$this->handleCurlClose($handle);
// return response and result
@@ -635,7 +638,7 @@ class Curl implements Interface\RequestsInterface
// execute query
$http_result = curl_exec($handle);
if ($http_result === true) {
// only if CURLOPT_RETURNTRANSFER
// only if CURLOPT_RETURNTRANSFER is turned off
return (string)self::HTTP_OK;
} elseif ($http_result !== false) {
return $http_result;
@@ -666,7 +669,7 @@ class Curl implements Interface\RequestsInterface
json_encode([
'status' => 'FAILURE',
'code' => 'C002',
'type' => 'CurlError',
'type' => 'CurlExecError',
'message' => $message,
'context' => [
'url' => $url,
@@ -681,40 +684,44 @@ class Curl implements Interface\RequestsInterface
// MARK: curl response handler
/**
* Handle curl response and not auth 401 errors
* Handle curl response, will throw exception on anything that is lower 400
* can be turned off by setting http_errors to false
*
* @param string $http_result
* @param \CurlHandle $handle
* @param string $http_result result string from the url call
* @param ?bool $http_errors if we should throw an exception on error, override config setting
* @param \CurlHandle $handle Curl handler
* @return string http response code
* @throws \RuntimeException Auth error
* @throws \RuntimeException if http_errors is true then will throw exception on any response code >= 400
*/
private function handleCurlResponse(
string $http_result,
?bool $http_errors,
\CurlHandle $handle
): string {
$http_response = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
if (
empty($this->config['exception_on_not_authorized']) ||
$http_response !== self::HTTP_NOT_AUTHORIZED
empty($http_errors ?? $this->config['http_errors']) ||
$http_response < self::HTTP_BAD_REQUEST
) {
return (string)$http_response;
}
// set curl error number
$err = curl_errno($handle);
// extract all the error codes
$result_ar = json_decode((string)$http_result, true);
$url = curl_getinfo($handle, CURLINFO_EFFECTIVE_URL);
// throw Error here with all codes
throw new RuntimeException(
json_encode([
'status' => 'ERROR',
'code' => $http_response,
'type' => 'UnauthorizedRequest',
'message' => 'Request could not be finished successfully because of an authorization error',
'code' => 'H' . (string)$http_response,
'type' => $http_response < 500 ? 'ClientError' : 'ServerError',
'message' => 'Request could not be finished successfully because of bad request response',
'context' => [
'url' => $url,
'result' => $result_ar,
'http_response' => $http_response,
// extract all the error content if returned
'result' => json_decode((string)$http_result, true),
// curl internal error number
'curl_errno' => $err,
// the full curl info block
'curl_info' => curl_getinfo($handle),
],
]) ?: '',
$err
@@ -761,7 +768,7 @@ class Curl implements Interface\RequestsInterface
throw new \UnexpectedValueException(
json_encode([
'status' => 'ERROR',
'code' => 'C004',
'code' => 'R001',
'type' => 'DuplicatedArrayKey',
'message' => 'Key already exists in the headers',
'context' => [
@@ -940,7 +947,7 @@ class Curl implements Interface\RequestsInterface
* phpcs:disable Generic.Files.LineLength
* @param string $type
* @param string $url
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<string,mixed>} $options
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
* @throws \UnexpectedValueException on missing body data when body data is needed
* phpcs:enable Generic.Files.LineLength
@@ -962,7 +969,8 @@ class Curl implements Interface\RequestsInterface
$url,
!array_key_exists('headers', $options) ? [] : $options['headers'],
$options['query'] ?? null,
$options['body'] ?? null
$options['body'] ?? null,
!array_key_exists('http_errors', $options) ? null : $options['http_errors'],
);
}
}

View File

@@ -25,8 +25,8 @@ trait CurlTrait
* "get" calls do not set any body
*
* @param string $type if set as get do not add body, else add body
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>} $options Request options
* @return array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>}
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Request options
* @return array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool}
*/
private function setOptions(string $type, array $options): array
{
@@ -34,12 +34,14 @@ trait CurlTrait
return [
"headers" => !array_key_exists('headers', $options) ? [] : $options['headers'],
"query" => $options['query'] ?? null,
"http_errors" => !array_key_exists('http_errors', $options) ? null : $options['http_errors'],
];
} else {
return [
"headers" => !array_key_exists('headers', $options) ? [] : $options['headers'],
"query" => $options['query'] ?? null,
"body" => $options['body'] ?? null,
"http_errors" => !array_key_exists('http_errors', $options) ? null : $options['http_errors'],
];
}
}
@@ -53,7 +55,7 @@ trait CurlTrait
*
* @param string $type What type of request we send, will throw exception if not a valid one
* @param string $url The url to send
* @param array{headers?:null|array<string,string|array<string>>,query?:null|string|array<string,mixed>,body?:null|string|array<string,mixed>} $options Request options
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Request options
* @return array{code:string,headers:array<string,array<string>>,content:string} [default=[]] Result code, headers and content as array, content is json
* @throws \UnexpectedValueException on missing body data when body data is needed
*/
@@ -65,7 +67,7 @@ trait CurlTrait
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>} $options Options to set
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} [default=[]] Result code, headers and content as array, content is json
*/
public function get(string $url, array $options = []): array
@@ -86,7 +88,7 @@ trait CurlTrait
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>} $options Options to set
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
*/
public function post(string $url, array $options): array
@@ -104,7 +106,7 @@ trait CurlTrait
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>} $options Options to set
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
*/
public function put(string $url, array $options): array
@@ -122,7 +124,7 @@ trait CurlTrait
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>} $options Options to set
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} Result code, headers and content as array, content is json
*/
public function patch(string $url, array $options): array
@@ -141,7 +143,7 @@ trait CurlTrait
*
* @param string $url The URL being requested,
* including domain and protocol
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>} $options Options to set
* @param array{headers?:null|array<string,string|array<string>>,query?:null|array<string,string>,body?:null|string|array<mixed>,http_errors?:null|bool} $options Options to set
* @return array{code:string,headers:array<string,array<string>>,content:string} [default=[]] Result code, headers and content as array, content is json
*/
public function delete(string $url, array $options = []): array