Skip to content

Commit 7f3730e

Browse files
feat!: use builders for RequestOptions
1 parent f38ec08 commit 7f3730e

File tree

7 files changed

+156
-70
lines changed

7 files changed

+156
-70
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Certain errors will be automatically retried 2 times by default, with a short ex
105105

106106
Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, >=500 Internal errors, and timeouts will all be retried by default.
107107

108-
You can use the `max_retries` option to configure or disable this:
108+
You can use the `maxRetries` option to configure or disable this:
109109

110110
```php
111111
<?php
@@ -118,7 +118,9 @@ $client = new Client(maxRetries: 0);
118118

119119
// Or, configure per-request:
120120

121-
$result = $client->casParser->smartParse(new RequestOptions(maxRetries: 5));
121+
$result = $client->casParser->smartParse(
122+
requestOptions: RequestOptions::with(maxRetries: 5)
123+
);
122124
```
123125

124126
## Advanced concepts
@@ -129,15 +131,15 @@ $result = $client->casParser->smartParse(new RequestOptions(maxRetries: 5));
129131

130132
You can send undocumented parameters to any endpoint, and read undocumented response properties, like so:
131133

132-
Note: the `extra_` parameters of the same name overrides the documented parameters.
134+
Note: the `extra*` parameters of the same name overrides the documented parameters.
133135

134136
```php
135137
<?php
136138

137139
use CasParser\RequestOptions;
138140

139141
$unifiedResponse = $client->casParser->smartParse(
140-
new RequestOptions(
142+
requestOptions: RequestOptions::with(
141143
extraQueryParams: ["my_query_parameter" => "value"],
142144
extraBodyParams: ["my_body_parameter" => "value"],
143145
extraHeaders: ["my-header" => "value"],

src/Client.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function __construct(?string $apiKey = null, ?string $baseUrl = null)
3232
'CAS_PARSER_BASE_URL'
3333
) ?: 'https://portfolio-parser.api.casparser.in';
3434

35-
$options = new RequestOptions(
35+
$options = RequestOptions::with(
3636
uriFactory: Psr17FactoryDiscovery::findUriFactory(),
3737
streamFactory: Psr17FactoryDiscovery::findStreamFactory(),
3838
requestFactory: Psr17FactoryDiscovery::findRequestFactory(),

src/Core.php

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

55
namespace CasParser\Core;
66

7-
use CasParser\Core\Implementation\Omittable;
7+
use CasParser\Core\Implementation\Omit;
88

9-
const OMIT = Omittable::OMIT;
9+
const OMIT = Omit::omit;

src/Core/BaseClient.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class BaseClient
3232
protected UriInterface $baseUrl;
3333

3434
/**
35+
* @internal
36+
*
3537
* @param array<string, string|int|list<string|int>|null> $headers
3638
*/
3739
public function __construct(
@@ -108,6 +110,8 @@ protected function authHeaders(): array
108110
}
109111

110112
/**
113+
* @internal
114+
*
111115
* @param string|list<string> $path
112116
* @param array<string, mixed> $query
113117
* @param array<string, string|int|list<string|int>|null> $headers
@@ -135,26 +139,31 @@ protected function buildRequest(
135139
mixed $body,
136140
RequestOptions|array|null $opts,
137141
): array {
138-
$opts = array_merge($this->options->toArray(), RequestOptions::parse($opts)->toArray());
139-
$options = new RequestOptions(...$opts);
142+
$options = RequestOptions::parse($this->options, $opts);
140143

141144
$parsedPath = Util::parsePath($path);
142145

143146
/** @var array<string, mixed> $mergedQuery */
144-
$mergedQuery = array_merge_recursive($query, $options->extraQueryParams);
147+
$mergedQuery = array_merge_recursive(
148+
$query,
149+
$options->extraQueryParams ?? [],
150+
);
145151
$uri = Util::joinUri($this->baseUrl, path: $parsedPath, query: $mergedQuery)->__toString();
146152

147153
/** @var array<string, string|list<string>|null> $mergedHeaders */
148154
$mergedHeaders = [...$this->headers,
149155
...$this->authHeaders(),
150156
...$headers,
151-
...$options->extraHeaders, ];
157+
...($options->extraHeaders ?? []), ];
152158

153159
$req = ['method' => strtoupper($method), 'path' => $uri, 'query' => $mergedQuery, 'headers' => $mergedHeaders, 'body' => $body];
154160

155161
return [$req, $options];
156162
}
157163

164+
/**
165+
* @internal
166+
*/
158167
protected function followRedirect(
159168
ResponseInterface $rsp,
160169
RequestInterface $req
@@ -170,6 +179,8 @@ protected function followRedirect(
170179
}
171180

172181
/**
182+
* @internal
183+
*
173184
* @param bool|int|float|string|resource|\Traversable<mixed>|array<string,
174185
* mixed,>|null $data
175186
*/

src/Core/Concerns/SdkParams.php

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,7 @@ trait SdkParams
1818
* @param array<string, mixed>|self|null $params
1919
* @param array<string, mixed>|RequestOptions|null $options
2020
*
21-
* @return array{array<string, mixed>, array{
22-
* timeout: float,
23-
* maxRetries: int,
24-
* initialRetryDelay: float,
25-
* maxRetryDelay: float,
26-
* extraHeaders: list<string>,
27-
* extraQueryParams: list<string>,
28-
* extraBodyParams: list<string>,
29-
* }}
21+
* @return array{array<string, mixed>, RequestOptions}
3022
*/
3123
public static function parseRequest(array|self|null $params, array|RequestOptions|null $options): array
3224
{
@@ -40,17 +32,6 @@ public static function parseRequest(array|self|null $params, array|RequestOption
4032
$opts->maxRetries = 0;
4133
}
4234

43-
$opt = $opts->__serialize();
44-
if (empty($opt['extraHeaders'])) {
45-
unset($opt['extraHeaders']);
46-
}
47-
if (empty($opt['extraQueryParams'])) {
48-
unset($opt['extraQueryParams']);
49-
}
50-
if (empty($opt['extraBodyParams'])) {
51-
unset($opt['extraBodyParams']);
52-
}
53-
54-
return [$dumped, $opt]; // @phpstan-ignore-line
35+
return [$dumped, $opts]; // @phpstan-ignore-line
5536
}
5637
}

src/Core/Implementation/Omittable.php renamed to src/Core/Implementation/Omit.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/**
88
* @internal
99
*/
10-
enum Omittable
10+
enum Omit
1111
{
12-
case OMIT;
12+
case omit;
1313
}

src/RequestOptions.php

Lines changed: 128 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use CasParser\Core\Attributes\Api as Property;
88
use CasParser\Core\Concerns\SdkModel;
99
use CasParser\Core\Contracts\BaseModel;
10-
use CasParser\Core\Implementation\Omittable;
10+
use CasParser\Core\Implementation\Omit;
1111
use Psr\Http\Client\ClientInterface;
1212
use Psr\Http\Message\RequestFactoryInterface;
1313
use Psr\Http\Message\StreamFactoryInterface;
@@ -48,15 +48,15 @@ final class RequestOptions implements BaseModel
4848
#[Property]
4949
public float $maxRetryDelay = 8.0;
5050

51-
/** @var array<string, string|int|list<string|int>|null> $extraHeaders */
52-
#[Property]
53-
public array $extraHeaders = [];
51+
/** @var array<string, string|int|list<string|int>|null>|null $extraHeaders */
52+
#[Property(optional: true)]
53+
public ?array $extraHeaders;
5454

55-
/** @var array<string, mixed> $extraQueryParams */
56-
#[Property]
57-
public array $extraQueryParams = [];
55+
/** @var array<string, mixed>|null $extraQueryParams */
56+
#[Property(optional: true)]
57+
public ?array $extraQueryParams;
5858

59-
#[Property]
59+
#[Property(optional: true)]
6060
public mixed $extraBodyParams;
6161

6262
#[Property(optional: true)]
@@ -71,12 +71,27 @@ final class RequestOptions implements BaseModel
7171
#[Property(optional: true)]
7272
public ?RequestFactoryInterface $requestFactory;
7373

74+
public function __construct()
75+
{
76+
$this->initialize();
77+
}
78+
79+
/**
80+
* @param request_opts|null $options
81+
*/
82+
public static function parse(RequestOptions|array|null ...$options): self
83+
{
84+
$parsed = array_map(static fn ($o) => $o instanceof self ? $o->toArray() : $o ?? [], array: $options);
85+
86+
return self::with(...array_merge(...$parsed)); // @phpstan-ignore-line
87+
}
88+
7489
/**
7590
* @param array<string, string|int|list<string|int>|null>|null $extraHeaders
7691
* @param array<string, mixed>|null $extraQueryParams
77-
* @param mixed|Omittable $extraBodyParams
92+
* @param mixed|Omit $extraBodyParams
7893
*/
79-
public function __construct(
94+
public static function with(
8095
?float $timeout = null,
8196
?int $maxRetries = null,
8297
?float $initialRetryDelay = null,
@@ -88,40 +103,117 @@ public function __construct(
88103
?UriFactoryInterface $uriFactory = null,
89104
?StreamFactoryInterface $streamFactory = null,
90105
?RequestFactoryInterface $requestFactory = null,
91-
) {
92-
$this->initialize();
106+
): self {
107+
$obj = new self;
108+
109+
null !== $timeout && $obj->timeout = $timeout;
110+
null !== $maxRetries && $obj->maxRetries = $maxRetries;
111+
null !== $initialRetryDelay && $obj->initialRetryDelay = $initialRetryDelay;
112+
null !== $maxRetryDelay && $obj->maxRetryDelay = $maxRetryDelay;
113+
null !== $extraHeaders && $obj->extraHeaders = $extraHeaders;
114+
null !== $extraQueryParams && $obj->extraQueryParams = $extraQueryParams;
115+
omit !== $extraBodyParams && $obj->extraBodyParams = $extraBodyParams;
116+
null !== $transporter && $obj->transporter = $transporter;
117+
null !== $uriFactory && $obj->uriFactory = $uriFactory;
118+
null !== $streamFactory && $obj->streamFactory = $streamFactory;
119+
null !== $requestFactory && $obj->requestFactory = $requestFactory;
120+
121+
return $obj;
122+
}
123+
124+
public function withTimeout(float $timeout): self
125+
{
126+
$obj = clone $this;
127+
$obj->timeout = $timeout;
128+
129+
return $obj;
130+
}
131+
132+
public function withMaxRetries(int $maxRetries): self
133+
{
134+
$obj = clone $this;
135+
$obj->maxRetries = $maxRetries;
136+
137+
return $obj;
138+
}
139+
140+
public function withInitialRetryDelay(float $initialRetryDelay): self
141+
{
142+
$obj = clone $this;
143+
$obj->initialRetryDelay = $initialRetryDelay;
93144

94-
null !== $timeout && $this->timeout = $timeout;
95-
null !== $maxRetries && $this->maxRetries = $maxRetries;
96-
null !== $initialRetryDelay && $this
97-
->initialRetryDelay = $initialRetryDelay
98-
;
99-
null !== $maxRetryDelay && $this->maxRetryDelay = $maxRetryDelay;
100-
null !== $extraHeaders && $this->extraHeaders = $extraHeaders;
101-
null !== $extraQueryParams && $this->extraQueryParams = $extraQueryParams;
102-
omit !== $extraBodyParams && $this->extraBodyParams = $extraBodyParams;
103-
null !== $transporter && $this->transporter = $transporter;
104-
null !== $uriFactory && $this->uriFactory = $uriFactory;
105-
null !== $streamFactory && $this->streamFactory = $streamFactory;
106-
null !== $requestFactory && $this->requestFactory = $requestFactory;
145+
return $obj;
146+
}
147+
148+
public function withMaxRetryDelay(float $maxRetryDelay): self
149+
{
150+
$obj = clone $this;
151+
$obj->maxRetryDelay = $maxRetryDelay;
152+
153+
return $obj;
107154
}
108155

109156
/**
110-
* @param request_opts|null $options
157+
* @param array<string, string|int|list<string|int>|null> $extraHeaders
158+
*/
159+
public function withExtraHeaders(array $extraHeaders): self
160+
{
161+
$obj = clone $this;
162+
$obj->extraHeaders = $extraHeaders;
163+
164+
return $obj;
165+
}
166+
167+
/**
168+
* @param array<string, mixed> $extraQueryParams
111169
*/
112-
public static function parse(RequestOptions|array|null $options): self
170+
public function withExtraQueryParams(array $extraQueryParams): self
171+
{
172+
$obj = clone $this;
173+
$obj->extraQueryParams = $extraQueryParams;
174+
175+
return $obj;
176+
}
177+
178+
public function withExtraBodyParams(mixed $extraBodyParams): self
179+
{
180+
$obj = clone $this;
181+
$obj->extraBodyParams = $extraBodyParams;
182+
183+
return $obj;
184+
}
185+
186+
public function withTransporter(ClientInterface $transporter): self
187+
{
188+
$obj = clone $this;
189+
$obj->transporter = $transporter;
190+
191+
return $obj;
192+
}
193+
194+
public function withUriFactory(UriFactoryInterface $uriFactory): self
113195
{
114-
if (is_null($options)) {
115-
return new self;
116-
}
196+
$obj = clone $this;
197+
$obj->uriFactory = $uriFactory;
117198

118-
if ($options instanceof self) {
119-
return $options;
120-
}
199+
return $obj;
200+
}
201+
202+
public function withStreamFactory(
203+
StreamFactoryInterface $streamFactory
204+
): self {
205+
$obj = clone $this;
206+
$obj->streamFactory = $streamFactory;
207+
208+
return $obj;
209+
}
121210

122-
$opts = new self;
123-
$opts->__unserialize($options);
211+
public function withRequestFactory(
212+
RequestFactoryInterface $requestFactory
213+
): self {
214+
$obj = clone $this;
215+
$obj->requestFactory = $requestFactory;
124216

125-
return $opts;
217+
return $obj;
126218
}
127219
}

0 commit comments

Comments
 (0)