diff --git a/src/Action/DisplayDumpLocation/DisplayDumpLocationAction.php b/src/Action/DisplayDumpLocation/DisplayDumpLocationAction.php index 5dda335..3d02228 100644 --- a/src/Action/DisplayDumpLocation/DisplayDumpLocationAction.php +++ b/src/Action/DisplayDumpLocation/DisplayDumpLocationAction.php @@ -15,6 +15,7 @@ use Ekino\Drupal\Debug\Action\EventSubscriberActionInterface; use Ekino\Drupal\Debug\Kernel\Event\DebugKernelEvents; +use Ekino\Drupal\Debug\Action\DisplayDumpLocation\SourceContextProvider as BackPortedSourceContextProvider; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; @@ -35,16 +36,13 @@ public static function getSubscribedEvents(): array public function process(): void { - if (!\class_exists(SourceContextProvider::class)) { - return; - } - $cloner = new VarCloner(); $dumper = \in_array(\PHP_SAPI, array('cli', 'phpdbg'), true) ? new CliDumper() : new HtmlDumper(); + $sourceContextProvider = $this->getSourceContextProvider(); - VarDumper::setHandler(function ($var) use ($cloner, $dumper): void { - (function (): void { - list('name' => $name, 'file' => $file, 'line' => $line) = (new SourceContextProvider())->getContext(); + VarDumper::setHandler(function ($var) use ($cloner, $dumper, $sourceContextProvider ): void { + (function () use ($sourceContextProvider) : void { + list('name' => $name, 'file' => $file, 'line' => $line) = $sourceContextProvider->getContext(); $attr = array(); if ($this instanceof HtmlDumper) { @@ -66,4 +64,20 @@ public function process(): void $dumper->dump($cloner->cloneVar($var)); }); } + + /** + * Get the Source Context Provider. + * It will return an instance of the SourceContextProvider if existing. + * Otherwise, it will return an instance of + * the BackPortedSourceContextProvider. + */ + private function getSourceContextProvider() + { + if (!\class_exists(SourceContextProvider::class)) { + return new BackPortedSourceContextProvider(); + } + + return new SourceContextProvider(); + } + } diff --git a/src/Action/DisplayDumpLocation/SourceContextProvider.php b/src/Action/DisplayDumpLocation/SourceContextProvider.php new file mode 100644 index 0000000..828ced4 --- /dev/null +++ b/src/Action/DisplayDumpLocation/SourceContextProvider.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Ekino\Drupal\Debug\Action\DisplayDumpLocation; + +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\VarDumper; +use Twig\Template; + +/** + * Tries to provide context from sources (class name, file, line, code excerpt, ...). + * + * @author Nicolas Grekas
+ * @author Maxime Steinhausser '.$this->htmlEncode($src[$i - 1]).''.implode("\n", $fileExcerpt).'
';
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (false === $name) {
+ $name = str_replace('\\', '/', $file);
+ $name = substr($name, strrpos($name, '/') + 1);
+ }
+
+ $context = ['name' => $name, 'file' => $file, 'line' => $line];
+ $context['file_excerpt'] = $fileExcerpt;
+
+ if (null !== $this->projectDir) {
+ $context['project_dir'] = $this->projectDir;
+ if (0 === strpos($file, $this->projectDir)) {
+ $context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR);
+ }
+ }
+
+ if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) {
+ $context['file_link'] = $fileLink;
+ }
+
+ return $context;
+ }
+
+ private function htmlEncode(string $s): string
+ {
+ $html = '';
+
+ $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset);
+ $dumper->setDumpHeader('');
+ $dumper->setDumpBoundaries('', '');
+
+ $cloner = new VarCloner();
+ $dumper->dump($cloner->cloneVar($s));
+
+ return substr(strip_tags($html), 1, -1);
+ }
+}
diff --git a/tests/Integration/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php b/tests/Integration/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php
index ab98310..d7ee265 100644
--- a/tests/Integration/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php
+++ b/tests/Integration/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php
@@ -31,10 +31,7 @@ protected function doTestInitialBehaviorWithDrupalKernel(Client $client): void
*/
protected function doTestTargetedBehaviorWithDebugKernel(Client $client): void
{
- $this->assertThat($this->getDumpText($client), $this->logicalOr(
- $this->identicalTo("add_dump_die.module on line 5:\n\"fcy\"\n"),
- $this->identicalTo("\"fcy\"\n")
- ));
+ $this->assertSame("add_dump_die.module on line 5:\n\"fcy\"\n", $this->getDumpText($client));
}
/**
diff --git a/tests/Unit/src/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php b/tests/Unit/src/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php
index 0cf924f..63f5e51 100644
--- a/tests/Unit/src/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php
+++ b/tests/Unit/src/Action/DisplayDumpLocation/DisplayDumpLocationActionTest.php
@@ -14,6 +14,7 @@
namespace Ekino\Drupal\Debug\Tests\Unit\Action\DisplayDumpLocation;
use Ekino\Drupal\Debug\Action\DisplayDumpLocation\DisplayDumpLocationAction;
+use Ekino\Drupal\Debug\Action\DisplayDumpLocation\SourceContextProvider as BackPortedSourceContextProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
use Symfony\Component\VarDumper\VarDumper;
@@ -30,13 +31,24 @@ public function testGetSubscribedEvents(): void
public function testProcess(): void
{
VarDumper::setHandler(null);
-
(new DisplayDumpLocationAction())->process();
+ $this->assertAttributeInstanceOf(\Closure::class, 'handler', VarDumper::class);
+ }
+
+ public function testGetSourceContextProvider(): void
+ {
+ $expectedSourceContextProviderClass = (!\class_exists(SourceContextProvider::class)) ?
+ BackPortedSourceContextProvider::class :
+ SourceContextProvider::class;
+
+ $displayDumpLocationAction = new DisplayDumpLocationAction();
+ $displayDumpLocationActionReflector = new \ReflectionClass(DisplayDumpLocationAction::class);
+
+ $getSourceContextProviderMethod = $displayDumpLocationActionReflector->getMethod('getSourceContextProvider');
+ $getSourceContextProviderMethod->setAccessible( true );
+
+ $sourceContextProvider = $getSourceContextProviderMethod->invoke($displayDumpLocationAction);
- if (!\class_exists(SourceContextProvider::class)) {
- $this->assertAttributeInternalType('null', 'handler', VarDumper::class);
- } else {
- $this->assertAttributeInstanceOf(\Closure::class, 'handler', VarDumper::class);
- }
+ $this->assertInstanceOf($expectedSourceContextProviderClass, $sourceContextProvider);
}
}