Skip to content

Commit ba6b2ff

Browse files
Merge branch '7.2' into 7.3
* 7.2: - [Console] Table counts wrong column width when using colspan and `setColumnMaxWidth()` [Console] Table counts wrong number of padding symbols in `renderCell()` method when cell contain unicode variant selector [Cache] Fix using a `ChainAdapter` as an adapter for a pool [Serializer] Fix collect_denormalization_errors flag in defaultContext [TypeInfo] Fix handling `ConstFetchNode` [VarDumper] Avoid deprecated call in PgSqlCaster Fix command option mode (InputOption::VALUE_REQUIRED) use an EOL-agnostic approach to parse class uses [Uid] Improve entropy of the increment for UUIDv7 [HttpKernel] Fix `#[MapUploadedFile]` handling for optional file uploads
2 parents d56ecc9 + d527640 commit ba6b2ff

File tree

30 files changed

+380
-52
lines changed

30 files changed

+380
-52
lines changed

src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ protected function configure(): void
6969
->setDefinition([
7070
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
7171
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
72-
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'),
72+
new InputOption('domain', null, InputOption::VALUE_REQUIRED, 'The messages domain'),
7373
new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Display only missing messages'),
7474
new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Display only unused messages'),
7575
new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'),

src/Symfony/Bundle/FrameworkBundle/Command/TranslationExtractCommand.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ protected function configure(): void
7272
->setDefinition([
7373
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
7474
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
75-
new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'),
75+
new InputOption('prefix', null, InputOption::VALUE_REQUIRED, 'Override the default prefix', '__'),
7676
new InputOption('no-fill', null, InputOption::VALUE_NONE, 'Extract translation keys without filling in values'),
77-
new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf12'),
77+
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'Override the default output format', 'xlf12'),
7878
new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'),
7979
new InputOption('force', null, InputOption::VALUE_NONE, 'Should the extract be done'),
8080
new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'),
81-
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to extract'),
82-
new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically'),
83-
new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'),
81+
new InputOption('domain', null, InputOption::VALUE_REQUIRED, 'Specify the domain to extract'),
82+
new InputOption('sort', null, InputOption::VALUE_REQUIRED, 'Return list of messages sorted alphabetically'),
83+
new InputOption('as-tree', null, InputOption::VALUE_REQUIRED, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'),
8484
])
8585
->setHelp(<<<'EOF'
8686
The <info>%command.name%</info> command extracts translation strings from templates

src/Symfony/Component/Cache/DependencyInjection/CachePoolPass.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ public function process(ContainerBuilder $container): void
5555
continue;
5656
}
5757
$class = $adapter->getClass();
58+
$providers = $adapter->getArguments();
5859
while ($adapter instanceof ChildDefinition) {
5960
$adapter = $container->findDefinition($adapter->getParent());
6061
$class = $class ?: $adapter->getClass();
62+
$providers += $adapter->getArguments();
6163
if ($t = $adapter->getTag('cache.pool')) {
6264
$tags[0] += $t[0];
6365
}
@@ -87,7 +89,7 @@ public function process(ContainerBuilder $container): void
8789

8890
if (ChainAdapter::class === $class) {
8991
$adapters = [];
90-
foreach ($adapter->getArgument(0) as $provider => $adapter) {
92+
foreach ($providers['index_0'] ?? $providers[0] as $provider => $adapter) {
9193
if ($adapter instanceof ChildDefinition) {
9294
$chainedPool = $adapter;
9395
} else {

src/Symfony/Component/Cache/Tests/DependencyInjection/CachePoolPassTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ public function testChainAdapterPool()
209209
$container->register('cache.adapter.apcu', ApcuAdapter::class)
210210
->setArguments([null, 0, null])
211211
->addTag('cache.pool');
212-
$container->register('cache.chain', ChainAdapter::class)
212+
$container->register('cache.adapter.chain', ChainAdapter::class);
213+
$container->setDefinition('cache.chain', new ChildDefinition('cache.adapter.chain'))
213214
->addArgument(['cache.adapter.array', 'cache.adapter.apcu'])
214215
->addTag('cache.pool');
215216
$container->setDefinition('cache.app', new ChildDefinition('cache.chain'))
@@ -224,7 +225,7 @@ public function testChainAdapterPool()
224225
$this->assertSame('cache.chain', $appCachePool->getParent());
225226

226227
$chainCachePool = $container->getDefinition('cache.chain');
227-
$this->assertNotInstanceOf(ChildDefinition::class, $chainCachePool);
228+
$this->assertInstanceOf(ChildDefinition::class, $chainCachePool);
228229
$this->assertCount(2, $chainCachePool->getArgument(0));
229230
$this->assertInstanceOf(ChildDefinition::class, $chainCachePool->getArgument(0)[0]);
230231
$this->assertSame('cache.adapter.array', $chainCachePool->getArgument(0)[0]->getParent());

src/Symfony/Component/Console/Application.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,7 @@ private function splitStringByWidth(string $string, int $width): array
12751275

12761276
foreach (preg_split('//u', $m[0]) as $char) {
12771277
// test if $char could be appended to current line
1278-
if (mb_strwidth($line.$char, 'utf8') <= $width) {
1278+
if (Helper::width($line.$char) <= $width) {
12791279
$line .= $char;
12801280
continue;
12811281
}

src/Symfony/Component/Console/Helper/Helper.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ public static function width(?string $string): int
4242
$string ??= '';
4343

4444
if (preg_match('//u', $string)) {
45-
return (new UnicodeString($string))->width(false);
45+
$string = preg_replace('/[\p{Cc}\x7F]++/u', '', $string, -1, $count);
46+
47+
return (new UnicodeString($string))->width(false) + $count;
4648
}
4749

4850
if (false === $encoding = mb_detect_encoding($string, null, true)) {

src/Symfony/Component/Console/Helper/Table.php

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -561,10 +561,7 @@ private function renderCell(array $row, int $column, string $cellFormat): string
561561
}
562562

563563
// str_pad won't work properly with multi-byte strings, we need to fix the padding
564-
if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
565-
$width += \strlen($cell) - mb_strwidth($cell, $encoding);
566-
}
567-
564+
$width += \strlen($cell) - Helper::width($cell) - substr_count($cell, "\0");
568565
$style = $this->getColumnStyle($column);
569566

570567
if ($cell instanceof TableSeparator) {
@@ -629,8 +626,48 @@ private function buildTableRows(array $rows): TableRows
629626
foreach ($rows[$rowKey] as $column => $cell) {
630627
$colspan = $cell instanceof TableCell ? $cell->getColspan() : 1;
631628

632-
if (isset($this->columnMaxWidths[$column]) && Helper::width(Helper::removeDecoration($formatter, $cell)) > $this->columnMaxWidths[$column]) {
633-
$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column] * $colspan);
629+
$minWrappedWidth = 0;
630+
$widthApplied = [];
631+
$lengthColumnBorder = $this->getColumnSeparatorWidth() + Helper::width($this->style->getCellRowContentFormat()) - 2;
632+
for ($i = $column; $i < ($column + $colspan); ++$i) {
633+
if (isset($this->columnMaxWidths[$i])) {
634+
$minWrappedWidth += $this->columnMaxWidths[$i];
635+
$widthApplied[] = ['type' => 'max', 'column' => $i];
636+
} elseif (($this->columnWidths[$i] ?? 0) > 0 && $colspan > 1) {
637+
$minWrappedWidth += $this->columnWidths[$i];
638+
$widthApplied[] = ['type' => 'min', 'column' => $i];
639+
}
640+
}
641+
if (1 === \count($widthApplied)) {
642+
if ($colspan > 1) {
643+
$minWrappedWidth *= $colspan; // previous logic
644+
}
645+
} elseif (\count($widthApplied) > 1) {
646+
$minWrappedWidth += (\count($widthApplied) - 1) * $lengthColumnBorder;
647+
}
648+
649+
$cellWidth = Helper::width(Helper::removeDecoration($formatter, $cell));
650+
if ($minWrappedWidth && $cellWidth > $minWrappedWidth) {
651+
$cell = $formatter->formatAndWrap($cell, $minWrappedWidth);
652+
}
653+
// update minimal columnWidths for spanned columns
654+
if ($colspan > 1 && $minWrappedWidth > 0) {
655+
$columnsMinWidthProcessed = [];
656+
$cellWidth = min($cellWidth, $minWrappedWidth);
657+
foreach ($widthApplied as $item) {
658+
if ('max' === $item['type'] && $cellWidth >= $this->columnMaxWidths[$item['column']]) {
659+
$minWidthColumn = $this->columnMaxWidths[$item['column']];
660+
$this->columnWidths[$item['column']] = $minWidthColumn;
661+
$columnsMinWidthProcessed[$item['column']] = true;
662+
$cellWidth -= $minWidthColumn + $lengthColumnBorder;
663+
}
664+
}
665+
for ($i = $column; $i < ($column + $colspan); ++$i) {
666+
if (isset($columnsMinWidthProcessed[$i])) {
667+
continue;
668+
}
669+
$this->columnWidths[$i] = $cellWidth + $lengthColumnBorder;
670+
}
634671
}
635672
if (!str_contains($cell ?? '', "\n")) {
636673
continue;

src/Symfony/Component/Console/Tests/Helper/TableTest.php

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,9 +1308,9 @@ public static function renderSetTitle()
13081308
'footer',
13091309
'default',
13101310
<<<'TABLE'
1311-
+---------------+---- Multiline
1311+
+---------------+--- Multiline
13121312
header
1313-
here -+------------------+
1313+
here +------------------+
13141314
| ISBN | Title | Author |
13151315
+---------------+--------------------------+------------------+
13161316
| 99921-58-10-7 | Divine Comedy | Dante Alighieri |
@@ -1590,17 +1590,17 @@ public function testWithColspanAndMaxWith()
15901590
$expected =
15911591
<<<TABLE
15921592
+-----------------+-----------------+-----------------+
1593-
| Lorem ipsum dolor sit amet, consectetur adipi |
1594-
| scing elit, sed do eiusmod tempor |
1593+
| Lorem ipsum dolor sit amet, consectetur adipiscing |
1594+
| elit, sed do eiusmod tempor |
15951595
+-----------------+-----------------+-----------------+
1596-
| Lorem ipsum dolor sit amet, consectetur |
1597-
| adipiscing elit, sed do eiusmod tempor |
1596+
| Lorem ipsum dolor sit amet, consectetur adipiscing |
1597+
| elit, sed do eiusmod tempor |
15981598
+-----------------+-----------------+-----------------+
1599-
| Lorem ipsum dolor sit amet, co | hello world |
1600-
| nsectetur | |
1599+
| Lorem ipsum dolor sit amet, conse | hello world |
1600+
| ctetur | |
16011601
+-----------------+-----------------+-----------------+
1602-
| hello world | Lorem ipsum dolor sit amet, co |
1603-
| | nsectetur adipiscing elit |
1602+
| hello world | Lorem ipsum dolor sit amet, conse |
1603+
| | ctetur adipiscing elit |
16041604
+-----------------+-----------------+-----------------+
16051605
| hello | world | Lorem ipsum |
16061606
| | | dolor sit amet, |
@@ -2092,4 +2092,36 @@ public function testGithubIssue52101HorizontalFalse()
20922092
$this->getOutputContent($output)
20932093
);
20942094
}
2095+
2096+
public function testGithubIssue60038WidthOfCellWithEmoji()
2097+
{
2098+
$table = (new Table($output = $this->getOutputStream()))
2099+
->setHeaderTitle('Test Title')
2100+
->setHeaders(['Title', 'Author'])
2101+
->setRows([
2102+
["🎭 💫 ☯"." Divine Comedy", "Dante Alighieri"],
2103+
// the snowflake (e2 9d 84 ef b8 8f) has a variant selector
2104+
["👑 ❄️ 🗡"." Game of Thrones", "George R.R. Martin"],
2105+
// the snowflake in text style (e2 9d 84 ef b8 8e) has a variant selector
2106+
["❄︎❄︎❄︎ snowflake in text style ❄︎❄︎❄︎", ""],
2107+
["And a very long line to show difference in previous lines", ""],
2108+
])
2109+
;
2110+
$table->render();
2111+
2112+
$this->assertSame(<<<TABLE
2113+
+---------------------------------- Test Title -------------+--------------------+
2114+
| Title | Author |
2115+
+-----------------------------------------------------------+--------------------+
2116+
| 🎭 💫 ☯ Divine Comedy | Dante Alighieri |
2117+
| 👑 ❄️ 🗡 Game of Thrones | George R.R. Martin |
2118+
| ❄︎❄︎❄︎ snowflake in text style ❄︎❄︎❄︎ | |
2119+
| And a very long line to show difference in previous lines | |
2120+
+-----------------------------------------------------------+--------------------+
2121+
2122+
TABLE
2123+
,
2124+
$this->getOutputContent($output)
2125+
);
2126+
}
20952127
}

src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ private function mapRequestPayload(Request $request, ArgumentMetadata $argument,
232232

233233
private function mapUploadedFile(Request $request, ArgumentMetadata $argument, MapUploadedFile $attribute): UploadedFile|array|null
234234
{
235-
return $request->files->get($attribute->name ?? $argument->getName(), []);
235+
if (!($files = $request->files->get($attribute->name ?? $argument->getName(), [])) && ($argument->isNullable() || $argument->hasDefaultValue())) {
236+
return null;
237+
}
238+
239+
return $files;
236240
}
237241
}

src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/UploadedFileValueResolverTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,66 @@ static function () {},
307307
$resolver->onKernelControllerArguments($event);
308308
}
309309

310+
/**
311+
* @dataProvider provideContext
312+
*/
313+
public function testShouldAllowEmptyWhenNullable(RequestPayloadValueResolver $resolver, Request $request)
314+
{
315+
$attribute = new MapUploadedFile();
316+
$argument = new ArgumentMetadata(
317+
'qux',
318+
UploadedFile::class,
319+
false,
320+
false,
321+
null,
322+
true,
323+
[$attribute::class => $attribute]
324+
);
325+
/** @var HttpKernelInterface&MockObject $httpKernel */
326+
$httpKernel = $this->createMock(HttpKernelInterface::class);
327+
$event = new ControllerArgumentsEvent(
328+
$httpKernel,
329+
static function () {},
330+
$resolver->resolve($request, $argument),
331+
$request,
332+
HttpKernelInterface::MAIN_REQUEST
333+
);
334+
$resolver->onKernelControllerArguments($event);
335+
$data = $event->getArguments()[0];
336+
337+
$this->assertNull($data);
338+
}
339+
340+
/**
341+
* @dataProvider provideContext
342+
*/
343+
public function testShouldAllowEmptyWhenHasDefaultValue(RequestPayloadValueResolver $resolver, Request $request)
344+
{
345+
$attribute = new MapUploadedFile();
346+
$argument = new ArgumentMetadata(
347+
'qux',
348+
UploadedFile::class,
349+
false,
350+
true,
351+
'default-value',
352+
false,
353+
[$attribute::class => $attribute]
354+
);
355+
/** @var HttpKernelInterface&MockObject $httpKernel */
356+
$httpKernel = $this->createMock(HttpKernelInterface::class);
357+
$event = new ControllerArgumentsEvent(
358+
$httpKernel,
359+
static function () {},
360+
$resolver->resolve($request, $argument),
361+
$request,
362+
HttpKernelInterface::MAIN_REQUEST
363+
);
364+
$resolver->onKernelControllerArguments($event);
365+
$data = $event->getArguments()[0];
366+
367+
$this->assertSame('default-value', $data);
368+
}
369+
310370
public static function provideContext(): iterable
311371
{
312372
$resolver = new RequestPayloadValueResolver(

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy