minor #26866 [VarDumper] Add controller & project dir to HTML output (ogizanagi)

This PR was squashed before being merged into the 4.1-dev branch (closes #26866).

Discussion
----------

[VarDumper] Add controller & project dir to HTML output

| Q             | A
| ------------- | ---
| Branch?       | master <!-- see below -->
| Bug fix?      | yes (fix callable controller support)
| New feature?  | yes <!-- don't forget to update src/**/CHANGELOG.md files -->
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | N/A   <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | N/A

Promise made, here is the missing controller info in HTML output (and additionally the project dir).

![screenshot 2018-04-08 a 19 19 30](https://user-images.githubusercontent.com/2211145/38470410-841875b6-3b62-11e8-97d3-bea9aeb70e13.PNG)
![screenshot 2018-04-08 a 19 19 19](https://user-images.githubusercontent.com/2211145/38470409-83eb98a2-3b62-11e8-9f45-f106eaf799a7.PNG)

Commits
-------

d286064 [VarDumper] Add controller & project dir to HTML output
This commit is contained in:
Nicolas Grekas 2018-04-29 09:56:09 +02:00
commit 3e320d6402
4 changed files with 99 additions and 17 deletions

View File

@ -37,6 +37,7 @@ class CliDescriptor implements DumpDescriptorInterface
public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void
{
$io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput(array()), $output);
$this->dumper->setColors($output->isDecorated());
$rows = array(array('date', date('r', $context['timestamp'])));
$lastIdentifier = $this->lastIdentifier;
@ -48,7 +49,7 @@ class CliDescriptor implements DumpDescriptorInterface
$this->lastIdentifier = $request['identifier'];
$section = sprintf('%s %s', $request['method'], $request['uri']);
if ($controller = $request['controller']) {
$rows[] = array('controller', $controller);
$rows[] = array('controller', rtrim($this->dumper->dump($controller, true), "\n"));
}
} elseif (isset($context['cli'])) {
$this->lastIdentifier = $context['cli']['identifier'];

View File

@ -44,6 +44,7 @@ class HtmlDescriptor implements DumpDescriptorInterface
$title = '-';
if (isset($context['request'])) {
$request = $context['request'];
$controller = "<span class='dumped-tag'>{$this->dumper->dump($request['controller'], true, array('maxDepth' => 0))}</span>";
$title = sprintf('<code>%s</code> <a href="%s">%s</a>', $request['method'], $uri = $request['uri'], $uri);
$dedupIdentifier = $request['identifier'];
} elseif (isset($context['cli'])) {
@ -53,31 +54,36 @@ class HtmlDescriptor implements DumpDescriptorInterface
$dedupIdentifier = uniqid('', true);
}
$contextText = array();
$sourceDescription = '';
if (isset($context['source'])) {
$source = $context['source'];
$projectDir = $source['project_dir'];
$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');
$tags = array_filter(array(
'controller' => $controller ?? null,
'project dir' => $projectDir ?? null,
));
$output->writeln(<<<HTML
<article data-dedup-id="$dedupIdentifier">
<header>
<h2>$title</h2>
<time class="text-small" title="$isoDate" datetime="$isoDate">
{$this->extractDate($context)}
</time>
<div class="row">
<h2 class="col">$title</h2>
<time class="col text-small" title="$isoDate" datetime="$isoDate">
{$this->extractDate($context)}
</time>
</div>
{$this->renderTags($tags)}
</header>
<section class="body">
<p class="text-small">
$contextText
$sourceDescription
</p>
{$this->dumper->dump($data, true)}
</section>
@ -90,4 +96,24 @@ HTML
{
return date($format, $context['timestamp']);
}
private function renderTags(array $tags): string
{
if (!$tags) {
return '';
}
$renderedTags = '';
foreach ($tags as $key => $value) {
$renderedTags .= sprintf('<li><span class="badge">%s</span>%s</li>', $key, $value);
}
return <<<HTML
<div class="row">
<ul class="tags">
$renderedTags
</ul>
</div>
HTML;
}
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\VarDumper\Dumper\ContextProvider;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\VarDumper\Cloner\VarCloner;
/**
* Tries to provide context from a request.
@ -21,10 +22,13 @@ use Symfony\Component\HttpFoundation\RequestStack;
final class RequestContextProvider implements ContextProviderInterface
{
private $requestStack;
private $cloner;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
$this->cloner = new VarCloner();
$this->cloner->setMaxItems(0);
}
public function getContext(): ?array
@ -33,10 +37,12 @@ final class RequestContextProvider implements ContextProviderInterface
return null;
}
$controller = $request->attributes->get('_controller');
return array(
'uri' => $request->getUri(),
'method' => $request->getMethod(),
'controller' => $request->attributes->get('_controller'),
'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller,
'identifier' => spl_object_hash($request),
);
}

View File

@ -37,36 +37,62 @@ article {
margin: 5px;
margin-bottom: 10px;
}
article > header {
article > header > .row {
display: flex;
flex-direction: row;
align-items: baseline;
margin-bottom: 10px;
}
article > header > * {
article > header > .row > .col {
flex: 1;
display: flex;
align-items: baseline;
}
article > header > h2 {
article > header > .row > h2 {
font-size: 14px;
color: #222;
font-weight: normal;
font-family: "Lucida Console", monospace, sans-serif;
word-break: break-all;
margin-right: 5px;
margin: 20px 5px 0 0;
user-select: all;
}
article > header > h2 > code {
article > header > .row > h2 > code {
white-space: nowrap;
user-select: none;
}
article > header > time {
article > header > .row > time.col {
flex: 0;
text-align: right;
white-space: nowrap;
color: #999;
font-style: italic;
}
article > header ul.tags {
list-style: none;
padding: 0;
margin: 0;
font-size: 12px;
}
article > header ul.tags > li {
user-select: all;
margin-bottom: 2px;
}
article > header ul.tags > li > span.badge {
display: inline-block;
padding: .25em .4em;
margin-right: 5px;
border-radius: 4px;
background-color: #6c757d3b;
color: #524d4d;
font-size: 12px;
text-align: center;
font-weight: 700;
line-height: 1;
white-space: nowrap;
vertical-align: baseline;
user-select: none;
}
article > section.body {
border: 1px solid #d8d8d8;
background: #FFF;
@ -80,3 +106,26 @@ pre.sf-dump {
.hidden {
display: none; !important
}
.dumped-tag > .sf-dump {
display: inline-block;
margin: 0;
padding: 1px 5px;
line-height: 1.4;
vertical-align: top;
background-color: transparent;
user-select: auto;
}
.dumped-tag > pre.sf-dump,
.dumped-tag > .sf-dump-default {
color: #CC7832;
background: none;
}
.dumped-tag > .sf-dump .sf-dump-str { color: #629755; }
.dumped-tag > .sf-dump .sf-dump-private,
.dumped-tag > .sf-dump .sf-dump-protected,
.dumped-tag > .sf-dump .sf-dump-public { color: #262626; }
.dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; }
.dumped-tag > .sf-dump .sf-dump-key { color: #789339; }
.dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; }
.dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; }
.dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; }