null
%kernel.charset%
diff --git a/src/Symfony/Component/VarDumper/Cloner/Data.php b/src/Symfony/Component/VarDumper/Cloner/Data.php
index c4dd5eeb4b6d7..7e148bf49eab3 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 @@ public function withRefHandles($useRefHandles)
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 @@ public function seek($key)
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 f390cd7832567..2cef630cf0173 100644
--- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
+++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
@@ -83,7 +83,7 @@ public function __construct($output = null, string $charset = null, int $flags =
]);
}
- $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 @@ protected function style($style, $value, $attr = [])
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 @@ protected function dumpLine($depth, $endOfValue = false)
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 @@ private function isWindowsTrueColor(): bool
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 0000000000000..76384176ef026
--- /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 334d5879d4997..2ffd65cfbc45d 100644
--- a/src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php
+++ b/src/Symfony/Component/VarDumper/Tests/Cloner/VarClonerTest.php
@@ -53,6 +53,10 @@ public function testMaxIntBoundary()
[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 @@ public function testClone()
[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 @@ public function testLimits()
[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 @@ public function testJsonCast()
$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 @@ public function testJsonCast()
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 @@ public function testCaster()
[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 @@ public function testPhp74()
[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 0000000000000..ba717940bc7d2
--- /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 009f662f3bf27..d336d5d52bc98 100644
--- a/src/Symfony/Component/VarDumper/VarDumper.php
+++ b/src/Symfony/Component/VarDumper/VarDumper.php
@@ -14,6 +14,8 @@
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 @@ public static function dump($var)
$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));
};