Repository: laravel/framework (php)

Issue:
[8.45.1] Cloned PendingRequest raises a "($request) must be of type Illuminate\Http\Client\Request, null given" error
<!-- DO NOT THROW THIS AWAY -->
<!-- Fill out the FULL versions with patch versions -->

- Laravel Version: 8.45.1
- PHP Version: 8.x
- Database Driver & Version: 
- Guzzle version: 7.3.0

### Description:

I notice that after to upgrade to 8.45.1 an error is raised when I attempt to perform a request in a cloned PendingRequest instance. I observed that this error is not raised with version 8.44.0.

The error raised is:

`TypeError: Illuminate\Http\Client\Events\ResponseReceived::__construct(): Argument #1 ($request) must be of type Illuminate\Http\Client\Request, null given, called in /home/vagrant/foobar/code/vendor/laravel/framework/src/Illuminate/Http/Client/PendingRequest.php on line 989`


The error is raised when the method "dispatchResponseReceivedEvent" is called and I observed that "$this->request" is null.

### Steps To Reproduce:

- Upgrade to Laravel 8.45.1
- Run the following code (for example into Tinker):

```
$client = \Http::timeout(10);
$newClient = clone $client;
$newClient->get('https://google.com');
```

I supect that this bug was introduced in https://github.com/laravel/framework/commit/065e130a0437423c3279a4aad9e3d32ea70a16f3


Pull request:
[8.x] Bugfix for cloning issues with PendingRequest object
Fixes #37594

This was caused by the closures registered in the `beforeSendingCallbacks` referencing the original instance in memory. These callbacks now receive the current instance as the third parameter, which allows them to set the provided request and cookies on the correct object instance.

Patch:
diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php
index c83fb95202..8edbcee179 100644
--- a/src/Illuminate/Http/Client/PendingRequest.php
+++ b/src/Illuminate/Http/Client/PendingRequest.php
@@ -137,7 +137,7 @@ class PendingRequest
      *
      * @var \Illuminate\Http\Client\Request|null
      */
-    protected $request;
+    protected $request = null;
 
     /**
      * Create a new HTTP Client instance.
@@ -156,11 +156,11 @@ public function __construct(Factory $factory = null)
             'http_errors' => false,
         ];
 
-        $this->beforeSendingCallbacks = collect([function (Request $request, array $options) {
-            $this->request = $request;
-            $this->cookies = $options['cookies'];
+        $this->beforeSendingCallbacks = collect([function (Request $request, array $options, PendingRequest $instance) {
+            $instance->request = $request;
+            $instance->cookies = $options['cookies'];
 
-            $this->dispatchRequestSendingEvent();
+            $instance->dispatchRequestSendingEvent();
         }]);
     }
 
@@ -913,7 +913,8 @@ public function runBeforeSendingCallbacks($request, array $options)
         return tap($request, function ($request) use ($options) {
             $this->beforeSendingCallbacks->each->__invoke(
                 (new Request($request))->withData($options['laravel_data']),
-                $options
+                $options,
+                $this
             );
         });
     }
@@ -985,9 +986,15 @@ protected function dispatchRequestSendingEvent()
      */
     protected function dispatchResponseReceivedEvent(Response $response)
     {
-        if ($dispatcher = optional($this->factory)->getDispatcher()) {
-            $dispatcher->dispatch(new ResponseReceived($this->request, $response));
+        if (! $dispatcher = optional($this->factory)->getDispatcher()) {
+            return;
+        }
+
+        if (! $this->request) {
+            return;
         }
+
+        $dispatcher->dispatch(new ResponseReceived($this->request, $response));
     }
 
     /**


Test patch:
diff --git a/tests/Http/HttpClientTest.php b/tests/Http/HttpClientTest.php
index 746e5ece7f..85050c9b81 100644
--- a/tests/Http/HttpClientTest.php
+++ b/tests/Http/HttpClientTest.php
@@ -939,4 +939,20 @@ public function testTheRequestSendingAndResponseReceivedEventsAreFiredWhenAReque
 
         m::close();
     }
+
+    public function testClonedClientsWorkSuccessfullyWithTheRequestObject()
+    {
+        $events = m::mock(Dispatcher::class);
+        $events->shouldReceive('dispatch')->once()->with(m::type(RequestSending::class));
+        $events->shouldReceive('dispatch')->once()->with(m::type(ResponseReceived::class));
+
+        $factory = new Factory($events);
+
+        $client = $factory->timeout(10);
+        $clonedClient = clone $client;
+
+        $clonedClient->get('https://example.com');
+
+        m::close();
+    }
 }