8000 [VarDumper] Introduce a new way to collect dumps through a server dumper · symfony/symfony@3db1404 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3db1404

Browse files
committed
[VarDumper] Introduce a new way to collect dumps through a server dumper
1 parent 9e82562 commit 3db1404

23 files changed

+1221
-97
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CHANGELOG
2+
=========
3+
4+
4.1.0
5+
-----
6+
7+
* Added the `debug.server_dump` config option allowing to enable and configure
8+
the `ServerDumper` and `server:dump` command
9+
* Added the `server:dump` command to run a server collecting and displaying
10+
dumps on a single place with multiple formats support

src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public function getConfigTreeBuilder()
5151
->example('php://stderr')
5252
->defaultNull()
5353
->end()
54+
->booleanNode('server_dump')
55+
->info('Enables the server dumper allowing to send dump data clone to a centralized server')
56+
->treatNullLike(true)
57+
->defaultFalse()
58+
->end()
5459
->end()
5560
;
5661

src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ public function load(array $configs, ContainerBuilder $container)
4848
->replaceArgument(4, new Reference('var_dumper.cli_dumper'))
4949
;
5050
}
51+
52+
if ($config['server_dump']) {
53+
$container->getDefinition('debug.dump_listener')
54+
->replaceArgument(1, new Reference('var_dumper.server_dumper'))
55+
;
56+
} else {
57+
$container->removeDefinition('var_dumper.server_dumper');
58+
$container->removeDefinition('var_dumper.command.server_dump');
59+
}
5160
}
5261

5362
/**

src/Symfony/Bundle/DebugBundle/Resources/config/services.xml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<argument>%kernel.charset%</argument>
2121
<argument type="service" id="request_stack" />
2222
<argument>null</argument><!-- var_dumper.cli_dumper when debug.dump_destination is set -->
23+
<argument type="service" id="var_dumper.server_dumper" on-invalid="null" />
2324
</service>
2425

2526
<service id="debug.dump_listener" class="Symfony\Component\HttpKernel\EventListener\DumpListener">
@@ -44,5 +45,43 @@
4445
</argument>
4546
</call>
4647
</service>
48+
<service id="var_dumper.server_dumper" class="Symfony\Component\VarDumper\Dumper\ServerDumper">
49+
<argument>null</argument> <!-- server host -->
50+
<argument type="service" id="var_dumper.cli_dumper" />
51+
<argument type="collection">
52+
<argument type="service" key="source">
53+
<service class="Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider">
54+
<argument>%kernel.charset%</argument>
55+
<argument type="string">%kernel.project_dir%</argument>
56+
<argument type="service" id="debug.file_link_formatter" on-invalid="null" />
57+
</service>
58+
</argument>
59+
<argument type="service" key="request">
60+
<service class="Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider">
61+
<argument type="service" id="request_stack" />
62+
</service>
63+
</argument>
64+
<argument type="service" key="cli">
65+
<service class="Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider" />
66+
</argument>
67+
</argument>
68+
</service>
69+
<service id="var_dumper.command.server_dump" class="Symfony\Component\VarDumper\Command\ServerDumpCommand">
70+
<argument type="collection">
71+
<argument type="service" key="cli">
72+
<service class="Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor">
73+
<argument type="service" id="var_dumper.cli_dumper" />
74+
</service>
75+
</argument>
76+
<argument type="service" key="html">
77+
<service class="Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor">
78+
<argument type="service" id="var_dumper.html_dumper" />
79+
</service>
80+
</argument>
81+
</argument>
82+
<argument type="service" id="logger" on-invalid="null" />
83+
<tag name="console.command" command="server:dump" />
84+
<tag name="monolog.logger" channel="debug" />
85+
</service>
4786
</services>
4887
</container>

src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php

Lines changed: 49 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
use Symfony\Component\VarDumper\Cloner\Data;
1919
use Symfony\Component\VarDumper\Cloner\VarCloner;
2020
use Symfony\Component\VarDumper\Dumper\CliDumper;
21+
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
2122
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
2223
use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
23-
use Twig\Template;
24+
use Symfony\Component\VarDumper\Dumper\ServerDumper;
2425

2526
/**
2627
* @author Nicolas Grekas <p@tchwork.com>
@@ -38,15 +39,18 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
3839
private $requestStack;
3940
private $dumper;
4041
private $dumperIsInjected;
42+
private $serverDumper;
43+
private $sourceContextProvider;
4144

42-
public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, string $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null)
45+
public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, string $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null, ServerDumper $serverDumper = null)
4346
{
4447
$this->stopwatch = $stopwatch;
4548
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
4649
$this->charset = $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8';
4750
$this->requestStack = $requestStack;
4851
$this->dumper = $dumper;
4952
$this->dumperIsInjected = null !== $dumper;
53+
$this->serverDumper = $serverDumper;
5054

5155
// All clones share these properties by reference:
5256
$this->rootRefs = array(
@@ -55,6 +59,8 @@ public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null,
5559
&$this->isCollected,
5660
&$this->clonesCount,
5761
);
62+
63+
$this->sourceContextProvider = new SourceContextProvider($this->charset);
5864
}
5965

6066
public function __clone()
@@ -71,61 +77,12 @@ public function dump(Data $data)
7177
$this->isCollected = false;
7278
}
7379

74-
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 7);
75-
76-
$file = $trace[0]['file'];
77-
$line = $trace[0]['line'];
78-
$name = false;
79-
$fileExcerpt = false;
80-
81-
for ($i = 1; $i < 7; ++$i) {
82-
if (isset($trace[$i]['class'], $trace[$i]['function'])
83-
&& 'dump' === $trace[$i]['function']
84-
&& 'Symfony\Component\VarDumper\VarDumper' === $trace[$i]['class']
85-
) {
86-
$file = $trace[$i]['file'];
87-
$line = $trace[$i]['line'];
88-
89-
while (++$i < 7) {
90-
if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) {
91-
$file = $trace[$i]['file'];
92-
$line = $trace[$i]['line'];
93-
94-
break;
95-
} elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) {
96-
$template = $trace[$i]['object'];
97-
$name = $template->getTemplateName();
98-
$src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false);
99-
$info = $template->getDebugInfo();
100-
if (isset($info[$trace[$i - 1]['line']])) {
101-
$line = $info[$trace[$i - 1]['line']];
102-
$file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null;
103-
104-
if ($src) {
105-
$src = explode("\n", $src);
106-
$fileExcerpt = array();
107-
108-
for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) {
109-
$fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
110-
}
111-
112-
$fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>';
113-
}
114-
}
115-
break;
116-
}
117-
}
118-
break;
119-
}
120-
}
80+
list('name' => $name, 'file' => $file, 'line' => $line, 'file_excerpt' => $fileExcerpt) = $this->sourceContextProvider->getContext();
12181

122-
if (false === $name) {
123-
$name = str_replace('\\', '/', $file);
124-
$name = substr($name, strrpos($name, '/') + 1);
125-
}
126-
127-
if ($this->dumper) {
128-
$this->doDump($data, $name, $file, $line);
82+
if ($this->serverDumper && $this->serverDumper->isServerListening()) {
83+
$this->doDump($this->serverDumper, $data, $name, $file, $line);
84+
} elseif ($this->dumper) {
85+
$this->doDump($this->dumper, $data, $name, $file, $line);
12986
}
13087

13188
$this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt');
@@ -138,6 +95,11 @@ public function dump(Data $data)
13895

13996
public function collect(Request $request, Response $response, \Exception $exception = null)
14097
{
98+
// If the server dumper is set and server is up, dump were already sent to it.
99+
if ($this->serverDumper && $this->serverDumper->isServerListening()) {
100+
return;
101+
}
102+
141103
// Sub-requests and programmatic calls stay in the collected profile.
142104
if ($this->dumper || ($this->requestStack && $this->requestStack->getMasterRequest() !== $request) || $request->isXmlHttpRequest() || $request->headers->has('Origin')) {
143105
return;
@@ -152,14 +114,14 @@ public function collect(Request $request, Response $response, \Exception $except
152114
|| false === strripos($response->getContent(), '</body>')
153115
) {
154116
if ($response->headers->has('Content-Type') && false !== strpos($response->headers->get('Content-Type'), 'html')) {
155-
$this->dumper = new HtmlDumper('php://output', $this->charset);
156-
$this->dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat));
117+
$dumper = new HtmlDumper('php://output', $this->charset);
118+
$dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat));
157119
} else {
158-
$this->dumper = new CliDumper('php://output', $this->charset);
120+
$dumper = new CliDumper('php://output', $this->charset);
159121
}
160122

161123
foreach ($this->data as $dump) {
162-
$this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']);
124+
$this->doDump($dumper, $dump['data'], $dump['name'], $dump['file'], $dump['line']);
163125
}
164126
}
165127
}
@@ -243,33 +205,36 @@ public function __destruct()
243205
$this->clonesCount = 0;
244206
$this->isCollected = true;
245207

246-
$h = headers_list();
247-
$i = count($h);
248-
array_unshift($h, 'Content-Type: '.ini_get('default_mimetype'));
249-
while (0 !== stripos($h[$i], 'Content-Type:')) {
250-
--$i;
251-
}
208+
// If the server dumper is set and server is up, dump were already sent to it.
209+
if (!$this->serverDumper || !$this->serverDumper->isServerListening()) {
210+
$h = headers_list();
211+
$i = count($h);
212+
array_unshift($h, 'Content-Type: '.ini_get('default_mimetype'));
213+
while (0 !== stripos($h[$i], 'Content-Type:')) {
214+
--$i;
215+
}
252216

253-
if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) {
254-
$this->dumper = new HtmlDumper('php://output', $this->charset);
255-
$this->dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat));
256-
} else {
257-
$this->dumper = new CliDumper('php://output', $this->charset);
258-
}
217+
if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) {
218+
$dumper = new HtmlDumper('php://output', $this->charset);
219+
$dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat));
220+
} else {
221+
$dumper = new CliDumper('php://output', $this->charset);
222+
}
259223

260-
foreach ($this->data as $i => $dump) {
261-
$this->data[$i] = null;
262-
$this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']);
224+
foreach ($this->data as $i => $dump) {
225+
$this->data[$i] = null;
226+
$this->doDump($dumper, $dump['data'], $dump['name'], $dump['file'], $dump['line']);
227+
}
263228
}
264229

265230
$this->data = array();
266231
$this->dataCount = 0;
267232
}
268233
}
269234

270-
private function doDump($data, $name, $file, $line)
235+
private function doDump(DataDumperInterface $dumper, $data, $name, $file, $line)
271236
{
272-
if ($this->dumper instanceof CliDumper) {
237+
if ($dumper instanceof CliDumper) {
273238
$contextDumper = function ($name, $file, $line, $fmt) {
274239
D7AE if ($this instanceof HtmlDumper) {
275240
if ($file) {
@@ -290,26 +255,16 @@ private function doDump($data, $name, $file, $line)
290255
}
291256
$this->dumpLine(0);
292257
};
293-
$contextDumper = $contextDumper->bindTo($this->dumper, $this->dumper);
258+
$contextDumper = $contextDumper->bindTo($dumper, $dumper);
294259
$contextDumper($name, $file, $line, $this->fileLinkFormat);
260+
} elseif ($dumper instanceof ServerDumper) {
261+
if ($this->dumper) {
262+
$dumper->setWrappedDumper($this->dumper);
263+
}
295264
} else {
296265
$cloner = new VarCloner();
297-
$this->dumper->dump($cloner->cloneVar($name.' on line '.$line.':'));
266+
$dumper->dump($cloner->cloneVar($name.' on line '.$line.':'));
298267
}
299-
$this->dumper->dump($data);
300-
}
301-
302-
private function htmlEncode($s)
303-
{
304-
$html = '';
305-
306-
$dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset);
307-
$dumper->setDumpHeader('');
308-
$dumper->setDumpBoundaries('', '');
309-
310-
$cloner = new VarCloner();
311-
$dumper->dump($cloner->cloneVar($s));
312-
313-
return substr(strip_tags($html), 1, -1);
268+
$dumper->dump($data);
314269
}
315270
}

0 commit comments

Comments
 (0)
0