Skip to content

Let Clients implements HttpClient #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 1, 2024
44 changes: 5 additions & 39 deletions src/Redmine/Api/AbstractApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Redmine\Exception;
use Redmine\Exception\SerializerException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Http\Request;
use Redmine\Http\Response;
use Redmine\Serializer\JsonSerializer;
Expand Down Expand Up @@ -73,7 +74,7 @@ final protected function getHttpClient(): HttpClient

final protected function getLastResponse(): Response
{
return $this->lastResponse !== null ? $this->lastResponse : $this->createResponse(0, '', '');
return $this->lastResponse !== null ? $this->lastResponse : HttpFactory::makeResponse(0, '', '');
}

/**
Expand Down Expand Up @@ -412,16 +413,12 @@ private function getResponseAsArray(Response $response): array

private function handleClient(Client $client): HttpClient
{
$responseFactory = Closure::fromCallable([$this, 'createResponse']);

return new class ($client, $responseFactory) implements HttpClient {
return new class ($client) implements HttpClient {
private $client;
private $responseFactory;

public function __construct(Client $client, Closure $responseFactory)
public function __construct(Client $client)
{
$this->client = $client;
$this->responseFactory = $responseFactory;
}

public function request(Request $request): Response
Expand All @@ -436,7 +433,7 @@ public function request(Request $request): Response
$this->client->requestGet($request->getPath());
}

return ($this->responseFactory)(
return HttpFactory::makeResponse(
$this->client->getLastResponseStatusCode(),
$this->client->getLastResponseContentType(),
$this->client->getLastResponseBody()
Expand All @@ -445,37 +442,6 @@ public function request(Request $request): Response
};
}

private function createResponse(int $statusCode, string $contentType, string $body): Response
{
return new class ($statusCode, $contentType, $body) implements Response {
private $statusCode;
private $contentType;
private $body;

public function __construct(int $statusCode, string $contentType, string $body)
{
$this->statusCode = $statusCode;
$this->contentType = $contentType;
$this->body = $body;
}

public function getStatusCode(): int
{
return $this->statusCode;
}

public function getContentType(): string
{
return $this->contentType;
}

public function getContent(): string
{
return $this->body;
}
};
}

private function createRequest(string $method, string $path, string $contentType, string $content = ''): Request
{
return new class ($method, $path, $contentType, $content) implements Request {
Expand Down
71 changes: 50 additions & 21 deletions src/Redmine/Client/NativeCurlClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
namespace Redmine\Client;

use Redmine\Exception\ClientException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Http\Request;
use Redmine\Http\Response;

/**
* Native cURL client.
*/
final class NativeCurlClient implements Client
final class NativeCurlClient implements Client, HttpClient
{
use ClientApiTrait;

Expand Down Expand Up @@ -55,6 +59,27 @@ public function __construct(
}
}

/**
* Create and send a HTTP request and return the response
*
* @throws ClientException If anything goes wrong on creating or sending the request
*/
public function request(Request $request): Response
{
$this->runRequest(
$request->getMethod(),
$request->getPath(),
$request->getContent(),
$request->getContentType()
);

return HttpFactory::makeResponse(
$this->lastResponseStatusCode,
$this->lastResponseContentType,
$this->lastResponseBody
);
}

/**
* Sets to an existing username so api calls can be
* impersonated to this user.
Expand All @@ -77,31 +102,31 @@ public function stopImpersonateUser(): void
*/
public function requestGet(string $path): bool
{
return $this->request('get', $path);
return $this->runRequest('GET', $path);
}

/**
* Create and send a POST request.
*/
public function requestPost(string $path, string $body): bool
{
return $this->request('post', $path, $body);
return $this->runRequest('POST', $path, $body);
}

/**
* Create and send a PUT request.
*/
public function requestPut(string $path, string $body): bool
{
return $this->request('put', $path, $body);
return $this->runRequest('PUT', $path, $body);
}

/**
* Create and send a DELETE request.
*/
public function requestDelete(string $path): bool
{
return $this->request('delete', $path);
return $this->runRequest('DELETE', $path);
}

/**
Expand Down Expand Up @@ -211,13 +236,13 @@ private function unsetHttpHeader(string $name): void
/**
* @throws ClientException If anything goes wrong on curl request
*/
private function request(string $method, string $path, string $body = ''): bool
private function runRequest(string $method, string $path, string $body = '', string $contentType = ''): bool
{
$this->lastResponseStatusCode = 0;
$this->lastResponseContentType = '';
$this->lastResponseBody = '';

$curl = $this->createCurl($method, $path, $body);
$curl = $this->createCurl($method, $path, $body, $contentType);

$response = curl_exec($curl);

Expand Down Expand Up @@ -249,7 +274,7 @@ private function request(string $method, string $path, string $body = ''): bool
*
* @return \CurlHandle a cURL handle on success, <b>FALSE</b> on errors
*/
private function createCurl(string $method, string $path, string $body = '')
private function createCurl(string $method, string $path, string $body = '', string $contentType = '')
{
// General cURL options
$curlOptions = [
Expand All @@ -264,13 +289,13 @@ private function createCurl(string $method, string $path, string $body = '')
$curlOptions[CURLOPT_URL] = $this->url . $path;

// Set the HTTP request headers
$curlOptions[CURLOPT_HTTPHEADER] = $this->createHttpHeader($path);
$curlOptions[CURLOPT_HTTPHEADER] = $this->createHttpHeader($path, $contentType);

unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
unset($curlOptions[CURLOPT_POST]);
unset($curlOptions[CURLOPT_POSTFIELDS]);
switch ($method) {
case 'post':
case 'POST':
$curlOptions[CURLOPT_POST] = 1;
if ($this->isUploadCall($path) && $this->isValidFilePath($body)) {
@trigger_error('Uploading an attachment by filepath is deprecated, use file_get_contents() to upload the file content instead.', E_USER_DEPRECATED);
Expand All @@ -286,13 +311,13 @@ private function createCurl(string $method, string $path, string $body = '')
$curlOptions[CURLOPT_POSTFIELDS] = $body;
}
break;
case 'put':
case 'PUT':
$curlOptions[CURLOPT_CUSTOMREQUEST] = 'PUT';
if ($body !== '') {
$curlOptions[CURLOPT_POSTFIELDS] = $body;
}
break;
case 'delete':
case 'DELETE':
$curlOptions[CURLOPT_CUSTOMREQUEST] = 'DELETE';
break;
default: // GET
Expand All @@ -314,7 +339,7 @@ private function createCurl(string $method, string $path, string $body = '')
return $curl;
}

private function createHttpHeader(string $path): array
private function createHttpHeader(string $path, string $contentType = ''): array
{
// Additional request headers
$httpHeaders = [
Expand Down Expand Up @@ -352,14 +377,18 @@ private function createHttpHeader(string $path): array
// Now set or reset mandatory headers

// Content type headers
$tmp = parse_url($this->url . $path);

if ($this->isUploadCall($path)) {
$httpHeaders[] = 'Content-Type: application/octet-stream';
} elseif ('json' === substr($tmp['path'], -4)) {
$httpHeaders[] = 'Content-Type: application/json';
} elseif ('xml' === substr($tmp['path'], -3)) {
$httpHeaders[] = 'Content-Type: text/xml';
if ($contentType !== '') {
$httpHeaders[] = 'Content-Type: ' . $contentType;
} else {
$tmp = parse_url($this->url . $path);

if ($this->isUploadCall($path)) {
$httpHeaders[] = 'Content-Type: application/octet-stream';
} elseif ('json' === substr($tmp['path'], -4)) {
$httpHeaders[] = 'Content-Type: application/json';
} elseif ('xml' === substr($tmp['path'], -3)) {
$httpHeaders[] = 'Content-Type: text/xml';
}
}

return $httpHeaders;
Expand Down
Loading