Skip to content

Commit b306fa4

Browse files
chore(internal): refactored internal codepaths
1 parent 666f374 commit b306fa4

File tree

11 files changed

+173
-176
lines changed

11 files changed

+173
-176
lines changed

src/Core/BaseClient.php

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
namespace CasParser\Core;
66

7+
use CasParser\Core\Contracts\BasePage;
8+
use CasParser\Core\Contracts\BaseStream;
9+
use CasParser\Core\Conversion\Contracts\Converter;
10+
use CasParser\Core\Conversion\Contracts\ConverterSource;
711
use CasParser\Core\Errors\APIStatusError;
812
use CasParser\RequestOptions;
913
use Http\Discovery\Psr17FactoryDiscovery;
@@ -16,6 +20,15 @@
1620
use Psr\Http\Message\UriFactoryInterface;
1721
use Psr\Http\Message\UriInterface;
1822

23+
/**
24+
* @phpstan-type normalized_request = array{
25+
* method: string,
26+
* path: string,
27+
* query: array<string, mixed>,
28+
* headers: array<string, string|null|list<string>>,
29+
* body: mixed,
30+
* }
31+
*/
1932
class BaseClient
2033
{
2134
protected UriInterface $baseUrl;
@@ -48,37 +61,56 @@ public function __construct(
4861
* @param string|list<mixed> $path
4962
* @param array<string, mixed> $query
5063
* @param array<string, mixed> $headers
64+
* @param class-string<BasePage<mixed>> $page
65+
* @param class-string<BaseStream<mixed>> $stream
5166
*/
5267
public function request(
5368
string $method,
5469
string|array $path,
5570
array $query = [],
5671
array $headers = [],
5772
mixed $body = null,
73+
string|Converter|ConverterSource|null $convert = null,
74+
?string $page = null,
75+
?string $stream = null,
5876
mixed $options = [],
5977
): mixed {
6078
// @phpstan-ignore-next-line
61-
[$req, $opts] = $this->buildRequest(method: $method, path: $path, query: $query, headers: $headers, opts: $options);
79+
[$req, $opts] = $this->buildRequest(method: $method, path: $path, query: $query, headers: $headers, body: $body, opts: $options);
80+
['method' => $method, 'path' => $uri, 'headers' => $headers] = $req;
81+
82+
$req = $this->requestFactory->createRequest($method, uri: $uri);
83+
$req = Util::withSetHeaders($req, headers: $headers);
6284

6385
// @phpstan-ignore-next-line
6486
$rsp = $this->sendRequest($req, data: $body, opts: $opts, redirectCount: 0, retryCount: 0);
65-
if (204 == $rsp->getStatusCode()) {
66-
return null; // Handle 204 No Content
87+
88+
$decoded = Util::decodeContent($rsp);
89+
90+
if (!is_null($stream)) {
91+
return new $stream(
92+
convert: $convert,
93+
request: $req,
94+
response: $rsp,
95+
stream: $decoded
96+
);
6797
}
6898

69-
return Util::decodeContent($rsp);
70-
}
99+
if (!is_null($page)) {
100+
return new $page(
101+
convert: $convert,
102+
client: $this,
103+
request: $req,
104+
options: $opts,
105+
data: $decoded,
106+
);
107+
}
71108

72-
/**
73-
* @template Item
74-
* @template T of Pagination\AbstractPage<Item>
75-
*
76-
* @param T $page
77-
*/
78-
public function requestApiList(object $page, RequestOptions $options): ResponseInterface
79-
{
80-
// @phpstan-ignore-next-line
81-
return null;
109+
if (!is_null($convert)) {
110+
return Conversion::coerce($convert, value: $decoded);
111+
}
112+
113+
return $decoded;
82114
}
83115

84116
/** @return array<string, string> */
@@ -91,24 +123,25 @@ protected function authHeaders(): array
91123
* @param string|list<string> $path
92124
* @param array<string, mixed> $query
93125
* @param array<string, string|int|list<string|int>|null> $headers
94-
* @param array{
126+
* @param RequestOptions|array{
95127
* timeout?: float|null,
96128
* maxRetries?: int|null,
97129
* initialRetryDelay?: float|null,
98130
* maxRetryDelay?: float|null,
99131
* extraHeaders?: list<string>|null,
100132
* extraQueryParams?: list<string>|null,
101133
* extraBodyParams?: list<string>|null,
102-
* }|RequestOptions|null $opts
134+
* }|null $opts
103135
*
104-
* @return array{RequestInterface, RequestOptions}
136+
* @return array{normalized_request, RequestOptions}
105137
*/
106138
protected function buildRequest(
107139
string $method,
108140
string|array $path,
109141
array $query,
110142
array $headers,
111-
array|RequestOptions|null $opts,
143+
mixed $body,
144+
RequestOptions|array|null $opts,
112145
): array {
113146
$opts = [...$this->options->__serialize(), ...RequestOptions::parse($opts)->__serialize()];
114147
$options = new RequestOptions(...$opts);
@@ -117,16 +150,15 @@ protected function buildRequest(
117150

118151
/** @var array<string, mixed> $mergedQuery */
119152
$mergedQuery = array_merge_recursive($query, $options->extraQueryParams);
120-
$uri = Util::joinUri($this->baseUrl, path: $parsedPath, query: $mergedQuery);
153+
$uri = Util::joinUri($this->baseUrl, path: $parsedPath, query: $mergedQuery)->__toString();
121154

122-
/** @var array<string, string | list<string>> $mergedHeaders */
155+
/** @var array<string, string|list<string>|null> $mergedHeaders */
123156
$mergedHeaders = [...$this->headers,
124157
...$this->authHeaders(),
125158
...$headers,
126159
...$options->extraHeaders, ];
127160

128-
$req = $this->requestFactory->createRequest(strtoupper($method), uri: $uri);
129-
$req = Util::withSetHeaders($req, headers: $mergedHeaders);
161+
$req = ['method' => strtoupper($method), 'path' => $uri, 'query' => $mergedQuery, 'headers' => $mergedHeaders, 'body' => $body];
130162

131163
return [$req, $options];
132164
}

src/Core/Concerns/SdkModel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public function __debugInfo(): array
6060
*/
6161
public function __toString(): string
6262
{
63-
return json_encode($this->__debugInfo(), flags: JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) ?: '';
63+
return Util::prettyEncodeJson($this->__debugInfo());
6464
}
6565

6666
/**

src/Core/Contracts/BasePage.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
namespace CasParser\Core\Contracts;
66

7-
use CasParser\Core\BaseClient;
8-
use CasParser\Core\Pagination\PageRequestOptions;
9-
use Psr\Http\Message\ResponseInterface;
7+
use CasParser\Client;
8+
use CasParser\Core\Conversion\Contracts\Converter;
9+
use CasParser\Core\Conversion\Contracts\ConverterSource;
10+
use CasParser\RequestOptions;
1011

1112
/**
1213
* @template Item
@@ -17,12 +18,15 @@ interface BasePage extends \IteratorAggregate
1718
{
1819
/**
1920
* @internal
21+
*
22+
* @param array<string, mixed> $request
2023
*/
2124
public function __construct(
22-
BaseClient $client,
23-
PageRequestOptions $options,
24-
ResponseInterface $response,
25-
mixed $body,
25+
Converter|ConverterSource|string $convert,
26+
Client $client,
27+
array $request,
28+
RequestOptions $options,
29+
mixed $data,
2630
);
2731

2832
public function hasNextPage(): bool;

src/Core/Contracts/BaseStream.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,28 @@
44

55
namespace CasParser\Core\Contracts;
66

7+
use CasParser\Core\Conversion\Contracts\Converter;
8+
use CasParser\Core\Conversion\Contracts\ConverterSource;
9+
use Psr\Http\Message\RequestInterface;
10+
use Psr\Http\Message\ResponseInterface;
11+
712
/**
813
* @template TInner
914
*
1015
* @extends \IteratorAggregate<int, TInner>
1116
*/
1217
interface BaseStream extends \IteratorAggregate
1318
{
19+
/**
20+
* @param \Generator<TInner> $stream
21+
*/
22+
public function __construct(
23+
Converter|ConverterSource|string $convert,
24+
RequestInterface $request,
25+
ResponseInterface $response,
26+
\Generator $stream,
27+
);
28+
1429
/**
1530
* Manually force the stream to close early.
1631
* Iterating through will automatically close as well.

src/Core/Errors/APIStatusError.php

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace CasParser\Core\Errors;
44

5+
use CasParser\Core\Util;
56
use Psr\Http\Message\RequestInterface;
67
use Psr\Http\Message\ResponseInterface;
7-
use Psr\Http\Message\StreamInterface;
88

99
class APIStatusError extends APIError
1010
{
@@ -23,8 +23,8 @@ public function __construct(
2323
$this->status = $response->getStatusCode();
2424

2525
$summary = 'Status: '.$this->status.PHP_EOL
26-
.'Response Body: '.self::fmtBody($response->getBody()).PHP_EOL
27-
.'Request Body: '.self::fmtBody($request->getBody()).PHP_EOL;
26+
.'Response Body: '.Util::prettyEncodeJson(Util::decodeJson($response->getBody())).PHP_EOL
27+
.'Request Body: '.Util::prettyEncodeJson(Util::decodeJson($request->getBody())).PHP_EOL;
2828

2929
if ('' != $message) {
3030
$summary .= $message.PHP_EOL.$summary;
@@ -35,7 +35,8 @@ public function __construct(
3535

3636
public static function from(
3737
RequestInterface $request,
38-
ResponseInterface $response
38+
ResponseInterface $response,
39+
string $message = ''
3940
): self {
4041
$status = $response->getStatusCode();
4142

@@ -51,11 +52,6 @@ public static function from(
5152
default => APIStatusError::class
5253
};
5354

54-
return new $cls(request: $request, response: $response);
55-
}
56-
57-
private static function fmtBody(StreamInterface $body): string
58-
{
59-
return json_encode(json_decode($body->__toString() ?: ''), JSON_PRETTY_PRINT) ?: '';
55+
return new $cls(request: $request, response: $response, message: $message);
6056
}
6157
}

src/Core/Pagination/AbstractPage.php

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,30 @@
44

55
namespace CasParser\Core\Pagination;
66

7-
use CasParser\Core\BaseClient;
7+
use CasParser\Client;
88
use CasParser\Core\Contracts\BasePage;
9+
use CasParser\Core\Conversion\Contracts\Converter;
10+
use CasParser\Core\Conversion\Contracts\ConverterSource;
911
use CasParser\Core\Errors\APIStatusError;
10-
use Psr\Http\Message\ResponseInterface;
12+
use CasParser\RequestOptions;
1113

1214
/**
1315
* @internal
1416
*
1517
* @template Item
1618
*
1719
* @implements BasePage<Item>
20+
*
21+
* @phpstan-import-type normalized_request from \CasParser\Core\BaseClient
1822
*/
1923
abstract class AbstractPage implements BasePage
2024
{
2125
public function __construct(
22-
protected BaseClient $client,
23-
protected PageRequestOptions $options,
24-
protected ResponseInterface $response,
25-
protected mixed $body,
26+
protected Converter|ConverterSource|string $convert,
27+
protected Client $client,
28+
protected array $request,
29+
protected RequestOptions $options,
30+
protected mixed $data,
2631
) {}
2732

2833
/**
@@ -37,7 +42,7 @@ public function hasNextPage(): bool
3742
return false;
3843
}
3944

40-
return null != $this->nextPageRequestOptions();
45+
return null != $this->nextRequest();
4146
}
4247

4348
/**
@@ -51,24 +56,17 @@ public function hasNextPage(): bool
5156
*/
5257
public function getNextPage(): static
5358
{
54-
$nextOptions = $this->nextPageRequestOptions();
55-
if (!$nextOptions) {
59+
$next = $this->nextRequest();
60+
if (!$next) {
5661
throw new \RuntimeException(
5762
'No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.'
5863
);
5964
}
6065

61-
$response = $this->client->requestApiList($this, $nextOptions);
62-
63-
/** @var static of AbstractPage<Item> $nextPage */
64-
$nextPage = new static(
65-
client: $this->client,
66-
options: $nextOptions,
67-
response: $response,
68-
body: $response->getBody()
69-
);
66+
[$req, $opts] = $next;
7067

71-
return $nextPage;
68+
// @phpstan-ignore-next-line
69+
return $this->client->request(...$req, convert: $this->convert, page: $this, options: $opts);
7270
}
7371

7472
/**
@@ -102,5 +100,8 @@ public function pagingEachItem(): \Generator
102100
}
103101
}
104102

105-
abstract protected function nextPageRequestOptions(): ?PageRequestOptions;
103+
/**
104+
* @return array{normalized_request, RequestOptions}
105+
*/
106+
abstract protected function nextRequest(): ?array;
106107
}

0 commit comments

Comments
 (0)