Skip to content

Commit e74bd5b

Browse files
Merge branch '6.1' into 6.2
* 6.1: [HttpClient] Copy as curl fixes [Messenger] Remove redundant interface in DoctrineReceiver [FrameworkBundle] Disable Serializer data collect by default [HttpClient][WebProfilerBundle] Catch errors when encoding body for curl command line [Routing] Fix $requirements PHPDoc for SCA Fix choice filter error when loading mix of grouped and non-grouped choices [Mime] Allow url as a path in the DataPart::fromPath Fix getting class constraints on debug command
2 parents aaa821b + 846bc96 commit e74bd5b

File tree

23 files changed

+315
-78
lines changed

23 files changed

+315
-78
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CHANGELOG
1515
* Add tag `routing.condition_service` to autoconfigure routing condition services
1616
* Automatically register kernel methods marked with the `Symfony\Component\Routing\Annotation\Route` attribute or annotation as controllers in `MicroKernelTrait`
1717
* Deprecate not setting the `http_method_override` config option. The default value will change to `false` in 7.0.
18+
* Add `framework.profiler.collect_serializer_data` config option, set it to `true` to enable the serializer data collector and profiler panel
1819

1920
6.0
2021
---

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode)
325325
->booleanNode('only_exceptions')->defaultFalse()->end()
326326
->booleanNode('only_main_requests')->defaultFalse()->end()
327327
->scalarNode('dsn')->defaultValue('file:%kernel.cache_dir%/profiler')->end()
328+
->booleanNode('collect_serializer_data')->info('Enables the serializer data collector and profiler panel')->defaultFalse()->end()
328329
->end()
329330
->end()
330331
->end()

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,7 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
826826
$loader->load('notifier_debug.php');
827827
}
828828

829-
if ($this->serializerConfigEnabled) {
829+
if ($this->serializerConfigEnabled && $config['collect_serializer_data']) {
830830
$loader->load('serializer_debug.php');
831831
}
832832

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
<xsd:attribute name="username" type="xsd:string" />
104104
<xsd:attribute name="password" type="xsd:string" />
105105
<xsd:attribute name="lifetime" type="xsd:string" />
106+
<xsd:attribute name="collect-serializer-data" type="xsd:boolean" />
106107
</xsd:complexType>
107108

108109
<xsd:complexType name="router">

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ protected static function getBundleDefaultConfig()
468468
'dsn' => 'file:%kernel.cache_dir%/profiler',
469469
'collect' => true,
470470
'collect_parameter' => null,
471+
'collect_serializer_data' => false,
471472
],
472473
'translator' => [
473474
'enabled' => !class_exists(FullStack::class),

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/profiler.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
'profiler' => [
66
'enabled' => true,
77
],
8+
'serializer' => [
9+
'enabled' => true
10+
],
811
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
$container->loadFromExtension('framework', [
4+
'http_method_override' => false,
5+
'profiler' => [
6+
'enabled' => true,
7+
'collect_serializer_data' => true,
8+
],
9+
'serializer' => [
10+
'enabled' => true,
11+
]
12+
]);

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88

99
<framework:config http-method-override="false">
1010
<framework:profiler enabled="true" />
11+
<framework:serializer enabled="true" />
1112
</framework:config>
1213
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:framework="http://symfony.com/schema/dic/symfony"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
8+
9+
<framework:config http-method-override="false">
10+
<framework:profiler enabled="true" collect-serializer-data="true" />
11+
<framework:serializer enabled="true" />
12+
</framework:config>
13+
</container>

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ framework:
22
http_method_override: false
33
profiler:
44
enabled: true
5+
serializer:
6+
enabled: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
framework:
2+
http_method_override: false
3+
serializer:
4+
enabled: true
5+
profiler:
6+
enabled: true
7+
collect_serializer_data: true

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,24 @@ public function testDisabledProfiler()
253253
$this->assertFalse($container->hasDefinition('data_collector.config'), '->registerProfilerConfiguration() does not load collectors.xml');
254254
}
255255

256+
public function testProfilerCollectSerializerDataEnabled()
257+
{
258+
$container = $this->createContainerFromFile('profiler_collect_serializer_data');
259+
260+
$this->assertTrue($container->hasDefinition('profiler'));
261+
$this->assertTrue($container->hasDefinition('serializer.data_collector'));
262+
$this->assertTrue($container->hasDefinition('debug.serializer'));
263+
}
264+
265+
public function testProfilerCollectSerializerDataDefaultDisabled()
266+
{
267+
$container = $this->createContainerFromFile('profiler');
268+
269+
$this->assertTrue($container->hasDefinition('profiler'));
270+
$this->assertFalse($container->hasDefinition('serializer.data_collector'));
271+
$this->assertFalse($container->hasDefinition('debug.serializer'));
272+
}
273+
256274
public function testWorkflows()
257275
{
258276
$container = $this->createContainerFromFile('workflows');

src/Symfony/Component/Form/ChoiceList/Loader/FilterChoiceLoaderDecorator.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,17 @@ protected function loadChoices(): iterable
3636
}
3737

3838
foreach ($structuredValues as $group => $values) {
39-
if ($values && $filtered = array_filter($list->getChoicesForValues($values), $this->filter)) {
40-
$choices[$group] = $filtered;
39+
if (is_array($values)) {
40+
if ($values && $filtered = array_filter($list->getChoicesForValues($values), $this->filter)) {
41+
$choices[$group] = $filtered;
42+
}
43+
continue;
44+
// filter empty groups
45+
}
46+
47+
if ($filtered = array_filter($list->getChoicesForValues([$values]), $this->filter)) {
48+
$choices[$group] = $filtered[0];
4149
}
42-
// filter empty groups
4350
}
4451

4552
return $choices ?? [];

src/Symfony/Component/Form/Tests/ChoiceList/Loader/FilterChoiceLoaderDecoratorTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ public function testLoadChoiceListWithGroupedChoices()
4747
]), $loader->loadChoiceList());
4848
}
4949

50+
public function testLoadChoiceListMixedWithGroupedAndNonGroupedChoices()
51+
{
52+
$filter = function ($choice) {
53+
return 0 === $choice % 2;
54+
};
55+
56+
$choices = array_merge(range(1, 9), ['grouped' => range(10, 40, 5)]);
57+
$loader = new FilterChoiceLoaderDecorator(new ArrayChoiceLoader($choices), $filter);
58+
59+
$this->assertEquals(new ArrayChoiceList([
60+
1 => 2,
61+
3 => 4,
62+
5 => 6,
63+
7 => 8,
64+
'grouped' => [
65+
0 => 10,
66+
2 => 20,
67+
4 => 30,
68+
6 => 40,
69+
],
70+
]), $loader->loadChoiceList());
71+
}
72+
5073
public function testLoadValuesForChoices()
5174
{
5275
$evenValues = [1 => '2', 3 => '4'];

src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\HttpClient\DataCollector;
1313

14+
use Symfony\Component\HttpClient\HttpClientTrait;
1415
use Symfony\Component\HttpClient\TraceableHttpClient;
1516
use Symfony\Component\HttpFoundation\Request;
1617
use Symfony\Component\HttpFoundation\Response;
@@ -23,6 +24,8 @@
2324
*/
2425
final class HttpClientDataCollector extends DataCollector implements LateDataCollectorInterface
2526
{
27+
use HttpClientTrait;
28+
2629
/**
2730
* @var TraceableHttpClient[]
2831
*/
@@ -176,7 +179,7 @@ private function getCurlCommand(array $trace): ?string
176179
}
177180

178181
$debug = explode("\n", $trace['info']['debug']);
179-
$url = $trace['url'];
182+
$url = self::mergeQueryString($trace['url'], $trace['options']['query'] ?? [], true);
180183
$command = ['curl', '--compressed'];
181184

182185
if (isset($trace['options']['resolve'])) {
@@ -194,10 +197,15 @@ private function getCurlCommand(array $trace): ?string
194197
$dataArg[] = '--data '.escapeshellarg(json_encode($json, \JSON_PRETTY_PRINT));
195198
} elseif ($body = $trace['options']['body'] ?? null) {
196199
if (\is_string($body)) {
197-
$dataArg[] = '--data '.escapeshellarg($body);
200+
try {
201+
$dataArg[] = '--data '.escapeshellarg($body);
202+
} catch (\ValueError $e) {
203+
return null;
204+
}
198205
} elseif (\is_array($body)) {
199-
foreach ($body as $key => $value) {
200-
$dataArg[] = '--data '.escapeshellarg("$key=$value");
206+
$body = explode('&', self::normalizeBody($body));
207+
foreach ($body as $value) {
208+
$dataArg[] = '--data '.escapeshellarg(urldecode($value));
201209
}
202210
} else {
203211
return null;

src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,21 @@ public function provideCurlRequests(): iterable
244244
'foo' => 'fooval',
245245
'bar' => 'barval',
246246
'baz' => 'bazval',
247+
'foobar' => [
248+
'baz' => 'bazval',
249+
'qux' => 'quxval',
250+
],
251+
'bazqux' => ['bazquxval1', 'bazquxval2'],
252+
'object' => (object) [
253+
'fooprop' => 'foopropval',
254+
'barprop' => 'barpropval',
255+
],
256+
'tostring' => new class() {
257+
public function __toString(): string
258+
{
259+
return 'tostringval';
260+
}
261+
},
247262
],
248263
],
249264
],
@@ -253,14 +268,37 @@ public function provideCurlRequests(): iterable
253268
--url %1$shttp://localhost:8057/json%1$s \\
254269
--header %1$sAccept: */*%1$s \\
255270
--header %1$sContent-Type: application/x-www-form-urlencoded%1$s \\
256-
--header %1$sContent-Length: 32%1$s \\
271+
--header %1$sContent-Length: 211%1$s \\
257272
--header %1$sAccept-Encoding: gzip%1$s \\
258273
--header %1$sUser-Agent: Symfony HttpClient/Native%1$s \\
259-
--data %1$sfoo=fooval%1$s --data %1$sbar=barval%1$s --data %1$sbaz=bazval%1$s',
274+
--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',
260275
];
261276

262-
// escapeshellarg on Windows replaces double quotes with spaces
277+
// escapeshellarg on Windows replaces double quotes & percent signs with spaces
263278
if ('\\' !== \DIRECTORY_SEPARATOR) {
279+
yield 'GET with query' => [
280+
[
281+
'method' => 'GET',
282+
'url' => 'http://localhost:8057/?foo=fooval&bar=barval',
283+
'options' => [
284+
'query' => [
285+
'bar' => 'newbarval',
286+
'foobar' => [
287+
'baz' => 'bazval',
288+
'qux' => 'quxval',
289+
],
290+
'bazqux' => ['bazquxval1', 'bazquxval2'],
291+
],
292+
],
293+
],
294+
'curl \\
295+
--compressed \\
296+
--request GET \\
297+
--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 \\
298+
--header %1$sAccept: */*%1$s \\
299+
--header %1$sAccept-Encoding: gzip%1$s \\
300+
--header %1$sUser-Agent: Symfony HttpClient/Native%1$s',
301+
];
264302
yield 'POST with json' => [
265303
[
266304
'method' => 'POST',
@@ -343,6 +381,28 @@ public function testItDoesNotGeneratesCurlCommandsForUnsupportedBodyType()
343381
self::assertNull($curlCommand);
344382
}
345383

384+
/**
385+
* @requires extension openssl
386+
*/
387+
public function testItDoesNotGeneratesCurlCommandsForNotEncodableBody()
388+
{
389+
$sut = new HttpClientDataCollector();
390+
$sut->registerClient('http_client', $this->httpClientThatHasTracedRequests([
391+
[
392+
'method' => 'POST',
393+
'url' => 'http://localhost:8057/json',
394+
'options' => [
395+
'body' => "\0",
396+
],
397+
],
398+
]));
399+
$sut->collect(new Request(), new Response());
400+
$collectedData = $sut->getClients();
401+
self::assertCount(1, $collectedData['http_client']['traces']);
402+
$curlCommand = $collectedData['http_client']['traces'][0]['curlCommand'];
403+
self::assertNull($curlCommand);
404+
}
405+
346406
private function httpClientThatHasTracedRequests($tracedRequests): TraceableHttpClient
347407
{
348408
$httpClient = new TraceableHttpClient(new NativeHttpClient());

src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/DoctrineReceiver.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,13 @@
2020
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
2121
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
2222
use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface;
23-
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
2423
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
2524
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
2625

2726
/**
2827
* @author Vincent Touzet <[email protected]>
2928
*/
30-
class DoctrineReceiver implements ReceiverInterface, MessageCountAwareInterface, ListableReceiverInterface
29+
class DoctrineReceiver implements ListableReceiverInterface, MessageCountAwareInterface
3130
{
3231
private const MAX_RETRIES = 3;
3332
private int $retryingSafetyCounter = 0;

src/Symfony/Component/Mime/Part/DataPart.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,20 @@ public static function fromPath(string $path, string $name = null, string $conte
6161
$contentType = self::$mimeTypes->getMimeTypes($ext)[0] ?? 'application/octet-stream';
6262
}
6363

64-
if (!is_file($path) || !is_readable($path)) {
64+
if ((is_file($path) && !is_readable($path)) || is_dir($path)) {
6565
throw new InvalidArgumentException(sprintf('Path "%s" is not readable.', $path));
6666
}
6767

6868
if (false === $handle = @fopen($path, 'r', false)) {
6969
throw new InvalidArgumentException(sprintf('Unable to open path "%s".', $path));
7070
}
71+
72+
if (!is_file($path)) {
73+
$cache = fopen('php://temp', 'r+');
74+
stream_copy_to_stream($handle, $cache);
75+
$handle = $cache;
76+
}
77+
7178
$p = new self($handle, $name ?: basename($path), $contentType);
7279
$p->handle = $handle;
7380

src/Symfony/Component/Mime/Tests/Part/DataPartTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,30 @@ public function testFromPathWithNotAFile()
134134
DataPart::fromPath(__DIR__.'/../Fixtures/mimetypes/');
135135
}
136136

137+
/**
138+
* @group network
139+
*/
140+
public function testFromPathWithUrl()
141+
{
142+
if (!\in_array('https', stream_get_wrappers())) {
143+
$this->markTestSkipped('"https" stream wrapper is not enabled.');
144+
}
145+
146+
$p = DataPart::fromPath($file = 'https://symfony.com/images/common/logo/logo_symfony_header.png');
147+
$content = file_get_contents($file);
148+
$this->assertEquals($content, $p->getBody());
149+
$maxLineLength = 76;
150+
$this->assertEquals(substr(base64_encode($content), 0, $maxLineLength), substr($p->bodyToString(), 0, $maxLineLength));
151+
$this->assertEquals(substr(base64_encode($content), 0, $maxLineLength), substr(implode('', iterator_to_array($p->bodyToIterable())), 0, $maxLineLength));
152+
$this->assertEquals('image', $p->getMediaType());
153+
$this->assertEquals('png', $p->getMediaSubType());
154+
$this->assertEquals(new Headers(
155+
new ParameterizedHeader('Content-Type', 'image/png', ['name' => 'logo_symfony_header.png']),
156+
new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
157+
new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'logo_symfony_header.png', 'filename' => 'logo_symfony_header.png'])
158+
), $p->getPreparedHeaders());
159+
}
160+
137161
public function testHasContentId()
138162
{
139163
$p = new DataPart('content');

src/Symfony/Component/Routing/Annotation/Route.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ class Route
3030
private array $schemes;
3131

3232
/**
33-
* @param string[] $requirements
34-
* @param string[]|string $methods
35-
* @param string[]|string $schemes
33+
* @param array<string|\Stringable> $requirements
34+
* @param string[]|string $methods
35+
* @param string[]|string $schemes
3636
*/
3737
public function __construct(
3838
string|array $path = null,

0 commit comments

Comments
 (0)