Skip to content

Commit 42b86e4

Browse files
Merge branch '7.1' into 7.2
* 7.1: Fix ProcessTest - testIgnoringSignal for local [Console][PhpUnitBridge][VarDumper] Fix `NO_COLOR` empty value handling [Translation] Fix CSV escape char in `CsvFileLoader` on PHP >= 7.4 [DoctrineBridge] fix messenger bus dispatch inside an active transaction [HttpFoundation] Add tests for uncovered sections treat uninitialized properties referenced by property paths as null [PropertyInfo] Check if property is nullable when using `ReflectionExtractor` properly set up constraint options [ErrorHandler][VarDumper] Remove PHP 8.4 deprecations move adding detailed JSON error messages to the validate phase [Profiler] Add word wrap in tables in dialog to see all the text [Core] Fix & Enhance security arabic translation. [HttpFoundation] Add tests for `MethodRequestMatcher` and `SchemeRequestMatcher`
2 parents da90fe7 + 1dbc465 commit 42b86e4

37 files changed

+378
-45
lines changed

src/Symfony/Bridge/Doctrine/Messenger/DoctrineOpenTransactionLoggerMiddleware.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,12 @@ protected function handleForManager(EntityManagerInterface $entityManager, Envel
4141
}
4242

4343
$this->isHandling = true;
44+
$initialTransactionLevel = $entityManager->getConnection()->getTransactionNestingLevel();
4445

4546
try {
4647
return $stack->next()->handle($envelope, $stack);
4748
} finally {
48-
if ($entityManager->getConnection()->isTransactionActive()) {
49+
if ($entityManager->getConnection()->getTransactionNestingLevel() > $initialTransactionLevel) {
4950
$this->logger?->error('A handler opened a transaction but did not close it.', [
5051
'message' => $envelope->getMessage(),
5152
]);

src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineOpenTransactionLoggerMiddlewareTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ public function log($level, $message, $context = []): void
5151

5252
public function testMiddlewareWrapsInTransactionAndFlushes()
5353
{
54-
$this->connection->expects($this->exactly(1))
55-
->method('isTransactionActive')
56-
->willReturn(true, true, false)
54+
$this->connection->expects($this->exactly(2))
55+
->method('getTransactionNestingLevel')
56+
->willReturn(0, 1)
5757
;
5858

5959
$this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock());

src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ private static function hasColorSupport(): bool
407407
}
408408

409409
// Follow https://no-color.org/
410-
if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
410+
if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) {
411411
return false;
412412
}
413413

src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ class_exists(\SymfonyExcludeListSimplePhpunit::class, false) && PHPUnit\Util\Bla
369369
}
370370
}
371371

372-
$cmd[0] = sprintf('%s %s --colors=%s', $PHP, escapeshellarg("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit"), false === $getEnvVar('NO_COLOR') ? 'always' : 'never');
372+
$cmd[0] = sprintf('%s %s --colors=%s', $PHP, escapeshellarg("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit"), '' === $getEnvVar('NO_COLOR', '') ? 'always' : 'never');
373373
$cmd = str_replace('%', '%%', implode(' ', $cmd)).' %1$s';
374374

375375
if ('\\' === \DIRECTORY_SEPARATOR) {
@@ -459,7 +459,7 @@ class SymfonyExcludeListSimplePhpunit
459459
{
460460
}
461461
}
462-
array_splice($argv, 1, 0, ['--colors='.(false === $getEnvVar('NO_COLOR') ? 'always' : 'never')]);
462+
array_splice($argv, 1, 0, ['--colors='.('' === $getEnvVar('NO_COLOR', '') ? 'always' : 'never')]);
463463
$_SERVER['argv'] = $argv;
464464
$_SERVER['argc'] = ++$argc;
465465
include "$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit";

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,8 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e
11181118
->end()
11191119
->arrayNode('default_context')
11201120
->normalizeKeys(false)
1121-
->beforeNormalization()
1121+
->useAttributeAsKey('name')
1122+
->validate()
11221123
->ifTrue(fn () => $this->debug && class_exists(JsonParser::class))
11231124
->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true])
11241125
->end()

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

+58
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\DBAL\Connection;
1515
use PHPUnit\Framework\TestCase;
16+
use Seld\JsonLint\JsonParser;
1617
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration;
1718
use Symfony\Bundle\FullStack;
1819
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
@@ -567,6 +568,63 @@ public function testEnabledLockNeedsResources()
567568
]);
568569
}
569570

571+
public function testSerializerJsonDetailedErrorMessagesEnabledWhenDefaultContextIsConfigured()
572+
{
573+
$processor = new Processor();
574+
$config = $processor->processConfiguration(new Configuration(true), [
575+
[
576+
'serializer' => [
577+
'default_context' => [
578+
'foo' => 'bar',
579+
],
580+
],
581+
],
582+
]);
583+
584+
$this->assertSame(['foo' => 'bar', JsonDecode::DETAILED_ERROR_MESSAGES => true], $config['serializer']['default_context'] ?? []);
585+
}
586+
587+
public function testSerializerJsonDetailedErrorMessagesInDefaultContextCanBeDisabled()
588+
{
589+
$processor = new Processor();
590+
$config = $processor->processConfiguration(new Configuration(true), [
591+
[
592+
'serializer' => [
593+
'default_context' => [
594+
'foo' => 'bar',
595+
JsonDecode::DETAILED_ERROR_MESSAGES => false,
596+
],
597+
],
598+
],
599+
]);
600+
601+
$this->assertSame(['foo' => 'bar', JsonDecode::DETAILED_ERROR_MESSAGES => false], $config['serializer']['default_context'] ?? []);
602+
}
603+
604+
public function testSerializerJsonDetailedErrorMessagesInDefaultContextCanBeDisabledWithSeveralConfigsBeingMerged()
605+
{
606+
$processor = new Processor();
607+
$config = $processor->processConfiguration(new Configuration(true), [
608+
[
609+
'serializer' => [
610+
'default_context' => [
611+
'foo' => 'bar',
612+
JsonDecode::DETAILED_ERROR_MESSAGES => false,
613+
],
614+
],
615+
],
616+
[
617+
'serializer' => [
618+
'default_context' => [
619+
'foobar' => 'baz',
620+
],
621+
],
622+
],
623+
]);
624+
625+
$this->assertSame(['foo' => 'bar', JsonDecode::DETAILED_ERROR_MESSAGES => false, 'foobar' => 'baz'], $config['serializer']['default_context'] ?? []);
626+
}
627+
570628
public function testScopedHttpClientsInheritRateLimiterAndRetryFailedConfiguration()
571629
{
572630
$processor = new Processor();

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/workflow.html.twig

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
border: 1px solid var(--table-border-color);
7474
padding: .625em;
7575
text-align: left;
76+
word-wrap: break-word;
7677
}
7778
7879
dialog table thead th {

src/Symfony/Component/Console/Output/StreamOutput.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ protected function doWrite(string $message, bool $newline): void
9090
protected function hasColorSupport(): bool
9191
{
9292
// Follow https://no-color.org/
93-
if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
93+
if ('' !== ($_SERVER['NO_COLOR'] ?? getenv('NO_COLOR') ?: '')) {
9494
return false;
9595
}
9696

src/Symfony/Component/ErrorHandler/ErrorHandler.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ class ErrorHandler
5555
\E_USER_DEPRECATED => 'User Deprecated',
5656
\E_NOTICE => 'Notice',
5757
\E_USER_NOTICE => 'User Notice',
58-
\E_STRICT => 'Runtime Notice',
5958
\E_WARNING => 'Warning',
6059
\E_USER_WARNING => 'User Warning',
6160
\E_COMPILE_WARNING => 'Compile Warning',
@@ -73,7 +72,6 @@ class ErrorHandler
7372
\E_USER_DEPRECATED => [null, LogLevel::INFO],
7473
\E_NOTICE => [null, LogLevel::ERROR],
7574
\E_USER_NOTICE => [null, LogLevel::ERROR],
76-
\E_STRICT => [null, LogLevel::ERROR],
7775
\E_WARNING => [null, LogLevel::ERROR],
7876
\E_USER_WARNING => [null, LogLevel::ERROR],
7977
\E_COMPILE_WARNING => [null, LogLevel::ERROR],
@@ -182,6 +180,11 @@ public function __construct(
182180
?BufferingLogger $bootstrappingLogger = null,
183181
private bool $debug = false,
184182
) {
183+
if (\PHP_VERSION_ID < 80400) {
184+
$this->levels[\E_STRICT] = 'Runtime Notice';
185+
$this->loggers[\E_STRICT] = [null, LogLevel::WARNING];
186+
}
187+
185188
if ($bootstrappingLogger) {
186189
$this->bootstrappingLogger = $bootstrappingLogger;
187190
$this->setDefaultLogger($bootstrappingLogger);

src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ public function testDefaultLogger()
203203
\E_USER_DEPRECATED => [null, LogLevel::INFO],
204204
\E_NOTICE => [$logger, LogLevel::ERROR],
205205
\E_USER_NOTICE => [$logger, LogLevel::CRITICAL],
206-
\E_STRICT => [null, LogLevel::ERROR],
207206
\E_WARNING => [null, LogLevel::ERROR],
208207
\E_USER_WARNING => [null, LogLevel::ERROR],
209208
\E_COMPILE_WARNING => [null, LogLevel::ERROR],
@@ -215,6 +214,11 @@ public function testDefaultLogger()
215214
\E_ERROR => [null, LogLevel::CRITICAL],
216215
\E_CORE_ERROR => [null, LogLevel::CRITICAL],
217216
];
217+
218+
if (\PHP_VERSION_ID < 80400) {
219+
$loggers[\E_STRICT] = [null, LogLevel::ERROR];
220+
}
221+
218222
$this->assertSame($loggers, $handler->setLoggers([]));
219223
} finally {
220224
restore_error_handler();
@@ -440,7 +444,6 @@ public function testBootstrappingLogger()
440444
\E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO],
441445
\E_NOTICE => [$bootLogger, LogLevel::ERROR],
442446
\E_USER_NOTICE => [$bootLogger, LogLevel::ERROR],
443-
\E_STRICT => [$bootLogger, LogLevel::ERROR],
444447
\E_WARNING => [$bootLogger, LogLevel::ERROR],
445448
\E_USER_WARNING => [$bootLogger, LogLevel::ERROR],
446449
\E_COMPILE_WARNING => [$bootLogger, LogLevel::ERROR],
@@ -453,6 +456,10 @@ public function testBootstrappingLogger()
453456
\E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL],
454457
];
455458

459+
if (\PHP_VERSION_ID < 80400) {
460+
$loggers[\E_STRICT] = [$bootLogger, LogLevel::ERROR];
461+
}
462+
456463
$this->assertSame($loggers, $handler->setLoggers([]));
457464

458465
$handler->handleError(\E_DEPRECATED, 'Foo message', __FILE__, 123, []);

src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php

+18
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ public function testFilterArrayWithoutArrayFlag()
154154
$bag->filter('foo', \FILTER_VALIDATE_INT);
155155
}
156156

157+
public function testAdd()
158+
{
159+
$bag = new InputBag(['foo' => 'bar']);
160+
$bag->add(['baz' => 'qux']);
161+
162+
$this->assertSame('bar', $bag->get('foo'), '->add() does not remove existing parameters');
163+
$this->assertSame('qux', $bag->get('baz'), '->add() adds new parameters');
164+
}
165+
166+
public function testReplace()
167+
{
168+
$bag = new InputBag(['foo' => 'bar']);
169+
$bag->replace(['baz' => 'qux']);
170+
171+
$this->assertNull($bag->get('foo'), '->replace() removes existing parameters');
172+
$this->assertSame('qux', $bag->get('baz'), '->replace() adds new parameters');
173+
}
174+
157175
public function testGetEnum()
158176
{
159177
$bag = new InputBag(['valid-value' => 1]);

src/Symfony/Component/HttpFoundation/Tests/RateLimiter/AbstractRequestRateLimiterTest.php

+29
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\RateLimiter\LimiterInterface;
17+
use Symfony\Component\RateLimiter\Policy\NoLimiter;
1718
use Symfony\Component\RateLimiter\RateLimit;
1819

1920
class AbstractRequestRateLimiterTest extends TestCase
@@ -33,6 +34,34 @@ public function testConsume(array $rateLimits, ?RateLimit $expected)
3334
$this->assertSame($expected, $rateLimiter->consume(new Request()));
3435
}
3536

37+
public function testConsumeWithoutLimiterAddsSpecialNoLimiter()
38+
{
39+
$rateLimiter = new MockAbstractRequestRateLimiter([]);
40+
41+
try {
42+
$this->assertSame(\PHP_INT_MAX, $rateLimiter->consume(new Request())->getLimit());
43+
} catch (\TypeError $error) {
44+
if (str_contains($error->getMessage(), 'RateLimit::__construct(): Argument #1 ($availableTokens) must be of type int, float given')) {
45+
$this->markTestSkipped('This test cannot be run on a version of the RateLimiter component that uses \INF instead of \PHP_INT_MAX in NoLimiter.');
46+
}
47+
48+
throw $error;
49+
}
50+
}
51+
52+
public function testResetLimiters()
53+
{
54+
$rateLimiter = new MockAbstractRequestRateLimiter([
55+
$limiter1 = $this->createMock(LimiterInterface::class),
56+
$limiter2 = $this->createMock(LimiterInterface::class),
57+
]);
58+
59+
$limiter1->expects($this->once())->method('reset');
60+
$limiter2->expects($this->once())->method('reset');
61+
62+
$rateLimiter->reset(new Request());
63+
}
64+
3665
public static function provideRateLimits()
3766
{
3867
$now = new \DateTimeImmutable();

src/Symfony/Component/HttpFoundation/Tests/RequestMatcher/MethodRequestMatcherTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ public function test(string $requestMethod, array|string $matcherMethod, bool $i
2727
$this->assertSame($isMatch, $matcher->matches($request));
2828
}
2929

30+
public function testAlwaysMatchesOnEmptyMethod()
31+
{
32+
$matcher = new MethodRequestMatcher([]);
33+
$request = Request::create('https://example.com', 'POST');
34+
$this->assertTrue($matcher->matches($request));
35+
}
36+
3037
public static function getData()
3138
{
3239
return [

src/Symfony/Component/HttpFoundation/Tests/RequestMatcher/SchemeRequestMatcherTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ public function test(string $requestScheme, array|string $matcherScheme, bool $i
4242
}
4343
}
4444

45+
public function testAlwaysMatchesOnParamsHeaders()
46+
{
47+
$matcher = new SchemeRequestMatcher([]);
48+
$request = Request::create('sftp://example.com');
49+
$this->assertTrue($matcher->matches($request));
50+
}
51+
4552
public static function getData()
4653
{
4754
return [

src/Symfony/Component/Process/Tests/ProcessTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -1669,7 +1669,7 @@ public function testIgnoringSignal()
16691669
$this->markTestSkipped('pnctl extension is required.');
16701670
}
16711671

1672-
$process = $this->getProcess('sleep 10');
1672+
$process = $this->getProcess(['sleep', '10']);
16731673
$process->setIgnoredSignals([\SIGTERM]);
16741674

16751675
$process->start();
@@ -1685,7 +1685,7 @@ public function testNotIgnoringSignal()
16851685
$this->markTestSkipped('pnctl extension is required.');
16861686
}
16871687

1688-
$process = $this->getProcess('sleep 10');
1688+
$process = $this->getProcess(['sleep', '10']);
16891689

16901690
$process->start();
16911691
$process->stop(timeout: 0.2);

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ public function getType(string $class, string $property, array $context = []): ?
217217
$type = $this->typeResolver->resolve($mutatorReflection->getParameters()[0]);
218218

219219
if (!$type instanceof CollectionType && \in_array($prefix, $this->arrayMutatorPrefixes, true)) {
220-
$type = Type::list($type);
220+
$type = $this->isNullableProperty($class, $property) ? Type::nullable(Type::list($type)) : Type::list($type);
221221
}
222222

223223
return $type;

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ public static function provideLegacyTypes()
164164
['listOfStrings', [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, false, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), new LegacyType(LegacyType::BUILTIN_TYPE_STRING))], null, null],
165165
['self', [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, Dummy::class)], null, null],
166166
['collectionAsObject', [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, DummyCollection::class, true, [new LegacyType(LegacyType::BUILTIN_TYPE_INT)], [new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])], null, null],
167+
['nullableTypedCollection', [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, true, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, Dummy::class))], null, null],
167168
];
168169
}
169170

@@ -548,6 +549,7 @@ public static function typeProvider(): iterable
548549
yield ['collection', Type::list(Type::object(\DateTimeImmutable::class)), null, null];
549550
yield ['nestedCollection', Type::list(Type::list(Type::string())), null, null];
550551
yield ['mixedCollection', Type::array(), null, null];
552+
yield ['nullableTypedCollection', Type::nullable(Type::list(Type::object(Dummy::class))), null, null];
551553
yield ['a', Type::int(), 'A.', null];
552554
yield ['b', Type::nullable(Type::object(ParentDummy::class)), 'B.', null];
553555
yield ['c', Type::nullable(Type::bool()), null, null];

src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public function testGetProperties()
7676
'listOfStrings',
7777
'parentAnnotation',
7878
'genericInterface',
79+
'nullableTypedCollection',
7980
'foo',
8081
'foo2',
8182
'foo3',
@@ -143,6 +144,7 @@ public function testGetPropertiesWithCustomPrefixes()
143144
'listOfStrings',
144145
'parentAnnotation',
145146
'genericInterface',
147+
'nullableTypedCollection',
146148
'foo',
147149
'foo2',
148150
'foo3',
@@ -199,6 +201,7 @@ public function testGetPropertiesWithNoPrefixes()
199201
'listOfStrings',
200202
'parentAnnotation',
201203
'genericInterface',
204+
'nullableTypedCollection',
202205
'foo',
203206
'foo2',
204207
'foo3',
@@ -866,6 +869,7 @@ public function testTypedProperties()
866869
$this->assertEquals(Type::list(Type::string()), $this->extractor->getType(Php74Dummy::class, 'stringCollection'));
867870
$this->assertEquals(Type::nullable(Type::int()), $this->extractor->getType(Php74Dummy::class, 'nullableWithDefault'));
868871
$this->assertEquals(Type::array(), $this->extractor->getType(Php74Dummy::class, 'collection'));
872+
$this->assertEquals(Type::nullable(Type::list(Type::object(Dummy::class))), $this->extractor->getType(Php74Dummy::class, 'nullableTypedCollection'));
869873
}
870874

871875
/**

0 commit comments

Comments
 (0)