Skip to content

Commit 21ac3b9

Browse files
Merge branch '6.4' into 7.2
* 6.4: [VarDumper] Fix dumping on systems that don't have a working iconv [Console] fix profiler with overridden `run()` method [Config] Fix support for attributes on class constants and enum cases
2 parents cfe62c5 + f12d58b commit 21ac3b9

File tree

8 files changed

+222
-9
lines changed

8 files changed

+222
-9
lines changed

src/Symfony/Component/Config/Resource/ReflectionClassResource.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ private function generateSignature(\ReflectionClass $class): iterable
134134
yield print_r($class->getConstants(), true);
135135
}
136136

137+
foreach ($class->getReflectionConstants() as $constant) {
138+
foreach ($constant->getAttributes() as $a) {
139+
$attributes[] = [$a->getName(), (string) $a];
140+
}
141+
yield $constant->name.print_r($attributes, true);
142+
$attributes = [];
143+
}
144+
137145
if (!$class->isInterface()) {
138146
$defaults = $class->getDefaultProperties();
139147

src/Symfony/Component/Config/Tests/Resource/ReflectionClassResourceTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,30 @@ public function testIgnoresObjectsInSignature()
195195
TestServiceWithStaticProperty::$initializedObject = new TestServiceWithStaticProperty();
196196
$this->assertTrue($res->isFresh(0));
197197
}
198+
199+
public function testEnum()
200+
{
201+
$res = new ReflectionClassResource($enum = new \ReflectionClass(SomeEnum::class));
202+
$r = new \ReflectionClass(ReflectionClassResource::class);
203+
$generateSignature = $r->getMethod('generateSignature')->getClosure($res);
204+
$actual = implode("\n", iterator_to_array($generateSignature($enum)));
205+
$this->assertStringContainsString('UnitEnum', $actual);
206+
$this->assertStringContainsString('TestAttribute', $actual);
207+
$this->assertStringContainsString('Beta', $actual);
208+
}
209+
210+
public function testBackedEnum()
211+
{
212+
$res = new ReflectionClassResource($enum = new \ReflectionClass(SomeBackedEnum::class));
213+
$r = new \ReflectionClass(ReflectionClassResource::class);
214+
$generateSignature = $r->getMethod('generateSignature')->getClosure($res);
215+
$actual = implode("\n", iterator_to_array($generateSignature($enum)));
216+
$this->assertStringContainsString('UnitEnum', $actual);
217+
$this->assertStringContainsString('BackedEnum', $actual);
218+
$this->assertStringContainsString('TestAttribute', $actual);
219+
$this->assertStringContainsString('Beta', $actual);
220+
$this->assertStringContainsString('beta', $actual);
221+
}
198222
}
199223

200224
interface DummyInterface
@@ -225,3 +249,19 @@ class TestServiceWithStaticProperty
225249
{
226250
public static object $initializedObject;
227251
}
252+
253+
enum SomeEnum
254+
{
255+
case Alpha;
256+
257+
#[TestAttribute]
258+
case Beta;
259+
}
260+
261+
enum SomeBackedEnum: string
262+
{
263+
case Alpha = 'alpha';
264+
265+
#[TestAttribute]
266+
case Beta = 'beta';
267+
}

src/Symfony/Component/Console/Command/TraceableCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public function run(InputInterface $input, OutputInterface $output): int
283283
$event = $this->stopwatch->start($this->getName(), 'command');
284284

285285
try {
286-
$this->exitCode = parent::run($input, $output);
286+
$this->exitCode = $this->command->run($input, $output);
287287
} finally {
288288
$event->stop();
289289

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Console\Application;
16+
use Symfony\Component\Console\Command\TraceableCommand;
17+
use Symfony\Component\Console\Tester\CommandTester;
18+
use Symfony\Component\Console\Tests\Fixtures\LoopExampleCommand;
19+
use Symfony\Component\Stopwatch\Stopwatch;
20+
21+
class TraceableCommandTest extends TestCase
22+
{
23+
private Application $application;
24+
25+
protected function setUp(): void
26+
{
27+
$this->application = new Application();
28+
$this->application->add(new LoopExampleCommand());
29+
}
30+
31+
public function testRunIsOverriddenWithoutProfile()
32+
{
33+
$command = $this->application->find('app:loop:example');
34+
$commandTester = new CommandTester($command);
35+
$commandTester->execute([]);
36+
$commandTester->assertCommandIsSuccessful();
37+
38+
$output = $commandTester->getDisplay();
39+
$this->assertLoopOutputCorrectness($output);
40+
}
41+
42+
public function testRunIsNotOverriddenWithProfile()
43+
{
44+
// Simulate the bug environment by wrapping
45+
// our command in TraceableCommand, which is what Symfony does
46+
// when you use the --profile option.
47+
$command = new LoopExampleCommand();
48+
$traceableCommand = new TraceableCommand($command, new Stopwatch());
49+
50+
$this->application->add($traceableCommand);
51+
52+
$commandTester = new CommandTester($traceableCommand);
53+
$commandTester->execute([]);
54+
$commandTester->assertCommandIsSuccessful();
55+
56+
$output = $commandTester->getDisplay();
57+
$this->assertLoopOutputCorrectness($output);
58+
}
59+
60+
public function assertLoopOutputCorrectness(string $output)
61+
{
62+
self::assertMatchesRegularExpression('~3/3\s+\[▓+]\s+100%~u', $output);
63+
self::assertStringContainsString('Loop finished.', $output);
64+
self::assertEquals(3, substr_count($output, 'Hello world'));
65+
}
66+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\Fixtures;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\Console\Style\SymfonyStyle;
18+
19+
abstract class AbstractLoopCommand extends Command
20+
{
21+
public function run(InputInterface $input, OutputInterface $output): int
22+
{
23+
$io = new SymfonyStyle($input, $output);
24+
$contexts = [1, 2, 3];
25+
$io->progressStart(count($contexts));
26+
$code = self::SUCCESS;
27+
28+
foreach ($contexts as $ignored) {
29+
$io->progressAdvance();
30+
try {
31+
parent::run($input, $output);
32+
} catch (\Throwable) {
33+
$code = self::FAILURE;
34+
}
35+
}
36+
$io->progressFinish();
37+
$output->writeln("\nLoop finished.");
38+
39+
return $code;
40+
}
41+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Console\Tests\Fixtures;
13+
14+
use Symfony\Component\Console\Attribute\AsCommand;
15+
use Symfony\Component\Console\Command\Command;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Output\OutputInterface;
18+
19+
#[AsCommand('app:loop:example')]
20+
class LoopExampleCommand extends AbstractLoopCommand
21+
{
22+
protected function execute(InputInterface $input, OutputInterface $output): int
23+
{
24+
$output->writeln(' Hello world');
25+
26+
return Command::SUCCESS;
27+
}
28+
}

src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,17 +185,48 @@ protected function utf8Encode(?string $s): ?string
185185
return $s;
186186
}
187187

188-
if (!\function_exists('iconv')) {
189-
throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');
188+
if (\function_exists('iconv')) {
189+
if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) {
190+
return $c;
191+
}
192+
if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) {
193+
return $c;
194+
}
190195
}
191196

192-
if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) {
193-
return $c;
197+
$s .= $s;
198+
$len = \strlen($s);
199+
$mapCp1252 = false;
200+
201+
for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {
202+
if ($s[$i] < "\x80") {
203+
$s[$j] = $s[$i];
204+
} elseif ($s[$i] < "\xC0") {
205+
$s[$j] = "\xC2";
206+
$s[++$j] = $s[$i];
207+
if ($s[$i] < "\xA0") {
208+
$mapCp1252 = true;
209+
}
210+
} else {
211+
$s[$j] = "\xC3";
212+
$s[++$j] = \chr(\ord($s[$i]) - 64);
213+
}
194214
}
195-
if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) {
196-
return $c;
215+
216+
$s = substr($s, 0, $j);
217+
218+
if (!$mapCp1252) {
219+
return $s;
197220
}
198221

199-
return iconv('CP850', 'UTF-8', $s);
222+
return strtr($s, [
223+
"\xC2\x80" => '', "\xC2\x82" => '', "\xC2\x83" => 'ƒ', "\xC2\x84" => '',
224+
"\xC2\x85" => '', "\xC2\x86" => '', "\xC2\x87" => '', "\xC2\x88" => 'ˆ',
225+
"\xC2\x89" => '', "\xC2\x8A" => 'Š', "\xC2\x8B" => '', "\xC2\x8C" => 'Œ',
226+
"\xC2\x8D" => 'Ž', "\xC2\x91" => '', "\xC2\x92" => '', "\xC2\x93" => '',
227+
"\xC2\x94" => '', "\xC2\x95" => '', "\xC2\x96" => '', "\xC2\x97" => '',
228+
"\xC2\x98" => '˜', "\xC2\x99" => '', "\xC2\x9A" => 'š', "\xC2\x9B" => '',
229+
"\xC2\x9C" => 'œ', "\xC2\x9E" => 'ž',
230+
]);
200231
}
201232
}

src/Symfony/Component/VarDumper/composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
"symfony/polyfill-mbstring": "~1.0"
2121
},
2222
"require-dev": {
23-
"ext-iconv": "*",
2423
"symfony/console": "^6.4|^7.0",
2524
"symfony/http-kernel": "^6.4|^7.0",
2625
"symfony/process": "^6.4|^7.0",

0 commit comments

Comments
 (0)