Repository: codeigniter4/CodeIgniter4 (PHP)

Issue:
Bug: CURLRequest unable to set body in options.
### PHP Version

8.1

### CodeIgniter4 Version

4.2.10

### CodeIgniter4 Installation Method

Composer (using `codeigniter4/appstarter`)

### Which operating systems have you tested for this bug?

Linux

### Which server did you use?

fpm-fcgi

### Database

MariaDB 10.6.11

### What happened?

From the docs: https://codeigniter4.github.io/CodeIgniter4/libraries/curlrequest.html#body

This works: `$client->setBody($body)->request('put', 'http://example.com');`
This doesn't:` $client->request('put', 'http://example.com', ['body' => $body]);`

The latter doesn't seem to set the body and the request sends empty content.




### Steps to Reproduce

Create a simple CURL request and try to send data in the body using the options parameter, when debugging enabled, you can see the Content-Length is set to 0.





### Expected Output

Expect the request to send the data in the body.

### Anything else?

_No response_


Discussion:
Hello, with the following change

```php
$client->request('PUT', 'http://example.com', [
   'body' => $body,
   'http_errors' => false,
   'debug' => true,
]);
```
What are the contents of file **log**?

This bug has been fixed in `develop`.
Please try if you can.




Patch:
diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php
index 1e8a1aedbaad..9a15325a530c 100644
--- a/system/HTTP/CURLRequest.php
+++ b/system/HTTP/CURLRequest.php
@@ -293,6 +293,11 @@ protected function parseOptions(array $options)
             unset($options['delay']);
         }
 
+        if (array_key_exists('body', $options)) {
+            $this->setBody($options['body']);
+            unset($options['body']);
+        }
+
         foreach ($options as $key => $value) {
             $this->config[$key] = $value;
         }


Test patch:
diff --git a/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php b/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php
index 40864bb0a8f8..f3540379dd6c 100644
--- a/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php
+++ b/tests/system/HTTP/CURLRequestDoNotShareOptionsTest.php
@@ -809,6 +809,22 @@ public function testApplyBody()
         $this->assertSame('name=George', $request->curl_options[CURLOPT_POSTFIELDS]);
     }
 
+    public function testApplyBodyByOptions()
+    {
+        $request = $this->getRequest([
+            'base_uri' => 'http://www.foo.com/api/v1/',
+            'delay'    => 100,
+        ]);
+
+        $request->setOutput('Hi there');
+        $response = $request->post('answer', [
+            'body' => 'name=George',
+        ]);
+
+        $this->assertSame('Hi there', $response->getBody());
+        $this->assertSame('name=George', $request->curl_options[CURLOPT_POSTFIELDS]);
+    }
+
     public function testBodyIsResetOnSecondRequest()
     {
         $request = $this->getRequest([
diff --git a/tests/system/HTTP/CURLRequestTest.php b/tests/system/HTTP/CURLRequestTest.php
index 36ee88863534..4cf1a03944b5 100644
--- a/tests/system/HTTP/CURLRequestTest.php
+++ b/tests/system/HTTP/CURLRequestTest.php
@@ -792,6 +792,22 @@ public function testApplyBody()
         $this->assertSame('name=George', $request->curl_options[CURLOPT_POSTFIELDS]);
     }
 
+    public function testApplyBodyByOptions()
+    {
+        $request = $this->getRequest([
+            'base_uri' => 'http://www.foo.com/api/v1/',
+            'delay'    => 100,
+        ]);
+
+        $request->setOutput('Hi there');
+        $response = $request->post('answer', [
+            'body' => 'name=George',
+        ]);
+
+        $this->assertSame('Hi there', $response->getBody());
+        $this->assertSame('name=George', $request->curl_options[CURLOPT_POSTFIELDS]);
+    }
+
     public function testResponseHeaders()
     {
         $request = $this->getRequest([