Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 74f7357

Browse files
committed
[Debug] Added an exception processor for dumping arguments
1 parent 8f4ab6a commit 74f7357

File tree

16 files changed

+418
-91
lines changed

16 files changed

+418
-91
lines changed

src/Symfony/Bridge/Twig/Extension/CodeExtension.php

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
*/
1111

1212
namespace Symfony\Bridge\Twig\Extension;
13+
use Symfony\Component\Debug\Exception\FlattenException;
14+
use Symfony\Component\Debug\Utils\HtmlUtils;
1315

1416
/**
1517
* Twig extension relate to PHP code and used by the profiler and the default exception templates.
@@ -21,6 +23,7 @@ class CodeExtension extends \Twig_Extension
2123
private $fileLinkFormat;
2224
private $rootDir;
2325
private $charset;
26+
private $htmlUtils;
2427

2528
/**
2629
* Constructor.
@@ -29,11 +32,12 @@ class CodeExtension extends \Twig_Extension
2932
* @param string $rootDir The project root directory
3033
* @param string $charset The charset
3134
*/
32-
public function __construct($fileLinkFormat, $rootDir, $charset)
35+
public function __construct($fileLinkFormat, $rootDir, $charset, HtmlUtils $htmlUtils = null)
3336
{
3437
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
3538
$this->rootDir = str_replace('/', DIRECTORY_SEPARATOR, dirname($rootDir)).DIRECTORY_SEPARATOR;
3639
$this->charset = $charset;
40+
$this->htmlUtils = $htmlUtils;
3741
}
3842

3943
/**
@@ -78,48 +82,27 @@ public function abbrMethod($method)
7882
/**
7983
* Formats an array as a string.
8084
*
81-
* @param array $args The argument array
85+
* @param array $args The argument array
86+
* @param FlattenException|null $exception The flatten exception
8287
*
8388
* @return string
8489
*/
85-
public function formatArgs($args)
90+
public function formatArgs($args, $exception = null)
8691
{
87-
$result = array();
88-
foreach ($args as $key => $item) {
89-
if ('object' === $item[0]) {
90-
$parts = explode('\\', $item[1]);
91-
$short = array_pop($parts);
92-
$formattedValue = sprintf('<em>object</em>(<abbr title="%s">%s</abbr>)', $item[1], $short);
93-
} elseif ('array' === $item[0]) {
94-
$formattedValue = sprintf('<em>array</em>(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
95-
} elseif ('string' === $item[0]) {
96-
$formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->charset));
97-
} elseif ('null' === $item[0]) {
98-
$formattedValue = '<em>null</em>';
99-
} elseif ('boolean' === $item[0]) {
100-
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
101-
} elseif ('resource' === $item[0]) {
102-
$formattedValue = '<em>resource</em>';
103-
} else {
104-
$formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->charset), true));
105-
}
106-
107-
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
108-
}
109-
110-
return implode(', ', $result);
92+
return $this->htmlUtils->formatArgs($args, $exception);
11193
}
11294

11395
/**
11496
* Formats an array as a string.
11597
*
116-
* @param array $args The argument array
98+
* @param array $args The argument array
99+
* @param FlattenException|null $exception The flatten exception
117100
*
118101
* @return string
119102
*/
120-
public function formatArgsAsText($args)
103+
public function formatArgsAsText($args, $exception = null)
121104
{
122-
return strip_tags($this->formatArgs($args));
105+
return strip_tags($this->formatArgs($args, $exception));
123106
}
124107

125108
/**

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@
3939
<tag name="exception.processor" />
4040
</service>
4141

42+
<service id="debug.exception_processor.arguments" class="Symfony\Component\Debug\ArgumentsFlattenExceptionProcessor" public="false">
43+
<tag name="exception.processor" />
44+
<argument type="service" id="var_dumper.cloner" />
45+
</service>
46+
47+
<service id="debug.html_utils" class="Symfony\Component\Debug\Utils\HtmlUtils">
48+
</service>
4249
</services>
4350

4451
</container>

src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
<argument /> <!-- %templating.helper.code.file_link_format% -->
102102
<argument>%kernel.root_dir%</argument>
103103
<argument>%kernel.charset%</argument>
104+
<argument type="service" id="debug.html_utils" on-invalid="ignore" />
104105
</service>
105106

106107
<service id="twig.extension.routing" class="%twig.extension.routing.class%" public="false">

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
</div>
3737
</div>
3838

39-
{% for position, e in exception.toarray %}
39+
{% for position, e in [exception]|merge(exception.allprevious) %}
4040
{% include 'TwigBundle:Exception:traces.html.twig' with { 'exception': e, 'position': position, 'count': previous_count } only %}
4141
{% endfor %}
4242

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.xml.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="{{ _charset }}" ?>
22

33
<error code="{{ status_code }}" message="{{ status_text }}">
4-
{% for e in exception.toarray %}
4+
{% for e in [exception]|merge(exception.allprevious) %}
55
<exception class="{{ e.class }}" message="{{ e.message }}">
66
{% include 'TwigBundle:Exception:traces.xml.twig' with { 'exception': e } only %}
77
</exception>

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<abbr title="{{ trace.class }}">{{ trace.short_class }}</abbr>
55
{{ trace.type ~ trace.function }}
66
</strong>
7-
({{ trace.args|format_args }})
7+
({{ trace.args|format_args(exception|default) }})
88
{% endif %}
99

1010
{% if trace.file is defined and trace.file and trace.line is defined and trace.line %}

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.txt.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{% if trace.function %}
2-
at {{ trace.class ~ trace.type ~ trace.function }}({{ trace.args|format_args_as_text }})
2+
at {{ trace.class ~ trace.type ~ trace.function }}({{ trace.args|format_args_as_text(exception|default) }})
33
{% else %}
44
at n/a
55
{% endif %}

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<ol class="traces list-exception" id="traces-{{ position }}" style="display: {{ 0 == count ? 'block' : 'none' }}">
1919
{% for i, trace in exception.trace %}
2020
<li>
21-
{% include 'TwigBundle:Exception:trace.html.twig' with { 'prefix': position, 'i': i, 'trace': trace } only %}
21+
{% include 'TwigBundle:Exception:trace.html.twig' with { exception: exception, 'prefix': position, 'i': i, 'trace': trace } only %}
2222
</li>
2323
{% endfor %}
2424
</ol>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{% if exception.trace|length %}
22
{% for trace in exception.trace %}
3-
{% include 'TwigBundle:Exception:trace.txt.twig' with { 'trace': trace } only %}
3+
{% include 'TwigBundle:Exception:trace.txt.twig' with { exception: exception, 'trace': trace } only %}
44

55
{% endfor %}
66
{% endif %}

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.xml.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<traces>
22
{% for trace in exception.trace %}
33
<trace>
4-
{% include 'TwigBundle:Exception:trace.txt.twig' with { 'trace': trace } only %}
4+
{% include 'TwigBundle:Exception:trace.txt.twig' with { exception: exception, 'trace': trace } only %}
55

66
</trace>
77
{% endfor %}

src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</h2>
1111

1212
<div id="traces-text" class="trace" style="display: none;">
13-
<pre>{% for i, e in exception.toarray %}
13+
<pre>{% for i, e in [exception]|merge(exception.allprevious) %}
1414
[{{ i + 1 }}] {{ e.class }}: {{ e.message }}
1515
{% include 'TwigBundle:Exception:traces.txt.twig' with { 'exception': e } only %}
1616
{% endfor %}</pre>
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Debug;
13+
14+
use Symfony\Component\Debug\Exception\FlattenException;
15+
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
16+
17+
/**
18+
* @author Martin Hasoň <[email protected]>
19+
*/
20+
class ArgumentsFlattenExceptionProcessor implements FlattenExceptionProcessorInterface
21+
{
22+
private $cloner;
23+
private $shareArguments;
24+
25+
public function __construct(ClonerInterface $cloner = null, $shareArguments = true)
26+
{
27+
$this->cloner = $cloner;
28+
$this->shareArguments = $shareArguments;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function process(\Exception $exception, FlattenException $flattenException, $master)
35+
{
36+
if (!$master) {
37+
return;
38+
}
39+
40+
$variables = array();
41+
$values = array();
42+
43+
$e = $exception;
44+
$f = $flattenException;
45+
46+
do {
47+
$trace = $f->getTrace();
48+
foreach ($e->getTrace() as $key => $entry) {
49+
if (!isset($entry['args']) || !isset($trace[$key])) {
50+
continue;
51+
}
52+
53+
$parameters = $this->getParameters($entry);
54+
55+
$arguments = array();
56+
foreach ($entry['args'] as $position => $argument) {
57+
$link = array_search($argument, $variables, true);
58+
59+
if (false === $link) {
60+
$link = hash('md5', uniqid(mt_rand(), true), false);
61+
$variables[$link] = $argument;
62+
$values[$link] = $this->shareArguments ? array('link', $link) : $this->flatten($argument);
63+
}
64+
65+
if (isset($parameters[$position])) {
66+
$arguments[$parameters[$position]->getName()] = $values[$link];
67+
} else {
68+
$arguments[] = $values[$link];
69+
}
70+
}
71+
72+
$trace[$key]['args'] = $arguments;
73+
}
74+
$f->replaceTrace($trace);
75+
} while (($e = $e->getPrevious()) && ($f = $f->getPrevious()));
76+
77+
if (!$this->shareArguments) {
78+
return;
79+
}
80+
81+
$flattenVariables = $this->flatten($variables);
82+
foreach (array_merge(array($flattenException), $flattenException->getAllPrevious()) as $f) {
83+
$f->setExtra('trace_arguments', $flattenVariables);
84+
}
85+
}
86+
87+
private function getParameters($entry)
88+
{
89+
if (!isset($entry['function'])) {
90+
return array();
91+
}
92+
93+
try {
94+
if (isset($entry['class'])) {
95+
$ref = new \ReflectionMethod($entry['class'], $entry['function']);
96+
} else {
97+
$ref = new \ReflectionFunction($entry['function']);
98+
}
99+
} catch (\ReflectionException $e) {
100+
return array();
101+
}
102+
103+
return $ref->getParameters();
104+
}
105+
106+
private function flatten($variable)
107+
{
108+
if (null === $this->cloner) {
109+
return $this->flattenValue($variable);
110+
} else {
111+
return $this->cloner->cloneVar($variable);
112+
}
113+
}
114+
115+
private function flattenValue($value, $level = 0, &$count = 0)
116+
{
117+
if ($count++ > 1e4) {
118+
return array('array', '*SKIPPED over 10000 entries*');
119+
}
120+
121+
if (is_object($value)) {
122+
return array('object', get_class($value));
123+
}
124+
125+
if (is_array($value)) {
126+
if ($level > 10) {
127+
return array('array', '*DEEP NESTED ARRAY*');
128+
}
129+
130+
$array = array();
131+
foreach ($value as $k => $v) {
132+
$array[$k] = $this->flattenValue($v, $level + 1, $count);
133+
}
134+
135+
return array('array', $array);
136+
}
137+
138+
if (null === $value) {
139+
return array('null', null);
140+
}
141+
142+
if (is_bool($value)) {
143+
return array('boolean', $value);
144+
}
145+
146+
if (is_float($value) || is_int($value)) {
147+
return array('number', $value);
148+
}
149+
150+
if (is_resource($value)) {
151+
return array('resource', get_resource_type($value));
152+
}
153+
154+
if ($value instanceof \__PHP_Incomplete_Class) {
155+
// Special case of object, is_object will return false
156+
return array('incomplete-object', $this->getClassNameFromIncomplete($value));
157+
}
158+
159+
return array('string', (string) $value);
160+
}
161+
162+
private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
163+
{
164+
$array = new \ArrayObject($value);
165+
166+
return $array['__PHP_Incomplete_Class_Name'];
167+
}
168+
}

0 commit comments

Comments
 (0)