feature #19079 [Debug] Do not quote numbers in stack trace (c960657)

This PR was merged into the 3.2-dev branch.

Discussion
----------

[Debug] Do not quote numbers in stack trace

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | yes
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

In the debug output from the exception handler, integers and floats are quoted. This adds unnecessary noise to the error page and is just wrong.

Fixing this requires introduces two new type descriptions in the output from getTrace(), so the change is not fully backwards compatible.

Commits
-------

fb10b33 [Debug] Do not quote numbers in stack trace
This commit is contained in:
Fabien Potencier 2016-06-19 12:27:18 +02:00
commit 414a4b4233
6 changed files with 77 additions and 48 deletions

View File

@ -1,6 +1,12 @@
UPGRADE FROM 3.x to 4.0 UPGRADE FROM 3.x to 4.0
======================= =======================
Debug
-----
* `FlattenException::getTrace()` now returns additional type descriptions
`integer` and `float`.
DependencyInjection DependencyInjection
------------------- -------------------

View File

@ -92,8 +92,6 @@ class CodeExtension extends \Twig_Extension
$formattedValue = sprintf('<em>object</em>(<abbr title="%s">%s</abbr>)', $item[1], $short); $formattedValue = sprintf('<em>object</em>(<abbr title="%s">%s</abbr>)', $item[1], $short);
} elseif ('array' === $item[0]) { } elseif ('array' === $item[0]) {
$formattedValue = sprintf('<em>array</em>(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); $formattedValue = sprintf('<em>array</em>(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
} elseif ('string' === $item[0]) {
$formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->charset));
} elseif ('null' === $item[0]) { } elseif ('null' === $item[0]) {
$formattedValue = '<em>null</em>'; $formattedValue = '<em>null</em>';
} elseif ('boolean' === $item[0]) { } elseif ('boolean' === $item[0]) {
@ -101,7 +99,7 @@ class CodeExtension extends \Twig_Extension
} elseif ('resource' === $item[0]) { } elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>'; $formattedValue = '<em>resource</em>';
} else { } else {
$formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->charset), true)); $formattedValue = str_replace("\n", '', htmlspecialchars(var_export($item[1], true), ENT_COMPAT | ENT_SUBSTITUTE, $this->charset));
} }
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
@ -174,7 +172,7 @@ class CodeExtension extends \Twig_Extension
$text = "$text at line $line"; $text = "$text at line $line";
if (false !== $link = $this->getFileLink($file, $line)) { if (false !== $link = $this->getFileLink($file, $line)) {
$flags = ENT_QUOTES | ENT_SUBSTITUTE; $flags = ENT_COMPAT | ENT_SUBSTITUTE;
return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, $flags, $this->charset), $text); return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, $flags, $this->charset), $text);
} }

View File

@ -1,6 +1,13 @@
CHANGELOG CHANGELOG
========= =========
3.2.0
-----
* `FlattenException::getTrace()` now returns additional type descriptions
`integer` and `float`.
3.0.0 3.0.0
----- -----

View File

@ -234,6 +234,10 @@ class FlattenException
$result[$key] = array('null', null); $result[$key] = array('null', null);
} elseif (is_bool($value)) { } elseif (is_bool($value)) {
$result[$key] = array('boolean', $value); $result[$key] = array('boolean', $value);
} elseif (is_integer($value)) {
$result[$key] = array('integer', $value);
} elseif (is_float($value)) {
$result[$key] = array('float', $value);
} elseif (is_resource($value)) { } elseif (is_resource($value)) {
$result[$key] = array('resource', get_resource_type($value)); $result[$key] = array('resource', get_resource_type($value));
} elseif ($value instanceof \__PHP_Incomplete_Class) { } elseif ($value instanceof \__PHP_Incomplete_Class) {

View File

@ -376,8 +376,6 @@ EOF;
$formattedValue = sprintf('<em>object</em>(%s)', $this->formatClass($item[1])); $formattedValue = sprintf('<em>object</em>(%s)', $this->formatClass($item[1]));
} elseif ('array' === $item[0]) { } elseif ('array' === $item[0]) {
$formattedValue = sprintf('<em>array</em>(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); $formattedValue = sprintf('<em>array</em>(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
} elseif ('string' === $item[0]) {
$formattedValue = sprintf("'%s'", $this->escapeHtml($item[1]));
} elseif ('null' === $item[0]) { } elseif ('null' === $item[0]) {
$formattedValue = '<em>null</em>'; $formattedValue = '<em>null</em>';
} elseif ('boolean' === $item[0]) { } elseif ('boolean' === $item[0]) {
@ -385,7 +383,7 @@ EOF;
} elseif ('resource' === $item[0]) { } elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>'; $formattedValue = '<em>resource</em>';
} else { } else {
$formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true)); $formattedValue = str_replace("\n", '', $this->escapeHtml(var_export($item[1], true)));
} }
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);

View File

@ -190,6 +190,60 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
); );
} }
public function testArguments()
{
$dh = opendir(__DIR__);
$incomplete = unserialize('O:14:"BogusTestClass":0:{}');
$exception = $this->createException(array(
(object) array('foo' => 1),
new NotFoundHttpException(),
$incomplete,
$dh,
function() {},
array(1, 2),
array('foo' => 123),
null,
true,
false,
0,
0.0,
'0',
'',
INF,
NAN,
));
$flattened = FlattenException::create($exception);
$trace = $flattened->getTrace();
$args = $trace[1]['args'];
$array = $args[0][1];
closedir($dh);
$i = 0;
$this->assertSame($array[$i++], array('object', 'stdClass'));
$this->assertSame($array[$i++], array('object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'));
$this->assertSame($array[$i++], array('incomplete-object', 'BogusTestClass'));
$this->assertSame($array[$i++], array('resource', 'stream'));
$this->assertSame($array[$i++], array('object', 'Closure'));
$this->assertSame($array[$i++], array('array', array(array('integer', 1), array('integer', 2))));
$this->assertSame($array[$i++], array('array', array('foo' => array('integer', 123))));
$this->assertSame($array[$i++], array('null', null));
$this->assertSame($array[$i++], array('boolean', true));
$this->assertSame($array[$i++], array('boolean', false));
$this->assertSame($array[$i++], array('integer', 0));
$this->assertSame($array[$i++], array('float', 0.0));
$this->assertSame($array[$i++], array('string', '0'));
$this->assertSame($array[$i++], array('string', ''));
$this->assertSame($array[$i++], array('float', INF));
// assertEquals() does not like NAN values.
$this->assertEquals($array[$i][0], 'float');
$this->assertTrue(is_nan($array[$i++][1]));
}
public function testRecursionInArguments() public function testRecursionInArguments()
{ {
$a = array('foo', array(2, &$a)); $a = array('foo', array(2, &$a));
@ -216,6 +270,9 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
$flattened = FlattenException::create($exception); $flattened = FlattenException::create($exception);
$trace = $flattened->getTrace(); $trace = $flattened->getTrace();
$this->assertSame($trace[1]['args'][0], array('array', array('array', '*SKIPPED over 10000 entries*')));
$serializeTrace = serialize($trace); $serializeTrace = serialize($trace);
$this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace); $this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace);
@ -226,45 +283,4 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
{ {
return new \Exception(); return new \Exception();
} }
public function testSetTraceIncompleteClass()
{
$flattened = FlattenException::create(new \Exception('test', 123));
$flattened->setTrace(
array(
array(
'file' => __FILE__,
'line' => 123,
'function' => 'test',
'args' => array(
unserialize('O:14:"BogusTestClass":0:{}'),
),
),
),
'foo.php', 123
);
$this->assertEquals(array(
array(
'message' => 'test',
'class' => 'Exception',
'trace' => array(
array(
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '',
'file' => 'foo.php', 'line' => 123,
'args' => array(),
),
array(
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => 'test',
'file' => __FILE__, 'line' => 123,
'args' => array(
array(
'incomplete-object', 'BogusTestClass',
),
),
),
),
),
), $flattened->toArray());
}
} }