10000 Improve support for anonymous classes · symfony/symfony@da351cd · GitHub
[go: up one dir, main page]

Skip to content

Commit da351cd

Browse files
Improve support for anonymous classes
1 parent f0168e3 commit da351cd

File tree

11 files changed

+135
-18
lines changed

11 files changed

+135
-18
lines changed

src/Symfony/Component/Console/Application.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,12 +753,20 @@ protected function doRenderException(\Exception $e, OutputInterface $output)
753753
do {
754754
$message = trim($e->getMessage());
755755
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
756-
$title = sprintf(' [%s%s] ', \get_class($e), 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
756+
$class = \get_class($e);
757+
$class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
758+
$title = 6D40 sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
757759
$len = Helper::strlen($title);
758760
} else {
759761
$len = 0;
760762
}
761763

764+
if (false !== strpos($message, "class@anonymous\0")) {
765+
$message = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
766+
return \class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
767+
}, $message);
768+
}
769+
762770
$width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX;
763771
$lines = array();
764772
foreach ('' !== $message ? preg_split('/\r?\n/', $message) : array() as $line) {

src/Symfony/Component/Console/Tests/ApplicationTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,31 @@ public function testRenderExceptionLineBreaks()
824824
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_linebreaks.txt', $tester->getDisplay(true), '->renderException() keep multiple line breaks');
825825
}
826826

827+
public function testRenderAnonymousException()
828+
{
829+
$application = new Application();
830+
$application->setAutoExit(false);
831+
$application->register('foo')->setCode(function () {
832+
throw new class('') extends \InvalidArgumentException {
833+
};
834+
});
835+
$tester = new ApplicationTester($application);
836+
837+
$tester->run(array('command' => 'foo'), array('decorated' => false));
838+
$this->assertContains('[InvalidArgumentException@anonymous]', $tester->getDisplay(true));
839+
840+
$application = new Application();
841+
$application->setAutoExit(false);
842+
$application->register('foo')->setCode(function () {
843+
throw new \InvalidArgumentException(sprintf('Dummy type "%s" is invalid.', \get_class(new class() {
844+
})));
845+
});
846+
$tester = new ApplicationTester($application);
847+
848+
$tester->run(array('command' => 'foo'), array('decorated' => false));
849+
$this->assertContains('Dummy type "@anonymous" is invalid.', $tester->getDisplay(true));
850+
}
851+
827852
public function testRun()
828853
{
829854
$application = new Application();

src/Symfony/Component/Debug/ErrorHandler.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Psr\Log\LogLevel;
1616
use Symfony\Component\Debug\Exception\FatalErrorException;
1717
use Symfony\Component\Debug\Exception\FatalThrowableError;
18+
use Symfony\Component\Debug\Exception\FlattenException;
1819
use Symfony\Component\Debug\Exception\OutOfMemoryException;
1920
use Symfony\Component\Debug\Exception\SilencedErrorContext;
2021
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
@@ -405,7 +406,12 @@ public function handleError($type, $message, $file, $line)
405406
$context = $e;
406407
}
407408

408-
$logMessage = $this->levels[$type].': '.$message;
409+
if (false !== strpos($message, "class@anonymous\0")) {
410+
($logMessage = new FlattenException())->setMessage($message);
411+
$logMessage = $this->levels[$type].': '.$logMessage->getMessage();
412+
} else {
413+
$logMessage = $this->levels[$type].': '.$message;
414+
}
409415

410416
if (null !== self::$toStringException) {
411417
$errorAsException = self::$toStringException;
@@ -518,21 +524,25 @@ public function handleException($exception, array $error = null)
518524
$handlerException = null;
519525

520526
if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
527+
if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
528+
($message = new FlattenException())->setMessage($message);
529+
$message = $message->getMessage();
530+
}
521531
if ($exception instanceof FatalErrorException) {
522532
if ($exception instanceof FatalThrowableError) {
523533
$error = array(
524534
'type' => $type,
525-
'message' => $message = $exception->getMessage(),
535+
'message' => $message,
526536
'file' => $exception->getFile(),
527537
'line' => $exception->getLine(),
528538
);
529539
} else {
530-
$message = 'Fatal '.$exception->getMessage();
540+
$message = 'Fatal '.$message;
531541
}
532542
} elseif ($exception instanceof \ErrorException) {
533-
$message = 'Uncaught '.$exception->getMessage();
543+
$message = 'Uncaught '.$message;
534544
} else {
535-
$message = 'Uncaught Exception: '.$exception->getMessage();
545+
$message = 'Uncaught Exception: '.$message;
536546
}
537547
}
538548
if ($this->loggedErrors & $type) {

src/Symfony/Component/Debug/Exception/FlattenException.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public function getClass()
112112

113113
public function setClass($class)
114114
{
115-
$this->class = $class;
115+
$this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
116116
}
117117

118118
public function getFile()
@@ -142,6 +142,12 @@ public function getMessage()
142142

143143
public function setMessage($message)
144144
{
145+
if (false !== strpos($message, "class@anonymous\0")) {
146+
$message = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
147+
return \class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
148+
}, $message);
149+
}
150+
145151
$this->message = $message;
146152
}
147153

src/Symfony/Component/Debug/ExceptionHandler.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ public function getContent(FlattenException $exception)
253253
} catch (\Exception $e) {
254254
// something nasty happened and we cannot throw an exception anymore
255255
if ($this->debug) {
256-
$title = sprintf('Exception thrown when handling an exception (%s: %s)', \get_class($e), $this->escapeHtml($e->getMessage()));
256+
$e = FlattenException::create($e);
257+
$title = sprintf('Exception thrown when handling an exception (%s: %s)', $e->getClass(), $this->escapeHtml($e->getMessage()));
257258
} else {
258259
$title = 'Whoops, looks like something went wrong.';
259260
}

src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,19 @@ public function testTooBigArray()
333333
$this->assertNotContains('*value1*', $serializeTrace);
334334
}
335335

336+
public function testAnonymousClass()
337+
{
338+
$flattened = FlattenException::create(new class() extends \RuntimeException {
339+
});
340+
341+
$this->assertSame('RuntimeException@anonymous', $flattened->getClass());
342+
343+
$flattened = FlattenException::create(new \Exception(sprintf('Class "%s" blah.', \get_class(new class() extends \RuntimeException {
344+
}))));
345+
346+
$this->assertSame('Class "RuntimeException@anonymous" blah.', $flattened->getMessage());
347+
}
348+
336349
private function createException($foo)
337350
{
338351
return new \Exception();

src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ public function __construct($controller, LoggerInterface $logger = null, $debug
5050

5151
public function logKernelException(GetResponseForExceptionEvent $event)
5252
{
53-
$exception = $event->getException();
53+
$e = FlattenException::create($event->getException());
5454

55-
$this->logException($exception, sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', \get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine()));
55+
$this->logException($event->getException(), sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', $e->getClass(), $e->getMessage(), $e->getFile(), $e->getLine()));
5656
}
5757

5858
public function onKernelException(GetResponseForExceptionEvent $event)
@@ -75,7 +75,9 @@ public function onKernelException(GetResponseForExceptionEvent $event)
7575
try {
7676
$response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false);
7777
} catch (\Exception $e) {
78-
$this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', \get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()));
78+
$f = FlattenException::create($e);
79+
80+
$this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', $f->getClass(), $f->getMessage(), $e->getFile(), $e->getLine()));
7981

8082
$wrapper = $e;
8183

src/Symfony/Component/VarDumper/Caster/ClassStub.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@ public function __construct(string $identifier, $callable = null)
2828
{
2929
$this->value = $identifier;
3030

31-
if (0 < $i = strrpos($identifier, '\\')) {
32-
$this->attr['ellipsis'] = \strlen($identifier) - $i;
33-
$this->attr['ellipsis-type'] = 'class';
34-
$this->attr['ellipsis-tail'] = 1;
35-
}
36-
3731
try {
3832
if (null !== $callable) {
3933
if ($callable instanceof \Closure) {
@@ -61,6 +55,12 @@ public function __construct(string $identifier, $callable = null)
6155
}
6256
}
6357

58+
if (false !== strpos($identifier, "class@anonymous\0")) {
59+
$this->value = $identifier = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
60+
return \class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
61+
}, $identifier);
62+
}
63+
6464
if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) {
6565
$s = ReflectionCaster::castFunctionAbstract($r, array(), new Stub(), true);
6666
$s = ReflectionCaster::getSignature($s);
@@ -76,6 +76,12 @@ public function __construct(string $identifier, $callable = null)
7676
}
7777
} catch (\ReflectionException $e) {
7878
return;
79+
} finally {
80+
if (0 < $i = strrpos($identifier, '\\')) {
81+
$this->attr['ellipsis'] = \strlen($identifier) - $i;
82+
$this->attr['ellipsis-type'] = 'class';
83+
$this->attr['ellipsis-tail'] = 1;
84+
}
7985
}
8086

8187
if ($f = $r->getFileName()) {

src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ public static function castThrowingCasterException(ThrowingCasterException $e, a
7171

7272
if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) {
7373
$b = (array) $a[$xPrefix.'previous'];
74-
self::traceUnshift($b[$xPrefix.'trace'], \get_class($a[$xPrefix.'previous']), $b[$prefix.'file'], $b[$prefix.'line']);
74+
$class = \get_class($a[$xPrefix.'previous']);
75+
$class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
76+
self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']);
7577
$a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value));
7678
}
7779

@@ -279,6 +281,12 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte
279281
}
280282
unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']);
281283

284+
if (isset($a[Caster::PREFIX_PROTECTED.'message']) && false !== strpos($a[Caster::PREFIX_PROTECTED.'message'], "class@anonymous\0")) {
285+
$a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
286+
return \class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
287+
}, $a[Caster::PREFIX_PROTECTED.'message']);
288+
}
289+
282290
if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) {
283291
$a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']);
284292
}

src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,23 @@ public function testExcludeVerbosity()
223223
#file: "%sExceptionCasterTest.php"
224224
#line: 28
225225
}
226+
EODUMP;
227+
228+
$this->assertDumpMatchesFormat($expectedDump, $e, Caster::EXCLUDE_VERBOSE);
229+
}
230+
231+
public function testAnonymous()
232+
{
233+
$e = new \Exception(sprintf('Boo "%s" ba.', \get_class(new class('Foo') extends \Exception {
234+
})));
235+
236+
$expectedDump = <<<'EODUMP'
237+
Exception {
238+
#message: "Boo "Exception@anonymous" ba."
239+
#code: 0
240+
#file: "%sExceptionCasterTest.php"
241+
#line: %d
242+
}
226243
EODUMP;
227244

228245
$this->assertDumpMatchesFormat($expectedDump, $e, Caster::EXCLUDE_VERBOSE);

src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,27 @@ public function testClassStubWithNotExistingMethod()
185185
<span class=sf-dump-index>0</span> => "<a href="%sFooInterface.php:5" rel="noopener noreferrer"><span class=sf-dump-str title="5 characters">hello</span></a>"
186186
</samp>]
187187
</bar>
188+
EODUMP;
189+
190+
$this->assertStringMatchesFormat($expectedDump, $dump);
191+
}
192+
193+
public function testClassStubWithAnonymousClass()
194+
{
195+
$var = array(new ClassStub(\get_class(new class() extends \Exception {
196+
})));
197+
198+
$cloner = new VarCloner();
199+
$dumper = new HtmlDumper();
200+
$dumper->setDumpHeader('<foo></foo>');
201+
$dumper->setDumpBoundaries('<bar>', '</bar>');
202+
$dump = $dumper->dump($cloner->cloneVar($var), true, array('fileLinkFormat' => '%f:%l'));
203+
204+
$expectedDump = <<<'EODUMP'
205+
<foo></foo><bar><span class=sf-dump-note>array:1</span> [<samp>
206+
<span class=sf-dump-index>0</span> => "<a href="%sStubCasterTest.php:195" rel="noopener noreferrer"><span class=sf-dump-str title="19 characters">Exception@anonymous</span></a>"
207+
</samp>]
208+
</bar>
188209
EODUMP;
189210

190211
$this->assertStringMatchesFormat($expectedDump, $dump);

0 commit comments

Comments
 (0)
0