20
20
* method: string,
21
21
* uri: string,
22
22
* headers: array<string, list<string>>,
23
- * body ?: string|null
23
+ * data ?: string|array<string, mixed> |null
24
24
* }
25
25
*
26
26
* @extends AbstractDataTransferObject<RequestArrayShape>
@@ -30,7 +30,7 @@ class Request extends AbstractDataTransferObject
30
30
public const KEY_METHOD = 'method ' ;
31
31
public const KEY_URI = 'uri ' ;
32
32
public const KEY_HEADERS = 'headers ' ;
33
- public const KEY_BODY = 'body ' ;
33
+ public const KEY_DATA = 'data ' ;
34
34
35
35
/**
36
36
* @var HttpMethodEnum The HTTP method.
@@ -48,9 +48,9 @@ class Request extends AbstractDataTransferObject
48
48
protected array $ headers ;
49
49
50
50
/**
51
- * @var string|null The request body .
51
+ * @var string|array<string, mixed>| null The request data .
52
52
*/
53
- protected ? string $ body ;
53
+ protected $ data ;
54
54
55
55
/**
56
56
* Constructor.
@@ -60,11 +60,11 @@ class Request extends AbstractDataTransferObject
60
60
* @param HttpMethodEnum $method The HTTP method.
61
61
* @param string $uri The request URI.
62
62
* @param array<string, string|list<string>> $headers The request headers.
63
- * @param string|null $body The request body .
63
+ * @param string|array<string, mixed>| null $data The request data .
64
64
*
65
65
* @throws InvalidArgumentException If the URI is empty.
66
66
*/
67
- public function __construct (HttpMethodEnum $ method , string $ uri , array $ headers = [], ? string $ body = null )
67
+ public function __construct (HttpMethodEnum $ method , string $ uri , array $ headers = [], $ data = null )
68
68
{
69
69
if (empty ($ uri )) {
70
70
throw new InvalidArgumentException ('URI cannot be empty. ' );
@@ -73,7 +73,7 @@ public function __construct(HttpMethodEnum $method, string $uri, array $headers
73
73
$ this ->method = $ method ;
74
74
$ this ->uri = $ uri ;
75
75
$ this ->headers = $ this ->normalizeHeaders ($ headers );
76
- $ this ->body = $ body ;
76
+ $ this ->data = $ data ;
77
77
}
78
78
79
79
/**
@@ -91,12 +91,20 @@ public function getMethod(): HttpMethodEnum
91
91
/**
92
92
* Gets the request URI.
93
93
*
94
+ * For GET requests with array data, appends the data as query parameters.
95
+ *
94
96
* @since n.e.x.t
95
97
*
96
98
* @return string The URI.
97
99
*/
98
100
public function getUri (): string
99
101
{
102
+ // If GET request with array data, append as query parameters
103
+ if ($ this ->method === HttpMethodEnum::GET () && is_array ($ this ->data ) && !empty ($ this ->data )) {
104
+ $ separator = strpos ($ this ->uri , '? ' ) === false ? '? ' : '& ' ;
105
+ return $ this ->uri . $ separator . http_build_query ($ this ->data );
106
+ }
107
+
100
108
return $ this ->uri ;
101
109
}
102
110
@@ -115,13 +123,65 @@ public function getHeaders(): array
115
123
/**
116
124
* Gets the request body.
117
125
*
126
+ * For GET requests, returns null.
127
+ * For POST/PUT/PATCH requests:
128
+ * - If data is a string, returns it as-is
129
+ * - If data is an array and Content-Type is JSON, returns JSON-encoded data
130
+ * - If data is an array and Content-Type is form, returns URL-encoded data
131
+ *
118
132
* @since n.e.x.t
119
133
*
120
134
* @return string|null The body.
135
+ * @throws JsonException If the data cannot be encoded to JSON.
121
136
*/
122
137
public function getBody (): ?string
123
138
{
124
- return $ this ->body ;
139
+ // GET requests don't have a body
140
+ if ($ this ->method === HttpMethodEnum::GET ()) {
141
+ return null ;
142
+ }
143
+
144
+ // If data is null, return null
145
+ if ($ this ->data === null ) {
146
+ return null ;
147
+ }
148
+
149
+ // If data is already a string, return it as-is
150
+ if (is_string ($ this ->data )) {
151
+ return $ this ->data ;
152
+ }
153
+
154
+ // If data is an array, encode based on content type
155
+ if (is_array ($ this ->data )) {
156
+ $ contentType = $ this ->getContentType ();
157
+
158
+ // JSON encoding
159
+ if ($ contentType !== null && stripos ($ contentType , 'application/json ' ) !== false ) {
160
+ return json_encode ($ this ->data , JSON_THROW_ON_ERROR );
161
+ }
162
+
163
+ // Default to URL encoding for forms
164
+ return http_build_query ($ this ->data );
165
+ }
166
+
167
+ return null ;
168
+ }
169
+
170
+ /**
171
+ * Gets the Content-Type header value.
172
+ *
173
+ * @since n.e.x.t
174
+ *
175
+ * @return string|null The Content-Type header value or null if not set.
176
+ */
177
+ private function getContentType (): ?string
178
+ {
179
+ foreach ($ this ->headers as $ name => $ values ) {
180
+ if (strcasecmp ($ name , 'Content-Type ' ) === 0 ) {
181
+ return $ values [0 ] ?? null ;
182
+ }
183
+ }
184
+ return null ;
125
185
}
126
186
127
187
/**
@@ -138,20 +198,20 @@ public function withHeader(string $name, $value): self
138
198
$ headers = $ this ->headers ;
139
199
$ headers [$ name ] = is_array ($ value ) ? array_values ($ value ) : [$ value ];
140
200
141
- return new self ($ this ->method , $ this ->uri , $ headers , $ this ->body );
201
+ return new self ($ this ->method , $ this ->uri , $ headers , $ this ->data );
142
202
}
143
203
144
204
/**
145
- * Returns a new instance with the specified body .
205
+ * Returns a new instance with the specified data .
146
206
*
147
207
* @since n.e.x.t
148
208
*
149
- * @param string $body The request body .
150
- * @return self A new instance with the body .
209
+ * @param string|array<string, mixed> $data The request data .
210
+ * @return self A new instance with the data .
151
211
*/
152
- public function withBody ( string $ body ): self
212
+ public function withData ( $ data ): self
153
213
{
154
- return new self ($ this ->method , $ this ->uri , $ this ->headers , $ body );
214
+ return new self ($ this ->method , $ this ->uri , $ this ->headers , $ data );
155
215
}
156
216
157
217
/**
@@ -172,29 +232,15 @@ private function normalizeHeaders(array $headers): array
172
232
}
173
233
174
234
/**
175
- * Gets the request data as an array.
176
- *
177
- * Attempts to decode the body as JSON. Returns null if the body
178
- * is empty or not valid JSON.
235
+ * Gets the request data.
179
236
*
180
237
* @since n.e.x.t
181
238
*
182
- * @return array<string, mixed>|null The decoded data or null .
239
+ * @return string| array<string, mixed>|null The request data.
183
240
*/
184
- public function getData (): ? array
241
+ public function getData ()
185
242
{
186
- if ($ this ->body === null || $ this ->body === '' ) {
187
- return null ;
188
- }
189
-
190
- $ data = json_decode ($ this ->body , true );
191
-
192
- if (json_last_error () !== JSON_ERROR_NONE ) {
193
- return null ;
194
- }
195
-
196
- /** @var array<string, mixed>|null $data */
197
- return is_array ($ data ) ? $ data : null ;
243
+ return $ this ->data ;
198
244
}
199
245
200
246
/**
@@ -223,9 +269,9 @@ public static function getJsonSchema(): array
223
269
],
224
270
'description ' => 'The request headers. ' ,
225
271
],
226
- self ::KEY_BODY => [
227
- 'type ' => ['string ' , 'null ' ],
228
- 'description ' => 'The request body . ' ,
272
+ self ::KEY_DATA => [
273
+ 'type ' => ['string ' , 'array ' , ' null ' ],
274
+ 'description ' => 'The request data . ' ,
229
275
],
230
276
],
231
277
'required ' => [self ::KEY_METHOD , self ::KEY_URI , self ::KEY_HEADERS ],
@@ -241,17 +287,17 @@ public static function getJsonSchema(): array
241
287
*/
242
288
public function toArray (): array
243
289
{
244
- $ data = [
290
+ $ array = [
245
291
self ::KEY_METHOD => $ this ->method ->value ,
246
292
self ::KEY_URI => $ this ->uri ,
247
293
self ::KEY_HEADERS => $ this ->headers ,
248
294
];
249
295
250
- if ($ this ->body !== null ) {
251
- $ data [self ::KEY_BODY ] = $ this ->body ;
296
+ if ($ this ->data !== null ) {
297
+ $ array [self ::KEY_DATA ] = $ this ->data ;
252
298
}
253
299
254
- return $ data ;
300
+ return $ array ;
255
301
}
256
302
257
303
/**
@@ -267,7 +313,7 @@ public static function fromArray(array $array): self
267
313
HttpMethodEnum::from ($ array [self ::KEY_METHOD ]),
268
314
$ array [self ::KEY_URI ],
269
315
$ array [self ::KEY_HEADERS ] ?? [],
270
- $ array [self ::KEY_BODY ] ?? null
316
+ $ array [self ::KEY_DATA ] ?? null
271
317
);
272
318
}
273
319
}
0 commit comments