diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV5.php b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV5.php new file mode 100644 index 0000000000000..f388a0918e746 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV5.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; + +/** + * @author Maxime Steinhausser + * + * @internal + */ +class VarDumperServerListenerForV5 extends \PHPUnit_Framework_BaseTestListener implements ContextProviderInterface +{ + private $trait; + + public function __construct(string $host = null) + { + $this->trait = new VarDumperServerListenerTrait($host); + } + + public function startTest(\PHPUnit_Framework_Test $test) + { + $this->trait->startTest($test); + } + + public function getContext(): ?array + { + $this->trait->getContext(); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV6.php new file mode 100644 index 0000000000000..9857917c819de --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV6.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\Framework\BaseTestListener; +use PHPUnit\Framework\Test; +use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; + +/** + * @author Maxime Steinhausser + * + * @internal + */ +class VarDumperServerListenerForV6 extends BaseTestListener implements ContextProviderInterface +{ + private $trait; + + public function __construct(string $host = null) + { + $this->trait = new VarDumperServerListenerTrait($host); + } + + public function startTest(Test $test) + { + $this->trait->startTest($test); + } + + public function getContext(): ?array + { + $this->trait->getContext(); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV7.php new file mode 100644 index 0000000000000..f51530bf9db34 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerForV7.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestListenerDefaultImplementation; +use PHPUnit\Framework\TestSuite; +use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; + +/** + * @author Maxime Steinhausser + * + * @internal + */ +class VarDumperServerListenerForV7 implements TestListener, ContextProviderInterface +{ + use TestListenerDefaultImplementation; + + private $trait; + + public function __construct(string $host = null) + { + $this->trait = new VarDumperServerListenerTrait($host); + } + + public function startTestSuite(TestSuite $suite): void + { + $this->trait->startTest($suite); + } + + public function getContext(): ?array + { + $this->trait->getContext(); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerTrait.php new file mode 100644 index 0000000000000..41d8da9e51022 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Legacy/VarDumperServerListenerTrait.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Legacy; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\VarDumperServerListener; +use Symfony\Component\VarDumper\Dumper\ServerDumper; + +/** + * @author Maxime Steinhausser + * + * @internal + */ +class VarDumperServerListenerTrait +{ + /** @var TestCase|null */ + private $currentTestCase; + + public function __construct(string $host = null) + { + if (!class_exists(ServerDumper::class)) { + throw new \LogicException(sprintf('The "%s" class is required for using the "%s" listener. Install "symfony/var-dumper" version 4.1 or above.', ServerDumper::class, VarDumperServerListener::class)); + } + + ServerDumper::register($host, true, array('phpunit' => $this)); + } + + public function startTest($test) + { + if (!$test instanceof TestCase) { + $this->currentTestCase = null; + + return; + } + + $this->currentTestCase = $test; + } + + public function getContext(): ?array + { + if (!$this->currentTestCase) { + return null; + } + + return array( + 'identifier' => spl_object_hash($this->currentTestCase), + 'test_class' => \get_class($this->currentTestCase), + 'test_case' => $this->currentTestCase->getName(), + ); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/VarDumperServerListener.php b/src/Symfony/Bridge/PhpUnit/VarDumperServerListener.php new file mode 100644 index 0000000000000..bddf027b3326f --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/VarDumperServerListener.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { + class_alias('Symfony\Bridge\PhpUnit\Legacy\VarDumperServerListenerForV5', 'Symfony\Bridge\PhpUnit\VarDumperServerListener'); +} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) { + class_alias('Symfony\Bridge\PhpUnit\Legacy\VarDumperServerListenerForV6', 'Symfony\Bridge\PhpUnit\VarDumperServerListener'); +} else { + class_alias('Symfony\Bridge\PhpUnit\Legacy\VarDumperServerListenerForV7', 'Symfony\Bridge\PhpUnit\VarDumperServerListener'); +} + +if (false) { + class VarDumperServerListener + { + } +} diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 7338fca00db74..d244770f35e93 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -22,6 +22,7 @@ }, "suggest": { "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader", + "symfony/var-dumper": "To use the VarDumperServerListener", "ext-zip": "Zip support is required when using bin/simple-phpunit" }, "conflict": { diff --git a/src/Symfony/Component/VarDumper/Command/Descriptor/CliDescriptor.php b/src/Symfony/Component/VarDumper/Command/Descriptor/CliDescriptor.php index 562258fa36881..0258b6a0d4d16 100644 --- a/src/Symfony/Component/VarDumper/Command/Descriptor/CliDescriptor.php +++ b/src/Symfony/Component/VarDumper/Command/Descriptor/CliDescriptor.php @@ -43,7 +43,11 @@ public function describe(OutputInterface $output, Data $data, array $context, in $this->lastIdentifier = $clientId; $section = "Received from client #$clientId"; - if (isset($context['request'])) { + if (isset($context['phpunit'])) { + $phpunit = $context['phpunit']; + $this->lastIdentifier = $phpunit['identifier']; + $section = sprintf('%s::%s', $phpunit['test_class'], $phpunit['test_case']); + } elseif (isset($context['request'])) { $request = $context['request']; $this->lastIdentifier = $request['identifier']; $section = sprintf('%s %s', $request['method'], $request['uri']); diff --git a/src/Symfony/Component/VarDumper/Command/Descriptor/HtmlDescriptor.php b/src/Symfony/Component/VarDumper/Command/Descriptor/HtmlDescriptor.php index e11d22ae3311c..fccbb040d115a 100644 --- a/src/Symfony/Component/VarDumper/Command/Descriptor/HtmlDescriptor.php +++ b/src/Symfony/Component/VarDumper/Command/Descriptor/HtmlDescriptor.php @@ -42,7 +42,11 @@ public function describe(OutputInterface $output, Data $data, array $context, in } $title = '-'; - if (isset($context['request'])) { + if (isset($context['phpunit'])) { + $phpunit = $context['phpunit']; + $title = 'PhpUnit'.sprintf('%s::%s', $phpunit['test_class'], $phpunit['test_case']); + $dedupIdentifier = $phpunit['identifier']; + } elseif (isset($context['request'])) { $request = $context['request']; $title = sprintf('%s %s', $request['method'], $uri = $request['uri'], $uri); $dedupIdentifier = $request['identifier']; diff --git a/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php b/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php index 7a25fed61480d..e82186dc0ada6 100644 --- a/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php @@ -11,8 +11,16 @@ namespace Symfony\Component\VarDumper\Dumper; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; +use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider; +use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; +use Symfony\Component\VarDumper\VarDumper; /** * ServerDumper forwards serialized Data clones to a server. @@ -42,6 +50,42 @@ public function __construct(string $host, DataDumperInterface $wrappedDumper = n $this->contextProviders = $contextProviders; } + /** + * @final + */ + public static function getDefaultContextProviders(Request $request = null, string $projectDir = null): array + { + $contextProviders = array(); + + if ('cli' !== PHP_SAPI && ($request || class_exists(Request::class))) { + $requestStack = new RequestStack(); + $requestStack->push($request ?? Request::createFromGlobals()); + $contextProviders['request'] = new RequestContextProvider($requestStack); + } + + $fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null, $projectDir) : null; + + return $contextProviders + array( + 'cli' => new CliContextProvider(), + 'source' => new SourceContextProvider(null, $projectDir, $fileLinkFormatter), + ); + } + + /** + * @final + */ + public static function register(string $host = null, bool $lock = false, array $contextProviders = array()): ?callable + { + $contextProviders += self::getDefaultContextProviders(); + $host = $host ?? $_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912'; + $cloner = new VarCloner(); + $dumper = new self($host, VarDumper::getDefaultDumper(), $contextProviders); + + return VarDumper::setHandler(function ($var) use ($dumper, $cloner) { + $dumper->dump($cloner->cloneVar($var)); + }, $lock); + } + public function getContextProviders(): array { return $this->contextProviders; diff --git a/src/Symfony/Component/VarDumper/VarDumper.php b/src/Symfony/Component/VarDumper/VarDumper.php index 137c47478029a..1a00636263e21 100644 --- a/src/Symfony/Component/VarDumper/VarDumper.php +++ b/src/Symfony/Component/VarDumper/VarDumper.php @@ -13,6 +13,7 @@ use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; use Symfony\Component\VarDumper\Dumper\HtmlDumper; // Load the global dump() function @@ -24,12 +25,13 @@ class VarDumper { private static $handler; + private static $locked = false; public static function dump($var) { if (null === self::$handler) { $cloner = new VarCloner(); - $dumper = \in_array(PHP_SAPI, array('cli', 'phpdbg'), true) ? new CliDumper() : new HtmlDumper(); + $dumper = self::getDefaultDumper(); self::$handler = function ($var) use ($cloner, $dumper) { $dumper->dump($cloner->cloneVar($var)); }; @@ -38,11 +40,31 @@ public static function dump($var) return call_user_func(self::$handler, $var); } - public static function setHandler(callable $callable = null) + /** + * @final since 4.1 + */ + public static function setHandler(callable $callable = null/*, bool $lock = false*/)/*: ?callable*/ { + $lock = \func_num_args() > 1 ? func_get_arg(1) : false; $prevHandler = self::$handler; + + if (self::$locked) { + return $prevHandler; + } + if ($lock) { + self::$locked = true; + } + self::$handler = $callable; return $prevHandler; } + + /** + * @final + */ + public static function getDefaultDumper(): DataDumperInterface + { + return \in_array(PHP_SAPI, array('cli', 'phpdbg'), true) ? new CliDumper() : new HtmlDumper(); + } }