From 9818410889b24063eebe9828afca91814b2131a5 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 7 Nov 2024 11:39:33 +0900 Subject: [PATCH] Local phan/phpstan install and config update, merge in UrlRequest changes --- .phan/config.php | 1 + composer.json | 2 + phpstan-bootstrap.php | 1 + src/UrlRequests/Curl.php | 95 +++++++++++-------- src/UrlRequests/CurlTrait.php | 34 +------ .../AAASetupData/requests/http_requests.php | 22 ++++- .../CoreLibsUrlRequestsCurlTest.php | 41 +++++++- 7 files changed, 121 insertions(+), 75 deletions(-) diff --git a/.phan/config.php b/.phan/config.php index 84e0364..5076c7f 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -370,6 +370,7 @@ return [ 'file_list' => [ "./test/configs/config.php", "./test/configs/config.other.php", + "./test/configs/config.path.php", "./test/configs/config.master.php", ], ]; diff --git a/composer.json b/composer.json index 8cb94ee..f5d9ec1 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,8 @@ "psr/log": "^3.0@dev" }, "require-dev": { + "phpstan/phpstan": "^1.12", + "phan/phan": "^5.4", "egrajp/smarty-extended": "^4.3", "gullevek/dotenv": "dev-master", "phpunit/phpunit": "^9" diff --git a/phpstan-bootstrap.php b/phpstan-bootstrap.php index 3d16522..5ca816c 100755 --- a/phpstan-bootstrap.php +++ b/phpstan-bootstrap.php @@ -8,5 +8,6 @@ $_SERVER['HTTP_HOST'] = 'soba.tokyo.tequila.jp'; // for whatever reason it does not load that from the confing.master.php // for includes/admin_header.php define('BASE_NAME', ''); +define('CONTENT_PATH', ''); // __END__ diff --git a/src/UrlRequests/Curl.php b/src/UrlRequests/Curl.php index d6ffab1..fcc8bc1 100644 --- a/src/UrlRequests/Curl.php +++ b/src/UrlRequests/Curl.php @@ -175,7 +175,7 @@ class Curl implements Interface\RequestsInterface * @param array{0:string,1:string,2:string} $auth * @return array{auth_basic_header:string,auth_type:int,auth_userpwd:string} */ - private function authParser(?array $auth): array + private function authParser(array $auth): array { $return_auth = [ 'auth_basic_header' => '', @@ -376,6 +376,8 @@ class Curl implements Interface\RequestsInterface // convert to string as JSON block if it is an array if (is_array($body)) { $params = Json::jsonConvertArrayTo($body); + } elseif (is_string($body)) { + $params = $body; } return $params ?? ''; } @@ -505,36 +507,59 @@ class Curl implements Interface\RequestsInterface return $headers; } + /** + * Set the array block that is sent to the request call + * Make sure that if headers is set as key but null it stays null and set to empty array + * if headers key is missing + * "get" calls do not set any body (null) + * + * phpcs:disable Generic.Files.LineLength + * @param string $type if set as get do not add body, else add body + * @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array>,query?:null|array,body?:null|string|array,http_errors?:null|bool} $options Request options + * @return array{auth:null|array{0:string,1:string,2:string},headers:null|array>,query:null|array,body:null|string|array,http_errors:null|bool} + * phpcs:enable Generic.Files.LineLength + */ + private function setOptions(string $type, array $options): array + { + return [ + "auth" => !array_key_exists('auth', $options) ? ['', '', ''] : $options['auth'], + "headers" => !array_key_exists('headers', $options) ? [] : $options['headers'], + "query" => $options['query'] ?? null, + "http_errors" => !array_key_exists('http_errors', $options) ? null : $options['http_errors'], + "body" => $options["body"] ?? + // check if we need a payload data set, set empty on not set + (in_array($type, self::MANDATORY_POST_FIELDS) && !isset($options['body']) ? [] : null) + ]; + } + // MARK: main curl request /** * 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> $headers Headers to be used in the request - * @param null|array $query Optinal query parameters - * @param null|string|array $body Data body, converted to JSON - * @param null|bool $http_errors Throw exception on http response - * 400 or higher if set to true - * @param null|array{0:string,1:string,2:string} $auth auth array, if null reset global set auth - * @return array{code:string,headers:array>,content:string} + * phpcs:disable Generic.Files.LineLength + * @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 array{auth?:null|array{0:string,1:string,2:string},headers?:null|array>,query?:null|array,body?:null|string|array,http_errors?:null|bool} $options Request options + * @return array{code:string,headers:array>,content:string} Return content + * code: HTTP code, if http_errors if off, this can also hold 400 or 500 type codes + * headers: earch header entry has an array of the entries, can be more than one if proxied, etc + * content: content string as is, if JSON type must be decoded afterwards * @throws \RuntimeException if type param is not valid + * phpcs:enable Generic.Files.LineLength */ private function curlRequest( string $type, string $url, - null|array $headers, - null|array $query, - null|string|array $body, - null|bool $http_errors, - null|array $auth, + array $options, ): array { + // check if we need a payload data set, set empty on not set + $options = $this->setOptions($type, $options); // set auth from override - if (is_array($auth)) { - $auth_data = $this->authParser($auth); + if (is_array($options['auth'])) { + $auth_data = $this->authParser($options['auth']); } else { $auth_data = [ 'auth_basic_header' => null, @@ -543,9 +568,9 @@ class Curl implements Interface\RequestsInterface ]; } // build url - $this->url = $this->buildQuery($url, $query); + $this->url = $this->buildQuery($url, $options['query']); $this->headers = $this->convertHeaders($this->buildHeaders( - $headers, + $options['headers'], $auth_data['auth_basic_header'] )); if (!in_array($type, self::VALID_REQUEST_TYPES)) { @@ -578,8 +603,8 @@ class Curl implements Interface\RequestsInterface curl_setopt($handle, CURLOPT_CUSTOMREQUEST, strtoupper($type)); } // set body data if not null, will send empty [] for empty data - if (in_array($type, self::HAVE_POST_FIELDS) && $body !== null) { - curl_setopt($handle, CURLOPT_POSTFIELDS, $this->convertPayloadData($body)); + if (in_array($type, self::HAVE_POST_FIELDS) && $options['body'] !== null) { + curl_setopt($handle, CURLOPT_POSTFIELDS, $this->convertPayloadData($options['body'])); } // reset all headers before we start the call $this->received_headers = []; @@ -588,7 +613,7 @@ class Curl implements Interface\RequestsInterface // for debug // print "CURLINFO_HEADER_OUT:
" . curl_getinfo($handle, CURLINFO_HEADER_OUT) . "
"; // get response code and bail on not authorized - $http_response = $this->handleCurlResponse($handle, $http_result, $http_errors); + $http_response = $this->handleCurlResponse($handle, $http_result, $options['http_errors']); // close handler $this->handleCurlClose($handle); // return response and result @@ -641,7 +666,7 @@ class Curl implements Interface\RequestsInterface * @param array{auth_type:?int,auth_userpwd:?string} $auth_data auth options to override global * @return void */ - private function setCurlOptions(\CurlHandle $handle, array $headers, ?array $auth_data): void + private function setCurlOptions(\CurlHandle $handle, array $headers, array $auth_data): void { // for not Basic auth only, basic auth sets its own header if ($auth_data['auth_type'] !== null || $auth_data['auth_userpwd'] !== null) { @@ -674,8 +699,7 @@ class Curl implements Interface\RequestsInterface $timeout_requires_no_signal = false; // if we have a timeout signal if (!empty($this->config['timeout'])) { - $timeout_requires_no_signal = $timeout_requires_no_signal || - $this->config['timeout'] < 1; + $timeout_requires_no_signal = $this->config['timeout'] < 1; curl_setopt($handle, CURLOPT_TIMEOUT_MS, $this->config['timeout'] * 1000); } if (!empty($this->config['connection_timeout'])) { @@ -772,9 +796,10 @@ class Curl implements Interface\RequestsInterface * 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 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 + * @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 * @return string http response code * @throws \RuntimeException if http_errors is true then will throw exception on any response code >= 400 */ @@ -1000,21 +1025,15 @@ class Curl implements Interface\RequestsInterface // can have // - headers // - query + // - auth: null for no auth at all + // - http_errors: false for no exception on http error // depending on type, must have (post/put/patch), optional for (delete) // - body $type = strtolower($type); - // check if we need a payload data set, set empty on not set - if (in_array($type, self::MANDATORY_POST_FIELDS) && !isset($options['body'])) { - $options['body'] = []; - } return $this->curlRequest( $type, $url, - !array_key_exists('headers', $options) ? [] : $options['headers'], - $options['query'] ?? null, - $options['body'] ?? null, - !array_key_exists('http_errors', $options) ? null : $options['http_errors'], - !array_key_exists('auth', $options) ? [] : $options['auth'], + $options, ); } } diff --git a/src/UrlRequests/CurlTrait.php b/src/UrlRequests/CurlTrait.php index afacf76..a3b0cbe 100644 --- a/src/UrlRequests/CurlTrait.php +++ b/src/UrlRequests/CurlTrait.php @@ -18,30 +18,6 @@ namespace CoreLibs\UrlRequests; trait CurlTrait { - /** - * Set the array block that is sent to the request call - * Make sure that if headers is set as key but null it stays null and set to empty array - * if headers key is missing - * "get" calls do not set any body - * - * @param string $type if set as get do not add body, else add body - * @param array{auth?:null|array{0:string,1:string,2:string},headers?:null|array>,query?:null|array,body?:null|string|array,http_errors?:null|bool} $options Request options - * @return array{auth?:array{0:string,1:string,2:string},headers?:null|array>,query?:null|array,body?:null|string|array,http_errors?:null|bool} - */ - private function setOptions(string $type, array $options): array - { - $base = [ - "auth" => !array_key_exists('auth', $options) ? [] : $options['auth'], - "headers" => !array_key_exists('headers', $options) ? [] : $options['headers'], - "query" => $options['query'] ?? null, - "http_errors" => !array_key_exists('http_errors', $options) ? null : $options['http_errors'], - ]; - if ($type != "get") { - $base["body"] = $options['body'] ?? null; - } - return $base; - } - /** * combined set call for any type of request with options type parameters * The following options can be set: @@ -71,7 +47,7 @@ trait CurlTrait return $this->request( "get", $url, - $this->setOptions('get', $options), + $options, ); } @@ -89,7 +65,7 @@ trait CurlTrait return $this->request( "post", $url, - $this->setOptions('post', $options), + $options, ); } @@ -107,7 +83,7 @@ trait CurlTrait return $this->request( "put", $url, - $this->setOptions('put', $options), + $options, ); } @@ -125,7 +101,7 @@ trait CurlTrait return $this->request( "patch", $url, - $this->setOptions('patch', $options), + $options, ); } @@ -144,7 +120,7 @@ trait CurlTrait return $this->request( "delete", $url, - $this->setOptions('delete', $options), + $options, ); } } diff --git a/test/phpunit/AAASetupData/requests/http_requests.php b/test/phpunit/AAASetupData/requests/http_requests.php index c355902..912d715 100644 --- a/test/phpunit/AAASetupData/requests/http_requests.php +++ b/test/phpunit/AAASetupData/requests/http_requests.php @@ -13,16 +13,26 @@ declare(strict_types=1); * build return json * * @param array $http_headers - * @param string $body + * @param ?string $body * @return string */ -function buildContent(array $http_headers, string $body): string +function buildContent(array $http_headers, ?string $body): string { + if (is_string($body) && !empty($body)) { + $_body = json_decode($body, true); + if (!is_array($_body)) { + $body = [$body]; + } else { + $body = $_body; + } + } elseif (is_string($body)) { + $body = []; + } return json_encode([ 'HEADERS' => $http_headers, "REQUEST_TYPE" => $_SERVER['REQUEST_METHOD'], "PARAMS" => $_GET, - "BODY" => json_decode($body, true) + "BODY" => $body, ]); } @@ -41,11 +51,15 @@ if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RU exit; } -if (($file_get = file_get_contents('php://input')) === false) { +// if server request type is get set file_get to null -> no body +if ($_SERVER['REQUEST_METHOD'] == "GET") { + $file_get = null; +} elseif (($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__ diff --git a/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php b/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php index 3d751c6..276b2ef 100644 --- a/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php +++ b/test/phpunit/UrlRequests/CoreLibsUrlRequestsCurlTest.php @@ -768,7 +768,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'type' => $type, 'options' => null, 'return_code' => "200", - 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":[],"BODY":null}' + 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":[],"BODY":[]}' ]; $provider["basic " . $type . ", query options"] = [ 'type' => $type, @@ -776,7 +776,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase "query" => ["foo" => "bar"], ], 'return_code' => "200", - 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":{"foo":"bar"},"BODY":null}' + 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":{"foo":"bar"},"BODY":[]}' ]; $provider["basic " . $type . ", query/body options"] = [ 'type' => $type, @@ -787,6 +787,22 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'return_code' => "200", 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":{"foo":"bar"},"BODY":{"foobar":"barbaz"}}' ]; + $provider["basic " . $type . ", body options"] = [ + 'type' => $type, + 'options' => [ + "body" => ["foobar" => "barbaz"], + ], + 'return_code' => "200", + 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":[],"BODY":{"foobar":"barbaz"}}' + ]; + $provider["basic " . $type . ", body options as string"] = [ + 'type' => $type, + 'options' => [ + "body" => "body is a string", + ], + 'return_code' => "200", + 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":[],"BODY":["body is a string"]}' + ]; } // MARK: post/put/patch foreach (['post', 'put', 'patch'] as $type) { @@ -814,7 +830,24 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'return_code' => "200", 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":{"foo":"bar"},"BODY":{"foobar":"barbaz"}}' ]; + $provider["basic " . $type . ", body options"] = [ + 'type' => $type, + 'options' => [ + "body" => ["foobar" => "barbaz"], + ], + 'return_code' => "200", + 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":[],"BODY":{"foobar":"barbaz"}}' + ]; + $provider["basic " . $type . ", body option as string"] = [ + 'type' => $type, + 'options' => [ + "body" => "body is a string", + ], + 'return_code' => "200", + 'return_content' => '{"HEADERS":{"HTTP_USER_AGENT":"CoreLibsUrlRequestCurl\/1","HTTP_ACCEPT":"*\/*","HTTP_HOST":"soba.egplusww.jp"},"REQUEST_TYPE":"' . strtoupper($type) . '","PARAMS":[],"BODY":["body is a string"]}' + ]; } + // $provider['"basic post'] return $provider; // phpcs:enable Generic.Files.LineLength } @@ -917,7 +950,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ); } - // TODO: multi requests with same base connection + // MARK: multi requests with same base connection /** * Undocumented function @@ -970,7 +1003,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase . '"HTTP_THIRD_CALL":"delete","HTTP_ACCEPT":"*\/*",' . '"HTTP_HOST":"soba.egplusww.jp"},' . '"REQUEST_TYPE":"DELETE",' - . '"PARAMS":[],"BODY":null}', + . '"PARAMS":[],"BODY":[]}', $response['content'], 'multi call: delete content not matching' );