99use Hypervel \HttpClient \PendingRequest as ClientPendingRequest ;
1010use Hypervel \HttpClient \Request ;
1111use Hypervel \Support \Facades \Http ;
12+ use Hypervel \Support \Pipeline ;
1213use Hypervel \Support \Traits \Conditionable ;
1314use InvalidArgumentException ;
1415use JsonSerializable ;
@@ -34,24 +35,30 @@ class PendingRequest
3435 protected array $ guzzleOptions = [];
3536
3637 /**
37- * @var array<RequestMiddleware |string>
38+ * @var array<callable|object |string>
3839 */
3940 protected array $ requestMiddleware = [];
4041
4142 /**
42- * @var array<ResponseMiddleware |string>
43+ * @var array<callable|object |string>
4344 */
4445 protected array $ responseMiddleware = [];
4546
4647 protected ?ClientPendingRequest $ request = null ;
4748
49+ protected Pipeline $ pipeline ;
50+
51+ protected static $ cachedMiddleware = [];
52+
4853 public function __construct (
4954 protected ApiClient $ client ,
55+ ?Pipeline $ pipeline = null ,
5056 ) {
5157 $ this ->resource = $ this ->client ->getResource ();
5258 $ this ->enableMiddleware = $ this ->client ->getEnableMiddleware ();
5359 $ this ->requestMiddleware = $ this ->client ->getRequestMiddleware ();
5460 $ this ->responseMiddleware = $ this ->client ->getResponseMiddleware ();
61+ $ this ->pipeline = $ pipeline ?? Pipeline::make ();
5562 }
5663
5764 /**
@@ -148,7 +155,7 @@ public function withResource(string $resource): static
148155 );
149156 }
150157
151- if (! is_subclass_of ($ resource , ApiResource::class)) {
158+ if (! is_subclass_of ($ resource , ApiResource::class)) { // @phpstan-ignore function.alreadyNarrowedType (validates PHPDoc contract at runtime)
152159 throw new InvalidArgumentException (
153160 sprintf ('Resource class `%s` must be a subclass of `%s` ' , $ resource , ApiResource::class)
154161 );
@@ -236,6 +243,14 @@ public function send(string $method, string $url, array $options = []): ApiResou
236243 return $ this ->sendRequest ('send ' , $ method , $ url , $ options );
237244 }
238245
246+ /**
247+ * Flush the cached middleware instances.
248+ */
249+ public function flushCache (): void
250+ {
251+ static ::$ cachedMiddleware = [];
252+ }
253+
239254 /**
240255 * Provide a dynamic method to pass calls to the pending request.
241256 */
@@ -255,17 +270,43 @@ protected function sendRequest(): ApiResource
255270 $ request = null ;
256271 $ response = $ this ->getClient ()
257272 ->beforeSending (function (Request $ httpRequest ) use (&$ request ) {
258- $ request = $ httpRequest ;
273+ $ request = new ApiRequest ($ httpRequest ->toPsrRequest ());
274+ if ($ this ->enableMiddleware ) {
275+ $ request = $ this ->handleMiddlewareRequest ($ request );
276+ }
277+ return $ request ->toPsrRequest ();
259278 })->{$ method }(...$ arguments );
260279
261280 if ($ response instanceof PromiseInterface) {
262281 throw new InvalidArgumentException ('Api client does not support async requests ' );
263282 }
264283
284+ $ response = new ApiResponse ($ response ->toPsrResponse ());
285+
286+ if ($ this ->enableMiddleware ) {
287+ $ response = $ this ->handleMiddlewareResponse ($ response );
288+ }
289+
265290 return $ this ->resource ::make ($ response , $ request );
266291 }
267292
268- protected function createMiddleware (array $ middlewareClasses , array $ options ): array
293+ protected function handleMiddlewareRequest (ApiRequest $ request ): ApiRequest
294+ {
295+ return $ this ->pipeline
296+ ->send ($ request ->withContext ('options ' , $ this ->middlewareOptions ))
297+ ->through ($ this ->createMiddleware ($ this ->requestMiddleware ))
298+ ->thenReturn ();
299+ }
300+
301+ protected function handleMiddlewareResponse (ApiResponse $ response ): ApiResponse
302+ {
303+ return $ this ->pipeline
304+ ->send ($ response ->withContext ('options ' , $ this ->middlewareOptions ))
305+ ->through ($ this ->createMiddleware ($ this ->responseMiddleware ))
306+ ->thenReturn ();
307+ }
308+
309+ protected function createMiddleware (array $ middlewareClasses ): array
269310 {
270311 $ middleware = [];
271312 foreach ($ middlewareClasses as $ value ) {
@@ -274,8 +315,11 @@ protected function createMiddleware(array $middlewareClasses, array $options): a
274315 sprintf ('Middleware class `%s` does not exist ' , $ value )
275316 );
276317 }
277-
278- $ middleware [] = new $ value ($ this ->client ->getConfig (), $ options );
318+ if ($ cache = static ::$ cachedMiddleware [$ value ] ?? null ) {
319+ $ middleware [] = $ cache ;
320+ continue ;
321+ }
322+ $ middleware [] = static ::$ cachedMiddleware [$ value ] = new $ value ($ this ->client ->getConfig ());
279323 }
280324
281325 return $ middleware ;
@@ -293,18 +337,6 @@ protected function getClient(): ClientPendingRequest
293337 $ request ->withOptions ($ this ->guzzleOptions );
294338 }
295339
296- if (! $ this ->enableMiddleware ) {
297- return $ request ;
298- }
299-
300- foreach ($ this ->createMiddleware ($ this ->requestMiddleware , $ this ->middlewareOptions ) as $ middleware ) {
301- $ request ->withRequestMiddleware ($ middleware );
302- }
303-
304- foreach ($ this ->createMiddleware ($ this ->responseMiddleware , $ this ->middlewareOptions ) as $ middleware ) {
305- $ request ->withResponseMiddleware ($ middleware );
306- }
307-
308340 return $ this ->request = $ request ;
309341 }
310342}
0 commit comments