bug #11759 [Debug] fix and enhance exception messages (nicolas-grekas)

This PR was merged into the 2.6-dev branch.

Discussion
----------

[Debug] fix and enhance exception messages

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

Commits
-------

8df1ce8 [Debug] fix and enhance exception messages
This commit is contained in:
Fabien Potencier 2014-08-26 19:33:51 +02:00
commit 4b507bd7a4
10 changed files with 84 additions and 26 deletions

View File

@ -210,9 +210,11 @@ class ExceptionHandler
$class = $this->formatClass($e['class']);
$message = nl2br(self::utf8Htmlize($e['message']));
$content .= sprintf(<<<EOF
<div class="block_exception clear_fix">
<h2><span>%d/%d</span> %s%s:<br />%s</h2>
</div>
<h2 class="block_exception clear_fix">
<span class="exception_counter">%d/%d</span>
<span class="exception_title">%s%s:</span>
<span class="exception_message">%s</span>
</h2>
<div class="block">
<ol class="traces list_exception">
@ -269,12 +271,14 @@ EOF;
.sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; }
.sf-reset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px }
.sf-reset strong { font-weight:bold; }
.sf-reset a { color:#6c6159; }
.sf-reset a { color:#6c6159; cursor: default; }
.sf-reset a img { border:none; }
.sf-reset a:hover { text-decoration:underline; }
.sf-reset em { font-style:italic; }
.sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif }
.sf-reset h2 span { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; }
.sf-reset .exception_counter { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; float: left; display: block; }
.sf-reset .exception_title { margin-left: 3em; margin-bottom: 0.7em; display: block; }
.sf-reset .exception_message { margin-left: 3em; display: block; }
.sf-reset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; }
.sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px;
-webkit-border-bottom-right-radius: 16px;
@ -352,10 +356,10 @@ EOF;
if ($linkFormat = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format')) {
$link = str_replace(array('%f', '%l'), array($path, $line), $linkFormat);
return sprintf(' <a href="%s" title="Go to source">in %s line %d</a>', $link, $file, $line);
return sprintf(' in <a href="%s" title="Go to source">%s line %d</a>', $link, $file, $line);
}
return sprintf(' <a title="in %s line %3$d" ondblclick="var f=this.innerHTML;this.innerHTML=this.title;this.title=f;">in %s line %d</a>', $path, $file, $line);
return sprintf(' in <a title="%s line %3$d" ondblclick="var f=this.innerHTML;this.innerHTML=this.title;this.title=f;">%s line %d</a>', $path, $file, $line);
}
/**

View File

@ -67,7 +67,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
$tail = ' for "'.$tail;
}
}
$message .= ' Did you forget a "use" statement'.$tail;
$message .= "\nDid you forget a \"use\" statement".$tail;
return new ClassNotFoundException($message, $exception);
}

View File

@ -76,7 +76,7 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
} else {
$candidates = '"'.$last;
}
$message .= ' Did you mean to call '.$candidates;
$message .= "\nDid you mean to call ".$candidates;
}
return new UndefinedFunctionException($message, $exception);

View File

@ -52,7 +52,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
} else {
$candidates = '"'.$last;
}
$message .= ' Did you mean to call '.$candidates;
$message .= "\nDid you mean to call ".$candidates;
}
return new UndefinedMethodException($message, $exception);

View File

@ -25,13 +25,13 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
$response = $handler->createResponse(new \RuntimeException('Foo'));
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent());
$this->assertNotContains('<div class="block_exception clear_fix">', $response->getContent());
$this->assertNotContains('<h2 class="block_exception clear_fix">', $response->getContent());
$handler = new ExceptionHandler(true);
$response = $handler->createResponse(new \RuntimeException('Foo'));
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent());
$this->assertContains('<div class="block_exception clear_fix">', $response->getContent());
$this->assertContains('<h2 class="block_exception clear_fix">', $response->getContent());
}
public function testStatusCode()

View File

@ -41,7 +41,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'WhizBangFactory\' not found',
),
'Attempted to load class "WhizBangFactory" from the global namespace. Did you forget a "use" statement?',
"Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
),
array(
array(
@ -50,7 +50,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found',
),
'Attempted to load class "WhizBangFactory" from namespace "Foo\\Bar". Did you forget a "use" statement for another namespace?',
"Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
),
array(
array(
@ -59,7 +59,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'UndefinedFunctionException\' not found',
),
'Attempted to load class "UndefinedFunctionException" from the global namespace. Did you forget a "use" statement for "Symfony\Component\Debug\Exception\UndefinedFunctionException"?',
"Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
),
array(
array(
@ -68,7 +68,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'PEARClass\' not found',
),
'Attempted to load class "PEARClass" from the global namespace. Did you forget a "use" statement for "Symfony_Component_Debug_Tests_Fixtures_PEARClass"?',
"Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?",
),
array(
array(
@ -77,7 +77,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
),
'Attempted to load class "UndefinedFunctionException" from namespace "Foo\Bar". Did you forget a "use" statement for "Symfony\Component\Debug\Exception\UndefinedFunctionException"?',
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
),
);
}

View File

@ -42,7 +42,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined function test_namespaced_function()',
),
'Attempted to call function "test_namespaced_function" from the global namespace. Did you mean to call "\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function"?',
"Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
),
array(
array(
@ -51,7 +51,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()',
),
'Attempted to call function "test_namespaced_function" from namespace "Foo\\Bar\\Baz". Did you mean to call "\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function"?',
"Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
),
array(
array(

View File

@ -50,7 +50,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::walid()',
),
'Attempted to call method "walid" on class "SplObjectStorage". Did you mean to call "valid"?',
"Attempted to call method \"walid\" on class \"SplObjectStorage\".\nDid you mean to call \"valid\"?",
),
array(
array(
@ -59,7 +59,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::offsetFet()',
),
'Attempted to call method "offsetFet" on class "SplObjectStorage". Did you mean to call e.g. "offsetGet", "offsetSet" or "offsetUnset"?',
"Attempted to call method \"offsetFet\" on class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?",
),
);
}

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\HttpKernel\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\AbstractExceptionHandler;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
@ -37,9 +37,7 @@ class DebugHandlersListener implements EventSubscriberInterface
*/
public function __construct($exceptionHandler, LoggerInterface $logger = null, $levels = null, $debug = true)
{
if (is_callable($exceptionHandler)) {
$this->exceptionHandler = $exceptionHandler;
}
$this->logger = $logger;
$this->levels = $levels;
$this->debug = $debug;
@ -76,7 +74,7 @@ class DebugHandlersListener implements EventSubscriberInterface
$handler->setExceptionHandler($h);
$handler = is_array($h) ? $h[0] : null;
}
if ($handler instanceof AbstractExceptionHandler) {
if ($handler instanceof ExceptionHandler) {
$handler->setHandler($this->exceptionHandler);
}
$this->exceptionHandler = null;

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Tests\EventListener;
use Psr\Log\LogLevel;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\HttpKernel\EventListener\DebugHandlersListener;
/**
* DebugHandlersListenerTest
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class DebugHandlersListenerTest extends \PHPUnit_Framework_TestCase
{
public function testConfigure()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$userHandler = function() {};
$listener = new DebugHandlersListener($userHandler, $logger);
$xHandler = new ExceptionHandler();
$eHandler = new ErrorHandler();
$eHandler->setExceptionHandler(array($xHandler, 'handle'));
$exception = null;
set_error_handler(array($eHandler, 'handleError'));
set_exception_handler(array($eHandler, 'handleException'));
try {
$listener->configure();
} catch (\Exception $exception) {
}
restore_exception_handler();
restore_error_handler();
if (null !== $exception) {
throw $exception;
}
$this->assertSame($userHandler, $xHandler->setHandler('var_dump'));
$loggers = $eHandler->setLoggers(array());
$this->assertArrayHasKey(E_DEPRECATED, $loggers);
$this->assertSame(array($logger, LogLevel::INFO), $loggers[E_DEPRECATED]);
}
}