From 1435d677bec7fb49350acb84d8bc8100024b83c9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 15 Jun 2018 20:19:15 +0200 Subject: [PATCH] [VarDumper] Fix dumping by splitting Server/Connection out of Dumper/ServerDumper --- .../DependencyInjection/DebugExtension.php | 26 +++-- .../DebugBundle/Resources/config/services.xml | 8 +- src/Symfony/Bundle/DebugBundle/composer.json | 2 +- .../DataCollector/DumpDataCollector.php | 27 +++--- .../HttpKernel/EventListener/DumpListener.php | 14 ++- .../DataCollector/DumpDataCollectorTest.php | 8 +- .../Component/HttpKernel/composer.json | 4 +- .../VarDumper/Dumper/AbstractDumper.php | 2 +- .../VarDumper/Dumper/ServerDumper.php | 76 ++------------- .../Resources/css/htmlDescriptor.css | 14 ++- .../Component/VarDumper/Server/Connection.php | 97 +++++++++++++++++++ .../Tests/Dumper/ServerDumperTest.php | 10 +- .../VarDumper/Tests/Fixtures/dump_server.php | 2 + .../VarDumper/Tests/Server/ConnectionTest.php | 88 +++++++++++++++++ 14 files changed, 252 insertions(+), 126 deletions(-) create mode 100644 src/Symfony/Component/VarDumper/Server/Connection.php create mode 100644 src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php diff --git a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php index a81c495970..aa124a8c03 100644 --- a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php +++ b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php @@ -17,7 +17,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\VarDumper\Dumper\ServerDumper; /** * DebugExtension. @@ -43,20 +42,21 @@ class DebugExtension extends Extension ->addMethodCall('setMaxString', array($config['max_string_length'])); if (null === $config['dump_destination']) { - //no-op + $container->getDefinition('var_dumper.command.server_dump') + ->setClass(ServerDumpPlaceholderCommand::class) + ; } elseif (0 === strpos($config['dump_destination'], 'tcp://')) { - $serverDumperHost = $config['dump_destination']; $container->getDefinition('debug.dump_listener') - ->replaceArgument(1, new Reference('var_dumper.server_dumper')) + ->replaceArgument(2, new Reference('var_dumper.server_connection')) ; $container->getDefinition('data_collector.dump') - ->replaceArgument(4, new Reference('var_dumper.server_dumper')) + ->replaceArgument(4, new Reference('var_dumper.server_connection')) ; $container->getDefinition('var_dumper.dump_server') - ->replaceArgument(0, $serverDumperHost) + ->replaceArgument(0, $config['dump_destination']) ; - $container->getDefinition('var_dumper.server_dumper') - ->replaceArgument(0, $serverDumperHost) + $container->getDefinition('var_dumper.server_connection') + ->replaceArgument(0, $config['dump_destination']) ; } else { $container->getDefinition('var_dumper.cli_dumper') @@ -65,13 +65,9 @@ class DebugExtension extends Extension $container->getDefinition('data_collector.dump') ->replaceArgument(4, new Reference('var_dumper.cli_dumper')) ; - } - - if (!isset($serverDumperHost)) { - $container->getDefinition('var_dumper.command.server_dump')->setClass(ServerDumpPlaceholderCommand::class); - if (!class_exists(ServerDumper::class)) { - $container->removeDefinition('var_dumper.command.server_dump'); - } + $container->getDefinition('var_dumper.command.server_dump') + ->setClass(ServerDumpPlaceholderCommand::class) + ; } } diff --git a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml index a0bbde8d3d..b878ee5b62 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -23,13 +23,14 @@ %kernel.charset% - null + null + null @@ -50,9 +51,8 @@ - - null - + + diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index 7897621b54..e909b45814 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -20,7 +20,7 @@ "ext-xml": "*", "symfony/http-kernel": "~3.4|~4.0", "symfony/twig-bridge": "~3.4|~4.0", - "symfony/var-dumper": "~4.1" + "symfony/var-dumper": "^4.1.1" }, "require-dev": { "symfony/config": "~3.4|~4.0", diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index cab37ecafe..44d6c5dc73 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -21,7 +21,7 @@ use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Dumper\DataDumperInterface; -use Symfony\Component\VarDumper\Dumper\ServerDumper; +use Symfony\Component\VarDumper\Server\Connection; /** * @author Nicolas Grekas @@ -38,17 +38,18 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface private $charset; private $requestStack; private $dumper; - private $dumperIsInjected; private $sourceContextProvider; - public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, string $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null) + /** + * @param DataDumperInterface|Connection|null $dumper + */ + public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, string $charset = null, RequestStack $requestStack = null, $dumper = null) { $this->stopwatch = $stopwatch; $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->charset = $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'; $this->requestStack = $requestStack; $this->dumper = $dumper; - $this->dumperIsInjected = null !== $dumper; // All clones share these properties by reference: $this->rootRefs = array( @@ -58,7 +59,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface &$this->clonesCount, ); - $this->sourceContextProvider = $dumper instanceof ServerDumper && isset($dumper->getContextProviders()['source']) ? $dumper->getContextProviders()['source'] : new SourceContextProvider($this->charset); + $this->sourceContextProvider = $dumper instanceof Connection && isset($dumper->getContextProviders()['source']) ? $dumper->getContextProviders()['source'] : new SourceContextProvider($this->charset); } public function __clone() @@ -71,14 +72,17 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface if ($this->stopwatch) { $this->stopwatch->start('dump'); } - if ($this->isCollected && !$this->dumper) { - $this->isCollected = false; - } list('name' => $name, 'file' => $file, 'line' => $line, 'file_excerpt' => $fileExcerpt) = $this->sourceContextProvider->getContext(); - if ($this->dumper) { + if ($this->dumper instanceof Connection) { + if (!$this->dumper->write($data)) { + $this->isCollected = false; + } + } elseif ($this->dumper) { $this->doDump($this->dumper, $data, $name, $file, $line); + } else { + $this->isCollected = false; } $this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt'); @@ -141,9 +145,6 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface $this->data = array(); $this->dataCount = 0; $this->isCollected = true; - if (!$this->dumperIsInjected) { - $this->dumper = null; - } return $ser; } @@ -245,7 +246,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface }; $contextDumper = $contextDumper->bindTo($dumper, $dumper); $contextDumper($name, $file, $line, $this->fileLinkFormat); - } elseif (!$dumper instanceof ServerDumper) { + } else { $cloner = new VarCloner(); $dumper->dump($cloner->cloneVar($name.' on line '.$line.':')); } diff --git a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php b/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php index de19e13113..3acbe7d46c 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\VarDumper\Cloner\ClonerInterface; use Symfony\Component\VarDumper\Dumper\DataDumperInterface; +use Symfony\Component\VarDumper\Server\Connection; use Symfony\Component\VarDumper\VarDumper; /** @@ -26,20 +27,27 @@ class DumpListener implements EventSubscriberInterface { private $cloner; private $dumper; + private $connection; - public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper) + public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper, Connection $connection = null) { $this->cloner = $cloner; $this->dumper = $dumper; + $this->connection = $connection; } public function configure() { $cloner = $this->cloner; $dumper = $this->dumper; + $connection = $this->connection; - VarDumper::setHandler(function ($var) use ($cloner, $dumper) { - $dumper->dump($cloner->cloneVar($var)); + VarDumper::setHandler(static function ($var) use ($cloner, $dumper, $connection) { + $data = $cloner->cloneVar($var); + + if (!$connection || !$connection->write($data)) { + $dumper->dump($data); + } }); } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index adfb3567c6..57384a79ff 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -17,7 +17,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\CliDumper; -use Symfony\Component\VarDumper\Dumper\ServerDumper; +use Symfony\Component\VarDumper\Server\Connection; /** * @author Nicolas Grekas @@ -57,13 +57,13 @@ class DumpDataCollectorTest extends TestCase $this->assertSame('a:2:{i:0;b:0;i:1;s:5:"UTF-8";}', $collector->serialize()); } - public function testDumpWithServerDumper() + public function testDumpWithServerConnection() { $data = new Data(array(array(123))); // Server is up, server dumper is used - $serverDumper = $this->getMockBuilder(ServerDumper::class)->disableOriginalConstructor()->getMock(); - $serverDumper->expects($this->once())->method('dump'); + $serverDumper = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); + $serverDumper->expects($this->once())->method('write')->willReturn(true); $collector = new DumpDataCollector(null, null, null, null, $serverDumper); $collector->dump($data); diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index e610f27418..453561f62d 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -37,7 +37,7 @@ "symfony/stopwatch": "~3.4|~4.0", "symfony/templating": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", - "symfony/var-dumper": "~4.1", + "symfony/var-dumper": "^4.1.1", "psr/cache": "~1.0" }, "provide": { @@ -46,7 +46,7 @@ "conflict": { "symfony/config": "<3.4", "symfony/dependency-injection": "<4.1", - "symfony/var-dumper": "<4.1", + "symfony/var-dumper": "<4.1.1", "twig/twig": "<1.34|<2.4,>=2" }, "suggest": { diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php index 73b5f643a7..01437d94c7 100644 --- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php @@ -164,7 +164,7 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface */ protected function dumpLine($depth) { - call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad); + \call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad); $this->line = ''; } diff --git a/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php b/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php index 7a25fed614..32e1f124cc 100644 --- a/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/ServerDumper.php @@ -13,6 +13,7 @@ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; +use Symfony\Component\VarDumper\Server\Connection; /** * ServerDumper forwards serialized Data clones to a server. @@ -21,10 +22,8 @@ use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; */ class ServerDumper implements DataDumperInterface { - private $host; + private $connection; private $wrappedDumper; - private $contextProviders; - private $socket; /** * @param string $host The server host @@ -33,83 +32,22 @@ class ServerDumper implements DataDumperInterface */ public function __construct(string $host, DataDumperInterface $wrappedDumper = null, array $contextProviders = array()) { - if (false === strpos($host, '://')) { - $host = 'tcp://'.$host; - } - - $this->host = $host; + $this->connection = new Connection($host, $contextProviders); $this->wrappedDumper = $wrappedDumper; - $this->contextProviders = $contextProviders; } public function getContextProviders(): array { - return $this->contextProviders; + return $this->connection->getContextProviders(); } /** * {@inheritdoc} */ - public function dump(Data $data, $output = null): void + public function dump(Data $data) { - set_error_handler(array(self::class, 'nullErrorHandler')); - - $failed = false; - try { - if (!$this->socket = $this->socket ?: $this->createSocket()) { - $failed = true; - - return; - } - } finally { - restore_error_handler(); - if ($failed && $this->wrappedDumper) { - $this->wrappedDumper->dump($data); - } + if (!$this->connection->write($data) && $this->wrappedDumper) { + $this->wrappedDumper->dump($data); } - - set_error_handler(array(self::class, 'nullErrorHandler')); - - $context = array('timestamp' => time()); - foreach ($this->contextProviders as $name => $provider) { - $context[$name] = $provider->getContext(); - } - $context = array_filter($context); - - $encodedPayload = base64_encode(serialize(array($data, $context)))."\n"; - $failed = false; - - try { - $retry = 3; - while ($retry > 0 && $failed = (-1 === stream_socket_sendto($this->socket, $encodedPayload))) { - stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); - if ($failed = !$this->socket = $this->createSocket()) { - break; - } - - --$retry; - } - } finally { - restore_error_handler(); - if ($failed && $this->wrappedDumper) { - $this->wrappedDumper->dump($data); - } - } - } - - private static function nullErrorHandler() - { - // noop - } - - private function createSocket() - { - $socket = stream_socket_client($this->host, $errno, $errstr, 1, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT); - - if ($socket) { - stream_set_blocking($socket, false); - } - - return $socket; } } diff --git a/src/Symfony/Component/VarDumper/Resources/css/htmlDescriptor.css b/src/Symfony/Component/VarDumper/Resources/css/htmlDescriptor.css index babb7ddbbc..4277c6809b 100644 --- a/src/Symfony/Component/VarDumper/Resources/css/htmlDescriptor.css +++ b/src/Symfony/Component/VarDumper/Resources/css/htmlDescriptor.css @@ -22,14 +22,6 @@ a { a:hover { text-decoration: underline; } -code { - color: #cc2255; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; - border-radius: 3px; - margin-right: 5px; - padding: 0 3px; -} .text-small { font-size: 12px !important; } @@ -60,6 +52,12 @@ article > header > .row > h2 { article > header > .row > h2 > code { white-space: nowrap; user-select: none; + color: #cc2255; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; + border-radius: 3px; + margin-right: 5px; + padding: 0 3px; } article > header > .row > time.col { flex: 0; diff --git a/src/Symfony/Component/VarDumper/Server/Connection.php b/src/Symfony/Component/VarDumper/Server/Connection.php new file mode 100644 index 0000000000..d447b0a98e --- /dev/null +++ b/src/Symfony/Component/VarDumper/Server/Connection.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Server; + +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; + +/** + * Forwards serialized Data clones to a server. + * + * @author Maxime Steinhausser + */ +class Connection +{ + private $host; + private $contextProviders; + private $socket; + + /** + * @param string $host The server host + * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name + */ + public function __construct(string $host, array $contextProviders = array()) + { + if (false === strpos($host, '://')) { + $host = 'tcp://'.$host; + } + + $this->host = $host; + $this->contextProviders = $contextProviders; + } + + public function getContextProviders(): array + { + return $this->contextProviders; + } + + public function write(Data $data): bool + { + $socketIsFresh = !$this->socket; + if (!$this->socket = $this->socket ?: $this->createSocket()) { + return false; + } + + $context = array('timestamp' => microtime(true)); + foreach ($this->contextProviders as $name => $provider) { + $context[$name] = $provider->getContext(); + } + $context = array_filter($context); + $encodedPayload = base64_encode(serialize(array($data, $context)))."\n"; + + set_error_handler(array(self::class, 'nullErrorHandler')); + try { + if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { + return true; + } + if (!$socketIsFresh) { + stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); + fclose($this->socket); + $this->socket = $this->createSocket(); + } + if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { + return true; + } + } finally { + restore_error_handler(); + } + + return false; + } + + private static function nullErrorHandler($t, $m) + { + // no-op + } + + private function createSocket() + { + set_error_handler(array(self::class, 'nullErrorHandler')); + try { + return stream_socket_client($this->host, $errno, $errstr, 3, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT); + } finally { + restore_error_handler(); + } + + return $socket; + } +} diff --git a/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php b/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php index f41d4c2641..288391bfcf 100644 --- a/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Dumper/ServerDumperTest.php @@ -55,26 +55,24 @@ class ServerDumperTest extends TestCase $dumped = null; $process = $this->getServerProcess(); - $process->start(function ($type, $buffer) use ($process, &$dumped) { + $process->start(function ($type, $buffer) use ($process, &$dumped, $dumper, $data) { if (Process::ERR === $type) { $process->stop(); $this->fail(); + } elseif ("READY\n" === $buffer) { + $dumper->dump($data); } else { $dumped .= $buffer; } }); - sleep(3); - - $dumper->dump($data); - $process->wait(); $this->assertTrue($process->isSuccessful()); $this->assertStringMatchesFormat(<<<'DUMP' (3) "foo" [ - "timestamp" => %d + "timestamp" => %d.%d "foo_provider" => [ (3) "foo" ] diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/dump_server.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/dump_server.php index 5c79ea5151..ed8bbfba58 100644 --- a/src/Symfony/Component/VarDumper/Tests/Fixtures/dump_server.php +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/dump_server.php @@ -29,6 +29,8 @@ $server = new DumpServer(getenv('VAR_DUMPER_SERVER')); $server->start(); +echo "READY\n"; + $server->listen(function (Data $data, array $context, $clientId) { dump((string) $data, $context, $clientId); diff --git a/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php b/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php new file mode 100644 index 0000000000..eeb7495dac --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Server/ConnectionTest.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Server; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\PhpProcess; +use Symfony\Component\Process\Process; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; +use Symfony\Component\VarDumper\Server\Connection; + +class ConnectionTest extends TestCase +{ + private const VAR_DUMPER_SERVER = 'tcp://127.0.0.1:9913'; + + public function testDump() + { + $cloner = new VarCloner(); + $data = $cloner->cloneVar('foo'); + $connection = new Connection(self::VAR_DUMPER_SERVER, array( + 'foo_provider' => new class() implements ContextProviderInterface { + public function getContext(): ?array + { + return array('foo'); + } + }, + )); + + $dumped = null; + $process = $this->getServerProcess(); + $process->start(function ($type, $buffer) use ($process, &$dumped, $connection, $data) { + if (Process::ERR === $type) { + $process->stop(); + $this->fail(); + } elseif ("READY\n" === $buffer) { + $connection->write($data); + } else { + $dumped .= $buffer; + } + }); + + $process->wait(); + + $this->assertTrue($process->isSuccessful()); + $this->assertStringMatchesFormat(<<<'DUMP' +(3) "foo" +[ + "timestamp" => %d.%d + "foo_provider" => [ + (3) "foo" + ] +] +%d + +DUMP + , $dumped); + } + + public function testNoServer() + { + $cloner = new VarCloner(); + $data = $cloner->cloneVar('foo'); + $connection = new Connection(self::VAR_DUMPER_SERVER); + $start = microtime(true); + $this->assertFalse($connection->write($data)); + $this->assertLessThan(1, microtime(true) - $start); + } + + private function getServerProcess(): Process + { + $process = new PhpProcess(file_get_contents(__DIR__.'/../Fixtures/dump_server.php'), null, array( + 'COMPONENT_ROOT' => __DIR__.'/../../', + 'VAR_DUMPER_SERVER' => self::VAR_DUMPER_SERVER, + )); + $process->inheritEnvironmentVariables(true); + + return $process->setTimeout(9); + } +}