diff --git a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php index 292cdf3945bcf..7d8409e7309f2 100644 --- a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php +++ b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpClient\DataCollector; +use Symfony\Component\HttpClient\HttpClientTrait; use Symfony\Component\HttpClient\TraceableHttpClient; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -23,6 +24,8 @@ */ final class HttpClientDataCollector extends DataCollector implements LateDataCollectorInterface { + use HttpClientTrait; + /** * @var TraceableHttpClient[] */ @@ -176,7 +179,7 @@ private function getCurlCommand(array $trace): ?string } $debug = explode("\n", $trace['info']['debug']); - $url = $trace['url']; + $url = self::mergeQueryString($trace['url'], $trace['options']['query'] ?? [], true); $command = ['curl', '--compressed']; if (isset($trace['options']['resolve'])) { @@ -196,8 +199,9 @@ private function getCurlCommand(array $trace): ?string if (\is_string($body)) { $dataArg[] = '--data '.escapeshellarg($body); } elseif (\is_array($body)) { - foreach ($body as $key => $value) { - $dataArg[] = '--data '.escapeshellarg("$key=$value"); + $body = explode('&', self::normalizeBody($body)); + foreach ($body as $value) { + $dataArg[] = '--data '.escapeshellarg(urldecode($value)); } } else { return null; diff --git a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php index ebe4c2c52569b..7d36c619f74cb 100755 --- a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php +++ b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php @@ -244,6 +244,21 @@ public function provideCurlRequests(): iterable 'foo' => 'fooval', 'bar' => 'barval', 'baz' => 'bazval', + 'foobar' => [ + 'baz' => 'bazval', + 'qux' => 'quxval', + ], + 'bazqux' => ['bazquxval1', 'bazquxval2'], + 'object' => (object) [ + 'fooprop' => 'foopropval', + 'barprop' => 'barpropval', + ], + 'tostring' => new class() { + public function __toString(): string + { + return 'tostringval'; + } + }, ], ], ], @@ -253,14 +268,37 @@ public function provideCurlRequests(): iterable --url %1$shttp://localhost:8057/json%1$s \\ --header %1$sAccept: */*%1$s \\ --header %1$sContent-Type: application/x-www-form-urlencoded%1$s \\ - --header %1$sContent-Length: 32%1$s \\ + --header %1$sContent-Length: 211%1$s \\ --header %1$sAccept-Encoding: gzip%1$s \\ --header %1$sUser-Agent: Symfony HttpClient/Native%1$s \\ - --data %1$sfoo=fooval%1$s --data %1$sbar=barval%1$s --data %1$sbaz=bazval%1$s', + --data %1$sfoo=fooval%1$s --data %1$sbar=barval%1$s --data %1$sbaz=bazval%1$s --data %1$sfoobar[baz]=bazval%1$s --data %1$sfoobar[qux]=quxval%1$s --data %1$sbazqux[0]=bazquxval1%1$s --data %1$sbazqux[1]=bazquxval2%1$s --data %1$sobject[fooprop]=foopropval%1$s --data %1$sobject[barprop]=barpropval%1$s --data %1$stostring=tostringval%1$s', ]; - // escapeshellarg on Windows replaces double quotes with spaces + // escapeshellarg on Windows replaces double quotes & percent signs with spaces if ('\\' !== \DIRECTORY_SEPARATOR) { + yield 'GET with query' => [ + [ + 'method' => 'GET', + 'url' => 'http://localhost:8057/?foo=fooval&bar=barval', + 'options' => [ + 'query' => [ + 'bar' => 'newbarval', + 'foobar' => [ + 'baz' => 'bazval', + 'qux' => 'quxval', + ], + 'bazqux' => ['bazquxval1', 'bazquxval2'], + ], + ], + ], + 'curl \\ + --compressed \\ + --request GET \\ + --url %1$shttp://localhost:8057/?foo=fooval&bar=newbarval&foobar%%5Bbaz%%5D=bazval&foobar%%5Bqux%%5D=quxval&bazqux%%5B0%%5D=bazquxval1&bazqux%%5B1%%5D=bazquxval2%1$s \\ + --header %1$sAccept: */*%1$s \\ + --header %1$sAccept-Encoding: gzip%1$s \\ + --header %1$sUser-Agent: Symfony HttpClient/Native%1$s', + ]; yield 'POST with json' => [ [ 'method' => 'POST',