[WebProfiler] add cURL copy/paste to request tab

This commit is contained in:
darkweak
2025-11-05 20:31:04 +01:00
committed by Nicolas Grekas
parent f8b6946fff
commit 162f45c761
2 changed files with 167 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Process\Process;
use Symfony\Component\VarDumper\Cloner\Data;
/**
@@ -130,6 +131,8 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
$this->data['content'] = $content;
$this->data['curlCommand'] = $this->computeCurlCommand($request, $content);
foreach ($this->data as $key => $value) {
if (!\is_array($value)) {
continue;
@@ -495,4 +498,61 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
return \is_string($controller) ? $controller : 'n/a';
}
private function computeCurlCommand(Request $request, ?string $content): string
{
$command = ['curl', '--compressed'];
$method = $request->getMethod();
if (Request::METHOD_HEAD === $method) {
$command[] = '--head';
} elseif (Request::METHOD_GET !== $method) {
$command[] = \sprintf('--request %s', $method);
}
$command[] = \sprintf('--url %s', escapeshellarg($request->getUri()));
foreach ($request->headers->all() as $name => $values) {
if (\in_array(strtolower($name), ['host', 'cookie'], true)) {
continue;
}
$command[] = '--header '.escapeshellarg(ucwords($name, '-').': '.implode(', ', $values));
}
if ($request->cookies->all()) {
$cookies = [];
foreach ($request->cookies->all() as $name => $value) {
$cookies[] = urlencode($name).'='.urlencode($value);
}
$command[] = '--cookie '.escapeshellarg(implode('; ', $cookies));
}
if ($content && \in_array($method, [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE], true)) {
$command[] = '--data-raw '.$this->escapePayload($content);
}
return implode(" \\\n ", $command);
}
public function getCurlCommand(): string
{
return $this->data['curlCommand'] ?? '';
}
private function escapePayload(string $payload): string
{
static $useProcess;
if ($useProcess ??= \function_exists('proc_open') && class_exists(Process::class)) {
return substr((new Process(['', $payload]))->getCommandLine(), 3);
}
if ('\\' === \DIRECTORY_SEPARATOR) {
return '"'.str_replace('"', '""', $payload).'"';
}
return "'".str_replace("'", "'\\''", $payload)."'";
}
}

View File

@@ -462,4 +462,111 @@ class RequestDataCollectorTest extends TestCase
['', null],
];
}
public function testCurlCommandGet()
{
$request = Request::create('http://test.com/foo?bar=baz');
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertStringStartsWith("curl \\\n --compressed", $curlCommand);
$this->assertStringContainsString("--url 'http://test.com/foo?bar=baz'", $curlCommand);
$this->assertStringNotContainsString('--request', $curlCommand);
}
public function testCurlCommandPost()
{
$request = Request::create('http://test.com/foo', 'POST', [], [], [], [], '{"key":"value"}');
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertStringContainsString('--request POST', $curlCommand);
$this->assertStringContainsString('--data-raw', $curlCommand);
$this->assertStringContainsString('{"key":"value"}', $curlCommand);
}
public function testCurlCommandHead()
{
$request = Request::create('http://test.com/foo', 'HEAD');
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertStringContainsString('--head', $curlCommand);
$this->assertStringNotContainsString('--request', $curlCommand);
}
public function testCurlCommandWithHeaders()
{
$request = Request::create('http://test.com/foo');
$request->headers->set('Accept', 'application/json');
$request->headers->set('X-Custom-Header', 'custom-value');
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertStringContainsString("--header 'Accept: application/json'", $curlCommand);
$this->assertStringContainsString("--header 'X-Custom-Header: custom-value'", $curlCommand);
$this->assertStringNotContainsString('Host:', $curlCommand);
}
public function testCurlCommandWithCookies()
{
$request = Request::create('http://test.com/foo', 'GET', [], ['session' => 'abc123', 'lang' => 'en']);
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertStringContainsString('--cookie', $curlCommand);
$this->assertStringContainsString('session=abc123', $curlCommand);
$this->assertStringContainsString('lang=en', $curlCommand);
}
public function testCurlCommandPutWithBody()
{
$request = Request::create('http://test.com/resource/1', 'PUT', [], [], [], [], 'updated data');
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertStringContainsString('--request PUT', $curlCommand);
$this->assertStringContainsString('--data-raw', $curlCommand);
}
public function testCurlCommandDoesNotDuplicateQueryString()
{
$request = Request::create('http://test.com/path?foo=bar&baz=qux');
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertSame(1, substr_count($curlCommand, 'foo=bar'));
$this->assertSame(1, substr_count($curlCommand, 'baz=qux'));
}
public function testCurlCommandGetWithNoBody()
{
$request = Request::create('http://test.com/foo', 'GET');
$c = new RequestDataCollector();
$c->collect($request, $this->createResponse());
$curlCommand = $c->getCurlCommand();
$this->assertStringNotContainsString('--data-raw', $curlCommand);
}
public function testCurlCommandIsEmptyStringWhenNotCollected()
{
$c = new RequestDataCollector();
$this->assertSame('', $c->getCurlCommand());
}
}