-
-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[VarDumper] Introduce a new way to collect dumps through a server dumper
- Loading branch information
Showing
16 changed files
with
1,035 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\VarDumper\Command\Descriptor; | ||
|
||
use Symfony\Component\Console\Input\ArrayInput; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Component\Console\Style\SymfonyStyle; | ||
use Symfony\Component\VarDumper\Cloner\Data; | ||
use Symfony\Component\VarDumper\Dumper\CliDumper; | ||
|
||
/** | ||
* Describe collected data clones for cli output. | ||
* | ||
* @author Maxime Steinhausser <[email protected]> | ||
* | ||
* @final | ||
*/ | ||
class CliDescriptor implements DumpDescriptorInterface | ||
{ | ||
private $dumper; | ||
private $lastIdentifier; | ||
|
||
public function __construct(CliDumper $dumper) | ||
{ | ||
$this->dumper = $dumper; | ||
} | ||
|
||
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void | ||
{ | ||
$io = new SymfonyStyle(new ArrayInput(array()), $output); | ||
|
||
$rows = array(array('date', date('r', $context['timestamp']))); | ||
$lastIdentifier = $this->lastIdentifier; | ||
$this->lastIdentifier = $clientId; | ||
|
||
$section = "Received from client #$clientId"; | ||
if (isset($context['request'])) { | ||
$request = $context['request']; | ||
$this->lastIdentifier = $request['identifier']; | ||
$section = sprintf('%s %s', $request['method'], $request['uri']); | ||
if ($controller = $request['controller']) { | ||
$rows[] = array('controller', $controller); | ||
} | ||
} elseif (isset($context['cli'])) { | ||
$this->lastIdentifier = $context['cli']['identifier']; | ||
$section = '$ '.$context['cli']['command_line']; | ||
} | ||
|
||
if ($this->lastIdentifier !== $lastIdentifier) { | ||
$io->section($section); | ||
} | ||
|
||
if (isset($context['source'])) { | ||
$source = $context['source']; | ||
$rows[] = array('source', sprintf('%s on line %d', $source['name'], $source['line'])); | ||
$file = $source['file_relative'] ?? $source['file']; | ||
$rows[] = array('file', $file); | ||
$fileLink = $source['file_link'] ?? null; | ||
} | ||
|
||
$io->table(array(), $rows); | ||
|
||
if (isset($fileLink)) { | ||
$io->writeln(array('<info>Open source in your IDE/browser:</info>', $fileLink)); | ||
$io->newLine(); | ||
} | ||
|
||
$this->dumper->dump($data); | ||
$io->newLine(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\VarDumper\Command\Descriptor; | ||
|
||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Component\VarDumper\Cloner\Data; | ||
|
||
/** | ||
* @author Maxime Steinhausser <[email protected]> | ||
*/ | ||
interface DumpDescriptorInterface | ||
{ | ||
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\VarDumper\Command\Descriptor; | ||
|
||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Component\VarDumper\Cloner\Data; | ||
use Symfony\Component\VarDumper\Dumper\HtmlDumper; | ||
|
||
/** | ||
* Describe collected data clones for html output. | ||
* | ||
* @author Maxime Steinhausser <[email protected]> | ||
* | ||
* @final | ||
*/ | ||
class HtmlDescriptor implements DumpDescriptorInterface | ||
{ | ||
private $dumper; | ||
private $initialized = false; | ||
|
||
public function __construct(HtmlDumper $dumper) | ||
{ | ||
$this->dumper = $dumper; | ||
} | ||
|
||
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void | ||
{ | ||
if (!$this->initialized) { | ||
$styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css'); | ||
$scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js'); | ||
$output->writeln("<style>$styles</style><script>$scripts</script>"); | ||
$this->initialized = true; | ||
} | ||
|
||
$title = '-'; | ||
if (isset($context['request'])) { | ||
$request = $context['request']; | ||
$title = sprintf('<code>%s</code> <a href="%s">%s</a>', $request['method'], $uri = $request['uri'], $uri); | ||
$dedupIdentifier = $request['identifier']; | ||
} elseif (isset($context['cli'])) { | ||
$title = '<code>$ </code>'.$context['cli']['command_line']; | ||
$dedupIdentifier = $context['cli']['identifier']; | ||
} else { | ||
$dedupIdentifier = uniqid('', true); | ||
} | ||
|
||
$contextText = array(); | ||
if (isset($context['source'])) { | ||
$source = $context['source']; | ||
$sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']); | ||
if (isset($source['file_link'])) { | ||
$sourceDescription = sprintf('<a href="%s">%s</a>', $source['file_link'], $sourceDescription); | ||
} | ||
|
||
$contextText[] = $sourceDescription; | ||
} | ||
|
||
$contextText = implode('<br />', $contextText); | ||
$isoDate = $this->extractDate($context, 'c'); | ||
|
||
$output->writeln(<<<HTML | ||
<article data-dedup-id="$dedupIdentifier"> | ||
<header> | ||
<h2>$title</h2> | ||
<time class="text-small" title="$isoDate" datetime="$isoDate"> | ||
{$this->extractDate($context)} | ||
</time> | ||
</header> | ||
<section class="body"> | ||
<p class="text-small"> | ||
$contextText | ||
</p> | ||
{$this->dumper->dump($data, true)} | ||
</section> | ||
</article> | ||
HTML | ||
); | ||
} | ||
|
||
private function extractDate(array $context, string $format = 'r'): string | ||
{ | ||
return date($format, $context['timestamp']); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\VarDumper\Command; | ||
|
||
use Psr\Log\LoggerInterface; | ||
use Symfony\Component\Console\Command\Command; | ||
use Symfony\Component\Console\Exception\InvalidArgumentException; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Input\InputOption; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Component\Console\Style\SymfonyStyle; | ||
use Symfony\Component\VarDumper\Cloner\Data; | ||
use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; | ||
use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface; | ||
use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; | ||
use Symfony\Component\VarDumper\Dumper\CliDumper; | ||
use Symfony\Component\VarDumper\Dumper\HtmlDumper; | ||
use Symfony\Component\VarDumper\Server\DumpServer; | ||
|
||
/** | ||
* Starts a dump server to collect and output dumps on a single place with multiple formats support. | ||
* | ||
* @author Maxime Steinhausser <[email protected]> | ||
* | ||
* @final | ||
*/ | ||
class ServerDumpCommand extends Command | ||
{ | ||
protected static $defaultName = 'server:dump'; | ||
|
||
private $logger; | ||
|
||
/** @var DumpDescriptorInterface[] */ | ||
private $descriptors; | ||
|
||
public function __construct(array $descriptors = array(), LoggerInterface $logger = null) | ||
{ | ||
$this->logger = $logger; | ||
$this->descriptors = $descriptors + array( | ||
'cli' => new CliDescriptor(new CliDumper()), | ||
'html' => new HtmlDescriptor(new HtmlDumper()), | ||
); | ||
|
||
parent::__construct(); | ||
} | ||
|
||
protected function configure() | ||
{ | ||
$availableFormats = implode(', ', array_keys($this->descriptors)); | ||
|
||
$this | ||
->addOption('format', null, InputOption::VALUE_REQUIRED, "The output format ($availableFormats)", 'cli') | ||
->setDescription('Starts a dump server that collects and displays dumps in a single place') | ||
->setHelp(<<<'EOF' | ||
<info>%command.name%</info> starts a dump server that collects and displays | ||
dumps in a single place for debugging you application: | ||
<info>php %command.full_name%</info> | ||
You can consult dumped data in HTML format in your browser by providing the <comment>--format=html</comment> option | ||
and redirecting the output to a file: | ||
<info>php %command.full_name% --format="html" > dump.html</info> | ||
|
||
EOF | ||
) | ||
; | ||
} | ||
|
||
protected function execute(InputInterface $input, OutputInterface $output) | ||
{ | ||
$io = new SymfonyStyle($input, $output); | ||
$format = $input->getOption('format'); | ||
|
||
if (!$descriptor = $this->descriptors[$format] ?? null) { | ||
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format)); | ||
} | ||
|
||
$errorIo = $io->getErrorStyle(); | ||
$errorIo->title('Symfony Var Dumper Server'); | ||
|
||
$server = new DumpServer(null, $this->logger); | ||
$server->start(); | ||
|
||
$errorIo->success(sprintf('Server listening on %s', $server->getHost())); | ||
$errorIo->comment('Quit the server with CONTROL-C.'); | ||
|
||
$server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $output) { | ||
$descriptor->describe($output, $data, $context, $clientId); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\VarDumper\Dumper\ContextProvider; | ||
|
||
/** | ||
* Tries to provide context on CLI. | ||
* | ||
* @author Maxime Steinhausser <[email protected]> | ||
*/ | ||
final class CliContextProvider implements ContextProviderInterface | ||
{ | ||
public function getContext(): ?array | ||
{ | ||
if ('cli' !== PHP_SAPI) { | ||
return null; | ||
} | ||
|
||
return array( | ||
'command_line' => $commandLine = implode(' ', $_SERVER['argv']), | ||
'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\VarDumper\Dumper\ContextProvider; | ||
|
||
/** | ||
* Interface to provide contextual data about dump data clones sent to a server. | ||
* | ||
* @author Maxime Steinhausser <[email protected]> | ||
*/ | ||
interface ContextProviderInterface | ||
{ | ||
/** | ||
* @return array|null Context data or null if unable to provide any context | ||
*/ | ||
public function getContext(): ?array; | ||
} |
Oops, something went wrong.