Skip to content

Commit

Permalink
[10.x] Fix Stringable objects not converted to string in HTTP facade …
Browse files Browse the repository at this point in the history
…Query parameters and Body (#48849)

* Update PendingRequest.php

* Add Test

* Fix tests and code

* Minor tidy

* Support for nested arrays of Stringable + tests

* refactor

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
LasseRafn and taylorotwell authored Oct 30, 2023
1 parent 0aeecec commit 0294be7
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
24 changes: 23 additions & 1 deletion src/Illuminate/Http/Client/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\Support\Stringable;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use JsonSerializable;
Expand Down Expand Up @@ -1035,10 +1036,12 @@ protected function sendRequest(string $method, string $url, array $options = [])
$this->transferStats = $transferStats;
};

return $this->buildClient()->$clientMethod($method, $url, $this->mergeOptions([
$mergedOptions = $this->normalizeRequestOptions($this->mergeOptions([
'laravel_data' => $laravelData,
'on_stats' => $onStats,
], $options));

return $this->buildClient()->$clientMethod($method, $url, $mergedOptions);
}

/**
Expand Down Expand Up @@ -1076,6 +1079,25 @@ protected function parseRequestData($method, $url, array $options)
return is_array($laravelData) ? $laravelData : [];
}

/**
* Normalize the given request options.
*
* @param array $options
* @return array
*/
protected function normalizeRequestOptions(array $options)
{
foreach ($options as $key => $value) {
$options[$key] = match (true) {
is_array($value) => $this->normalizeRequestOptions($value),
$value instanceof Stringable => $value->toString(),
default => $value,
};
}

return $options;
}

/**
* Populate the given response with additional data.
*
Expand Down
77 changes: 77 additions & 0 deletions tests/Http/HttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,57 @@ public function toArray(): array
});
}

public function testCanSendJsonDataWithStringable()
{
$this->factory->fake();

$this->factory->withHeaders([
'X-Test-Header' => 'foo',
'X-Test-ArrayHeader' => ['bar', 'baz'],
])->post('http://foo.com/json', [
'name' => Str::of('Taylor'),
]);

$this->factory->assertSent(function (Request $request) {
return $request->url() === 'http://foo.com/json' &&
$request->hasHeader('Content-Type', 'application/json') &&
$request->hasHeader('X-Test-Header', 'foo') &&
$request->hasHeader('X-Test-ArrayHeader', ['bar', 'baz']) &&
$request['name'] === 'Taylor';
});
}

public function testCanSendFormDataWithStringable()
{
$this->factory->fake();

$this->factory->asForm()->post('http://foo.com/form', [
'name' => Str::of('Taylor'),
'title' => 'Laravel Developer',
]);

$this->factory->assertSent(function (Request $request) {
return $request->url() === 'http://foo.com/form' &&
$request->hasHeader('Content-Type', 'application/x-www-form-urlencoded') &&
$request['name'] === 'Taylor';
});
}

public function testCanSendFormDataWithStringableInArrays()
{
$this->factory->fake();

$this->factory->asForm()->post('http://foo.com/form', [
'posts' => [['title' => Str::of('Taylor')]],
]);

$this->factory->assertSent(function (Request $request) {
return $request->url() === 'http://foo.com/form' &&
$request->hasHeader('Content-Type', 'application/x-www-form-urlencoded') &&
$request['posts'][0]['title'] === 'Taylor';
});
}

public function testRecordedCallsAreEmptiedWhenFakeIsCalled()
{
$this->factory->fake([
Expand Down Expand Up @@ -800,6 +851,32 @@ public function testWithQueryParametersAllowsOverridingParameterOnRequest()
});
}

public function testWithStringableQueryParameters()
{
$this->factory->fake();

$this->factory->withQueryParameters(
['foo' => Str::of('bar'),]
)->get('https://laravel.com');

$this->factory->assertSent(function (Request $request) {
return $request->url() === 'https://laravel.com?foo=bar';
});
}

public function testWithArrayStringableQueryParameters()
{
$this->factory->fake();

$this->factory->withQueryParameters(
['foo' => ['bar', Str::of('baz')]],
)->get('https://laravel.com');

$this->factory->assertSent(function (Request $request) {
return $request->url() === 'https://laravel.com?foo%5B0%5D=bar&foo%5B1%5D=baz';
});
}

public function testGetWithArrayQueryParam()
{
$this->factory->fake();
Expand Down

0 comments on commit 0294be7

Please sign in to comment.