From f0a59d3eabcd29709f6c2a11fe73e0a78728f67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?THERAGE=20K=C3=A9vin?= Date: Fri, 12 Jul 2019 13:26:59 +0200 Subject: [PATCH 1/2] [VarDumper] Output the location of calls to dump() --- .../DebugBundle/Resources/config/services.xml | 13 ++++++ .../Component/VarDumper/Cloner/Data.php | 26 ++++++++++- .../Component/VarDumper/Dumper/CliDumper.php | 10 ++++- .../VarDumper/Dumper/ContextualizedDumper.php | 43 +++++++++++++++++++ .../VarDumper/Tests/Cloner/VarClonerTest.php | 25 ++++++++++- .../Tests/Dumper/ContextualizedDumperTest.php | 43 +++++++++++++++++++ src/Symfony/Component/VarDumper/VarDumper.php | 4 ++ 7 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 src/Symfony/Component/VarDumper/Dumper/ContextualizedDumper.php create mode 100644 src/Symfony/Component/VarDumper/Tests/Dumper/ContextualizedDumperTest.php diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml index 75819a017b..9cb70d08cc 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -40,6 +40,19 @@ 0 + + + + + + %kernel.charset% + %kernel.project_dir% + + + + + + null %kernel.charset% diff --git a/src/Symfony/Component/VarDumper/Cloner/Data.php b/src/Symfony/Component/VarDumper/Cloner/Data.php index c4dd5eeb4b..7e148bf49e 100644 --- a/src/Symfony/Component/VarDumper/Cloner/Data.php +++ b/src/Symfony/Component/VarDumper/Cloner/Data.php @@ -12,6 +12,7 @@ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; +use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; /** * @author Nicolas Grekas @@ -24,6 +25,7 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate private $maxDepth = 20; private $maxItemsPerDepth = -1; private $useRefHandles = -1; + private $context = []; /** * @param array $data An array as returned by ClonerInterface::cloneVar() @@ -227,6 +229,17 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate return $data; } + /** + * @return static + */ + public function withContext(array $context) + { + $data = clone $this; + $data->context = $context; + + return $data; + } + /** * Seeks to a specific key in nested data structures. * @@ -281,7 +294,18 @@ class Data implements \ArrayAccess, \Countable, \IteratorAggregate public function dump(DumperInterface $dumper) { $refs = [0]; - $this->dumpItem($dumper, new Cursor(), $refs, $this->data[$this->position][$this->key]); + $cursor = new Cursor(); + + if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) { + $cursor->attr['if_links'] = true; + $cursor->hashType = -1; + $dumper->dumpScalar($cursor, 'default', '^'); + $cursor->attr = ['if_links' => true]; + $dumper->dumpScalar($cursor, 'default', ' '); + $cursor->hashType = 0; + } + + $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); } /** diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php index f390cd7832..2cef630cf0 100644 --- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php @@ -83,7 +83,7 @@ class CliDumper extends AbstractDumper ]); } - $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f'; + $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; } /** @@ -490,6 +490,8 @@ class CliDumper extends AbstractDumper if (isset($attr['href'])) { $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; } + } elseif ($attr['if_links'] ?? false) { + return ''; } return $value; @@ -548,6 +550,10 @@ class CliDumper extends AbstractDumper protected function endValue(Cursor $cursor) { + if (-1 === $cursor->hashType) { + return; + } + if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { $this->line .= ','; @@ -628,7 +634,7 @@ class CliDumper extends AbstractDumper private function getSourceLink(string $file, int $line) { if ($fmt = $this->displayOptions['fileLinkFormat']) { - return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file); + return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); } return false; diff --git a/src/Symfony/Component/VarDumper/Dumper/ContextualizedDumper.php b/src/Symfony/Component/VarDumper/Dumper/ContextualizedDumper.php new file mode 100644 index 0000000000..76384176ef --- /dev/null +++ b/src/Symfony/Component/VarDumper/Dumper/ContextualizedDumper.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Dumper; + +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; + +/** + * @author Kévin Thérage + */ +class ContextualizedDumper implements DataDumperInterface +{ + private $wrappedDumper; + private $contextProviders; + + /** + * @param ContextProviderInterface[] $contextProviders + */ + public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders) + { + $this->wrappedDumper = $wrappedDumper; + $this->contextProviders = $contextProviders; + } + + public function dump(Data $data) + { + $context = []; + foreach ($this->contextProviders as $contextProvider) { + $context[\get_class($contextProvider)] = $contextProvider->getContext(); + } + + $this->wrappedDumper->dump($data->withContext($context)); + } +} diff --git a/src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php b/src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php index 334d5879d4..2ffd65cfbc 100644 --- a/src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php @@ -53,6 +53,10 @@ Symfony\Component\VarDumper\Cloner\Data Object [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array + ( + ) + ) EOTXT; @@ -141,6 +145,10 @@ Symfony\Component\VarDumper\Cloner\Data Object [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array + ( + ) + ) EOTXT; @@ -309,6 +317,10 @@ Symfony\Component\VarDumper\Cloner\Data Object [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array + ( + ) + ) EOTXT; @@ -327,7 +339,7 @@ EOTXT; $clone = $cloner->cloneVar($data); $expected = <<<'EOTXT' -object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) { +object(Symfony\Component\VarDumper\Cloner\Data)#%d (7) { ["data":"Symfony\Component\VarDumper\Cloner\Data":private]=> array(2) { [0]=> @@ -372,6 +384,9 @@ object(Symfony\Component\VarDumper\Cloner\Data)#%i (6) { int(-1) ["useRefHandles":"Symfony\Component\VarDumper\Cloner\Data":private]=> int(-1) + ["context":"Symfony\Component\VarDumper\Cloner\Data":private]=> + array(0) { + } } EOTXT; @@ -432,6 +447,10 @@ Symfony\Component\VarDumper\Cloner\Data Object [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array + ( + ) + ) EOTXT; @@ -501,6 +520,10 @@ Symfony\Component\VarDumper\Cloner\Data Object [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [context:Symfony\Component\VarDumper\Cloner\Data:private] => Array + ( + ) + ) EOTXT; diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/ContextualizedDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/ContextualizedDumperTest.php new file mode 100644 index 0000000000..ba717940bc --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/ContextualizedDumperTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; +use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; + +/** + * @author Kévin Thérage + */ +class ContextualizedDumperTest extends TestCase +{ + public function testContextualizedCliDumper() + { + $wrappedDumper = new CliDumper('php://output'); + $wrappedDumper->setColors(true); + + $var = 'example'; + $href = sprintf('file://%s#L%s', __FILE__, 37); + $dumper = new ContextualizedDumper($wrappedDumper, [new SourceContextProvider()]); + $cloner = new VarCloner(); + $data = $cloner->cloneVar($var); + + ob_start(); + $dumper->dump($data); + $out = ob_get_clean(); + + $this->assertStringContainsString("\e]8;;{$href}\e\\\e[", $out); + $this->assertStringContainsString("m{$var}\e[", $out); + } +} diff --git a/src/Symfony/Component/VarDumper/VarDumper.php b/src/Symfony/Component/VarDumper/VarDumper.php index 009f662f3b..d336d5d52b 100644 --- a/src/Symfony/Component/VarDumper/VarDumper.php +++ b/src/Symfony/Component/VarDumper/VarDumper.php @@ -14,6 +14,8 @@ namespace Symfony\Component\VarDumper; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; +use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; // Load the global dump() function @@ -38,6 +40,8 @@ class VarDumper $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper() : new HtmlDumper(); } + $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]); + self::$handler = function ($var) use ($cloner, $dumper) { $dumper->dump($cloner->cloneVar($var)); }; From b416c12d29ba9ac7d70ddfa6ce44499057922978 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 29 Sep 2019 14:13:01 +0200 Subject: [PATCH 2/2] [4.4] Fix some leftovers --- .../Bundle/DebugBundle/Resources/config/services.xml | 2 +- .../HttpKernel/EventListener/DebugHandlersListener.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml index 9cb70d08cc..49772b4696 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -96,7 +96,7 @@ - + diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php index 841c772c32..03885a46b8 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -125,7 +125,11 @@ class DebugHandlersListener implements EventSubscriberInterface if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } - $this->exceptionHandler = function ($e) use ($app, $output) { + $this->exceptionHandler = static function (\Throwable $e) use ($app, $output) { + if (!$e instanceof \Exception) { + $e = new FatalThrowableError($e); + } + $app->renderException($e, $output); }; }