diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
index 6ac84813c2..67579511fe 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
@@ -124,7 +124,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);
};
}
diff --git a/src/Symfony/Component/VarDumper/Cloner/Data.php b/src/Symfony/Component/VarDumper/Cloner/Data.php
index d3aaaf204f..e907197b70 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()
@@ -223,6 +225,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.
*
@@ -277,7 +290,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 0c25c075f3..207b716ea5 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';
}
/**
@@ -486,6 +486,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;
@@ -544,6 +546,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 .= ',';
@@ -624,7 +630,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 f3a928d807..43869dd2eb 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));
};