merged branch fabpot/debug-component (PR #7441)
This PR was merged into the master branch. Discussion ---------- [Debug] added the component (closes #6828, closes #6834, closes #7330) | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #6828, #6834, #7330 | License | MIT | Doc PR | symfony/symfony-docs#2479 You can use the individual tools, or register them all: ```php use Symfony\Component\Debug\Debug; Debug::enable(); ``` Changes in Symfony SE: symfony/symfony-standard#523 Commits -------f693128
fixed typos1ab1146
[Debug] fixed minor bugsdaa3a3c
[Debug] changed composer to accept more versionse455269
[Debug] ensured that the Debug tools can only be registered once946bfb2
[Debug] made the exception handler independant of HttpFoundation2b305c2
added a main Debug class to ease integration2ff0927
[Debug] added the component (closes #6828, closes #6834, closes #7330)
This commit is contained in:
commit
6d552c956e
@ -3,8 +3,8 @@ UPGRADE FROM 2.x to 3.0
|
|||||||
|
|
||||||
### ClassLoader
|
### ClassLoader
|
||||||
|
|
||||||
* The `UniversalClassLoader` class has been removed in favor of `ClassLoader`. The only difference is that some method
|
* The `UniversalClassLoader` class has been removed in favor of
|
||||||
names are different:
|
`ClassLoader`. The only difference is that some method names are different:
|
||||||
|
|
||||||
* `registerNamespaces()` -> `addPrefixes()`
|
* `registerNamespaces()` -> `addPrefixes()`
|
||||||
* `registerPrefixes()` -> `addPrefixes()`
|
* `registerPrefixes()` -> `addPrefixes()`
|
||||||
@ -34,6 +34,16 @@ UPGRADE FROM 2.x to 3.0
|
|||||||
* `Symfony\Bridge\Monolog\Logger`
|
* `Symfony\Bridge\Monolog\Logger`
|
||||||
* `Symfony\Component\HttpKernel\Log\NullLogger`
|
* `Symfony\Component\HttpKernel\Log\NullLogger`
|
||||||
|
|
||||||
|
* The `Symfony\Component\HttpKernel\Kernel::init()` method has been removed.
|
||||||
|
|
||||||
|
* The following classes have been renamed as they have been moved to the
|
||||||
|
Debug component:
|
||||||
|
|
||||||
|
* `Symfony\Component\HttpKernel\Debug\ErrorHandler` -> `Symfony\Component\Debug\ErrorHandler`
|
||||||
|
* `Symfony\Component\HttpKernel\Debug\ExceptionHandler` -> `Symfony\Component\Debug\ExceptionHandler`
|
||||||
|
* `Symfony\Component\HttpKernel\Exception\FatalErrorException` -> `Symfony\Component\Debug\Exception\FatalErrorException`
|
||||||
|
* `Symfony\Component\HttpKernel\Exception\FlattenException` -> `Symfony\Component\Debug\Exception\FlattenException`
|
||||||
|
|
||||||
### Routing
|
### Routing
|
||||||
|
|
||||||
* Some route settings have been renamed:
|
* Some route settings have been renamed:
|
||||||
|
3
src/Symfony/Component/Debug/.gitignore
vendored
Normal file
3
src/Symfony/Component/Debug/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
vendor/
|
||||||
|
composer.lock
|
||||||
|
phpunit.xml
|
7
src/Symfony/Component/Debug/CHANGELOG.md
Normal file
7
src/Symfony/Component/Debug/CHANGELOG.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
2.3.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added the component
|
56
src/Symfony/Component/Debug/Debug.php
Normal file
56
src/Symfony/Component/Debug/Debug.php
Normal 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\Debug;
|
||||||
|
|
||||||
|
use Symfony\Component\ClassLoader\DebugClassLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers all the debug tools.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
class Debug
|
||||||
|
{
|
||||||
|
private static $enabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the debug tools.
|
||||||
|
*
|
||||||
|
* This method registers an error handler and an exception handler.
|
||||||
|
*
|
||||||
|
* If the Symfony ClassLoader component is available, a special
|
||||||
|
* class loader is also registered.
|
||||||
|
*
|
||||||
|
* @param integer $errorReportingLevel The level of error reporting you want
|
||||||
|
*/
|
||||||
|
public static function enable($errorReportingLevel = null)
|
||||||
|
{
|
||||||
|
if (static::$enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static::$enabled = true;
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
|
||||||
|
ErrorHandler::register($errorReportingLevel);
|
||||||
|
if ('cli' !== php_sapi_name()) {
|
||||||
|
ExceptionHandler::register();
|
||||||
|
} elseif (!ini_get('log_errors') || ini_get('error_log')) {
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (class_exists('Symfony\Component\ClassLoader\DebugClassLoader')) {
|
||||||
|
DebugClassLoader::enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
src/Symfony/Component/Debug/ErrorHandler.php
Normal file
128
src/Symfony/Component/Debug/ErrorHandler.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?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\Debug;
|
||||||
|
|
||||||
|
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ErrorHandler.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
class ErrorHandler
|
||||||
|
{
|
||||||
|
const TYPE_DEPRECATION = -100;
|
||||||
|
|
||||||
|
private $levels = array(
|
||||||
|
E_WARNING => 'Warning',
|
||||||
|
E_NOTICE => 'Notice',
|
||||||
|
E_USER_ERROR => 'User Error',
|
||||||
|
E_USER_WARNING => 'User Warning',
|
||||||
|
E_USER_NOTICE => 'User Notice',
|
||||||
|
E_STRICT => 'Runtime Notice',
|
||||||
|
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
|
||||||
|
E_DEPRECATED => 'Deprecated',
|
||||||
|
E_USER_DEPRECATED => 'User Deprecated',
|
||||||
|
E_ERROR => 'Error',
|
||||||
|
E_CORE_ERROR => 'Core Error',
|
||||||
|
E_COMPILE_ERROR => 'Compile Error',
|
||||||
|
E_PARSE => 'Parse',
|
||||||
|
);
|
||||||
|
|
||||||
|
private $level;
|
||||||
|
|
||||||
|
private $reservedMemory;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
private static $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the error handler.
|
||||||
|
*
|
||||||
|
* @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
|
||||||
|
*
|
||||||
|
* @return The registered error handler
|
||||||
|
*/
|
||||||
|
public static function register($level = null)
|
||||||
|
{
|
||||||
|
$handler = new static();
|
||||||
|
$handler->setLevel($level);
|
||||||
|
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
set_error_handler(array($handler, 'handle'));
|
||||||
|
register_shutdown_function(array($handler, 'handleFatal'));
|
||||||
|
$handler->reservedMemory = str_repeat('x', 10240);
|
||||||
|
|
||||||
|
return $handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLevel($level)
|
||||||
|
{
|
||||||
|
$this->level = null === $level ? error_reporting() : $level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function setLogger(LoggerInterface $logger)
|
||||||
|
{
|
||||||
|
self::$logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \ErrorException When error_reporting returns error
|
||||||
|
*/
|
||||||
|
public function handle($level, $message, $file, $line, $context)
|
||||||
|
{
|
||||||
|
if (0 === $this->level) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) {
|
||||||
|
if (null !== self::$logger) {
|
||||||
|
$stack = version_compare(PHP_VERSION, '5.4', '<') ? array_slice(debug_backtrace(false), 0, 10) : debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
|
||||||
|
|
||||||
|
self::$logger->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_reporting() & $level && $this->level & $level) {
|
||||||
|
throw new \ErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleFatal()
|
||||||
|
{
|
||||||
|
if (null === $error = error_get_last()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->reservedMemory);
|
||||||
|
$type = $error['type'];
|
||||||
|
if (0 === $this->level || !in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current exception handler
|
||||||
|
$exceptionHandler = set_exception_handler(function() {});
|
||||||
|
restore_exception_handler();
|
||||||
|
|
||||||
|
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {
|
||||||
|
$level = isset($this->levels[$type]) ? $this->levels[$type] : $type;
|
||||||
|
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
|
||||||
|
$exception = new FatalErrorException($message, 0, $type, $error['file'], $error['line']);
|
||||||
|
$exceptionHandler[0]->handle($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?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\Debug\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fatal Error Exception.
|
||||||
|
*
|
||||||
|
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||||
|
*/
|
||||||
|
class FatalErrorException extends \ErrorException
|
||||||
|
{
|
||||||
|
}
|
277
src/Symfony/Component/Debug/Exception/FlattenException.php
Normal file
277
src/Symfony/Component/Debug/Exception/FlattenException.php
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
<?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\Debug\Exception;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlattenException wraps a PHP Exception to be able to serialize it.
|
||||||
|
*
|
||||||
|
* Basically, this class removes all objects from the trace.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
class FlattenException
|
||||||
|
{
|
||||||
|
private $message;
|
||||||
|
private $code;
|
||||||
|
private $previous;
|
||||||
|
private $trace;
|
||||||
|
private $class;
|
||||||
|
private $statusCode;
|
||||||
|
private $headers;
|
||||||
|
private $file;
|
||||||
|
private $line;
|
||||||
|
|
||||||
|
public static function create(\Exception $exception, $statusCode = null, array $headers = array())
|
||||||
|
{
|
||||||
|
$e = new static();
|
||||||
|
$e->setMessage($exception->getMessage());
|
||||||
|
$e->setCode($exception->getCode());
|
||||||
|
|
||||||
|
if ($exception instanceof HttpExceptionInterface) {
|
||||||
|
$statusCode = $exception->getStatusCode();
|
||||||
|
$headers = array_merge($headers, $exception->getHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $statusCode) {
|
||||||
|
$statusCode = 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
$e->setStatusCode($statusCode);
|
||||||
|
$e->setHeaders($headers);
|
||||||
|
$e->setTraceFromException($exception);
|
||||||
|
$e->setClass(get_class($exception));
|
||||||
|
$e->setFile($exception->getFile());
|
||||||
|
$e->setLine($exception->getLine());
|
||||||
|
if ($exception->getPrevious()) {
|
||||||
|
$e->setPrevious(static::create($exception->getPrevious()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
$exceptions = array();
|
||||||
|
foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) {
|
||||||
|
$exceptions[] = array(
|
||||||
|
'message' => $exception->getMessage(),
|
||||||
|
'class' => $exception->getClass(),
|
||||||
|
'trace' => $exception->getTrace(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusCode()
|
||||||
|
{
|
||||||
|
return $this->statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStatusCode($code)
|
||||||
|
{
|
||||||
|
$this->statusCode = $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaders()
|
||||||
|
{
|
||||||
|
return $this->headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHeaders(array $headers)
|
||||||
|
{
|
||||||
|
$this->headers = $headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClass()
|
||||||
|
{
|
||||||
|
return $this->class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setClass($class)
|
||||||
|
{
|
||||||
|
$this->class = $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFile()
|
||||||
|
{
|
||||||
|
return $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFile($file)
|
||||||
|
{
|
||||||
|
$this->file = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLine()
|
||||||
|
{
|
||||||
|
return $this->line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLine($line)
|
||||||
|
{
|
||||||
|
$this->line = $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessage()
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMessage($message)
|
||||||
|
{
|
||||||
|
$this->message = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode()
|
||||||
|
{
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCode($code)
|
||||||
|
{
|
||||||
|
$this->code = $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrevious()
|
||||||
|
{
|
||||||
|
return $this->previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPrevious(FlattenException $previous)
|
||||||
|
{
|
||||||
|
$this->previous = $previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllPrevious()
|
||||||
|
{
|
||||||
|
$exceptions = array();
|
||||||
|
$e = $this;
|
||||||
|
while ($e = $e->getPrevious()) {
|
||||||
|
$exceptions[] = $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTrace()
|
||||||
|
{
|
||||||
|
return $this->trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTraceFromException(\Exception $exception)
|
||||||
|
{
|
||||||
|
$trace = $exception->getTrace();
|
||||||
|
|
||||||
|
if ($exception instanceof FatalErrorException) {
|
||||||
|
if (function_exists('xdebug_get_function_stack')) {
|
||||||
|
$trace = array_slice(array_reverse(xdebug_get_function_stack()), 4);
|
||||||
|
|
||||||
|
foreach ($trace as $i => $frame) {
|
||||||
|
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
|
||||||
|
if (!isset($frame['type'])) {
|
||||||
|
$trace[$i]['type'] = '??';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('dynamic' === $trace[$i]['type']) {
|
||||||
|
$trace[$i]['type'] = '->';
|
||||||
|
} elseif ('static' === $trace[$i]['type']) {
|
||||||
|
$trace[$i]['type'] = '::';
|
||||||
|
}
|
||||||
|
|
||||||
|
// XDebug also has a different name for the parameters array
|
||||||
|
if (isset($frame['params']) && !isset($frame['args'])) {
|
||||||
|
$trace[$i]['args'] = $frame['params'];
|
||||||
|
unset($trace[$i]['params']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$trace = array_slice(array_reverse($trace), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setTrace($trace, $exception->getFile(), $exception->getLine());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTrace($trace, $file, $line)
|
||||||
|
{
|
||||||
|
$this->trace = array();
|
||||||
|
$this->trace[] = array(
|
||||||
|
'namespace' => '',
|
||||||
|
'short_class' => '',
|
||||||
|
'class' => '',
|
||||||
|
'type' => '',
|
||||||
|
'function' => '',
|
||||||
|
'file' => $file,
|
||||||
|
'line' => $line,
|
||||||
|
'args' => array(),
|
||||||
|
);
|
||||||
|
foreach ($trace as $entry) {
|
||||||
|
$class = '';
|
||||||
|
$namespace = '';
|
||||||
|
if (isset($entry['class'])) {
|
||||||
|
$parts = explode('\\', $entry['class']);
|
||||||
|
$class = array_pop($parts);
|
||||||
|
$namespace = implode('\\', $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->trace[] = array(
|
||||||
|
'namespace' => $namespace,
|
||||||
|
'short_class' => $class,
|
||||||
|
'class' => isset($entry['class']) ? $entry['class'] : '',
|
||||||
|
'type' => isset($entry['type']) ? $entry['type'] : '',
|
||||||
|
'function' => isset($entry['function']) ? $entry['function'] : null,
|
||||||
|
'file' => isset($entry['file']) ? $entry['file'] : null,
|
||||||
|
'line' => isset($entry['line']) ? $entry['line'] : null,
|
||||||
|
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function flattenArgs($args, $level = 0)
|
||||||
|
{
|
||||||
|
$result = array();
|
||||||
|
foreach ($args as $key => $value) {
|
||||||
|
if (is_object($value)) {
|
||||||
|
$result[$key] = array('object', get_class($value));
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
if ($level > 10) {
|
||||||
|
$result[$key] = array('array', '*DEEP NESTED ARRAY*');
|
||||||
|
} else {
|
||||||
|
$result[$key] = array('array', $this->flattenArgs($value, ++$level));
|
||||||
|
}
|
||||||
|
} elseif (null === $value) {
|
||||||
|
$result[$key] = array('null', null);
|
||||||
|
} elseif (is_bool($value)) {
|
||||||
|
$result[$key] = array('boolean', $value);
|
||||||
|
} elseif (is_resource($value)) {
|
||||||
|
$result[$key] = array('resource', get_resource_type($value));
|
||||||
|
} elseif ($value instanceof \__PHP_Incomplete_Class) {
|
||||||
|
// Special case of object, is_object will return false
|
||||||
|
$result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value));
|
||||||
|
} else {
|
||||||
|
$result[$key] = array('string', (string) $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
|
||||||
|
{
|
||||||
|
$array = new \ArrayObject($value);
|
||||||
|
|
||||||
|
return $array['__PHP_Incomplete_Class_Name'];
|
||||||
|
}
|
||||||
|
}
|
316
src/Symfony/Component/Debug/ExceptionHandler.php
Normal file
316
src/Symfony/Component/Debug/ExceptionHandler.php
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
<?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\Debug;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Debug\Exception\FlattenException;
|
||||||
|
|
||||||
|
if (!defined('ENT_SUBSTITUTE')) {
|
||||||
|
define('ENT_SUBSTITUTE', 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExceptionHandler converts an exception to a Response object.
|
||||||
|
*
|
||||||
|
* It is mostly useful in debug mode to replace the default PHP/XDebug
|
||||||
|
* output with something prettier and more useful.
|
||||||
|
*
|
||||||
|
* As this class is mainly used during Kernel boot, where nothing is yet
|
||||||
|
* available, the Response content is always HTML.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*/
|
||||||
|
class ExceptionHandler
|
||||||
|
{
|
||||||
|
private $debug;
|
||||||
|
private $charset;
|
||||||
|
|
||||||
|
public function __construct($debug = true, $charset = 'UTF-8')
|
||||||
|
{
|
||||||
|
$this->debug = $debug;
|
||||||
|
$this->charset = $charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the exception handler.
|
||||||
|
*
|
||||||
|
* @param Boolean $debug
|
||||||
|
*
|
||||||
|
* @return ExceptionHandler The registered exception handler
|
||||||
|
*/
|
||||||
|
public static function register($debug = true)
|
||||||
|
{
|
||||||
|
$handler = new static($debug);
|
||||||
|
|
||||||
|
set_exception_handler(array($handler, 'handle'));
|
||||||
|
|
||||||
|
return $handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a response for the given Exception.
|
||||||
|
*
|
||||||
|
* If you have the Symfony HttpFoundation component installed,
|
||||||
|
* this method will use it to create and send the response. If not,
|
||||||
|
* it will fallback to plain PHP functions.
|
||||||
|
*
|
||||||
|
* @param \Exception $exception An \Exception instance
|
||||||
|
*
|
||||||
|
* @see sendPhpResponse
|
||||||
|
* @see createResponse
|
||||||
|
*/
|
||||||
|
public function handle(\Exception $exception)
|
||||||
|
{
|
||||||
|
if (class_exists('Symfony\Component\HttpFoundation\Response')) {
|
||||||
|
$this->createResponse($exception)->send();
|
||||||
|
} else {
|
||||||
|
$this->sendPhpResponse($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the error associated with the given Exception as a plain PHP response.
|
||||||
|
*
|
||||||
|
* This method uses plain PHP functions like header() and echo to output
|
||||||
|
* the response.
|
||||||
|
*
|
||||||
|
* @param \Exception|FlattenException $exception An \Exception instance
|
||||||
|
*/
|
||||||
|
public function sendPhpResponse($exception)
|
||||||
|
{
|
||||||
|
if (!$exception instanceof FlattenException) {
|
||||||
|
$exception = FlattenException::create($exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
header(sprintf('HTTP/1.0 %s', $exception->getStatusCode()));
|
||||||
|
foreach ($exception->getHeaders() as $name => $value) {
|
||||||
|
header($name.': '.$value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the error Response associated with the given Exception.
|
||||||
|
*
|
||||||
|
* @param \Exception|FlattenException $exception An \Exception instance
|
||||||
|
*
|
||||||
|
* @return Response A Response instance
|
||||||
|
*/
|
||||||
|
public function createResponse($exception)
|
||||||
|
{
|
||||||
|
if (!$exception instanceof FlattenException) {
|
||||||
|
$exception = FlattenException::create($exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response($this->decorate($this->getContent($exception), $this->getStylesheet($exception)), $exception->getStatusCode(), $exception->getHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the HTML content associated with the given exception.
|
||||||
|
*
|
||||||
|
* @param FlattenException $exception A FlattenException instance
|
||||||
|
*
|
||||||
|
* @return string The content as a string
|
||||||
|
*/
|
||||||
|
public function getContent(FlattenException $exception)
|
||||||
|
{
|
||||||
|
switch ($exception->getStatusCode()) {
|
||||||
|
case 404:
|
||||||
|
$title = 'Sorry, the page you are looking for could not be found.';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$title = 'Whoops, looks like something went wrong.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = '';
|
||||||
|
if ($this->debug) {
|
||||||
|
try {
|
||||||
|
$count = count($exception->getAllPrevious());
|
||||||
|
$total = $count + 1;
|
||||||
|
foreach ($exception->toArray() as $position => $e) {
|
||||||
|
$ind = $count - $position + 1;
|
||||||
|
$class = $this->abbrClass($e['class']);
|
||||||
|
$message = nl2br($e['message']);
|
||||||
|
$content .= sprintf(<<<EOF
|
||||||
|
<div class="block_exception clear_fix">
|
||||||
|
<h2><span>%d/%d</span> %s: %s</h2>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<ol class="traces list_exception">
|
||||||
|
|
||||||
|
EOF
|
||||||
|
, $ind, $total, $class, $message);
|
||||||
|
foreach ($e['trace'] as $trace) {
|
||||||
|
$content .= ' <li>';
|
||||||
|
if ($trace['function']) {
|
||||||
|
$content .= sprintf('at %s%s%s(%s)', $this->abbrClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
|
||||||
|
}
|
||||||
|
if (isset($trace['file']) && isset($trace['line'])) {
|
||||||
|
if ($linkFormat = ini_get('xdebug.file_link_format')) {
|
||||||
|
$link = str_replace(array('%f', '%l'), array($trace['file'], $trace['line']), $linkFormat);
|
||||||
|
$content .= sprintf(' in <a href="%s" title="Go to source">%s line %s</a>', $link, $trace['file'], $trace['line']);
|
||||||
|
} else {
|
||||||
|
$content .= sprintf(' in %s line %s', $trace['file'], $trace['line']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$content .= "</li>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$content .= " </ol>\n</div>\n";
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// something nasty happened and we cannot throw an exception anymore
|
||||||
|
if ($this->debug) {
|
||||||
|
$title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($exception), $exception->getMessage());
|
||||||
|
} else {
|
||||||
|
$title = 'Whoops, looks like something went wrong.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <<<EOF
|
||||||
|
<div id="sf-resetcontent" class="sf-reset">
|
||||||
|
<h1>$title</h1>
|
||||||
|
$content
|
||||||
|
</div>
|
||||||
|
EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the stylesheet associated with the given exception.
|
||||||
|
*
|
||||||
|
* @param FlattenException $exception A FlattenException instance
|
||||||
|
*
|
||||||
|
* @return string The stylesheet as a string
|
||||||
|
*/
|
||||||
|
public function getStylesheet(FlattenException $exception)
|
||||||
|
{
|
||||||
|
return <<<EOF
|
||||||
|
.sf-reset { font: 11px Verdana, Arial, sans-serif; color: #333 }
|
||||||
|
.sf-reset .clear { clear:both; height:0; font-size:0; line-height:0; }
|
||||||
|
.sf-reset .clear_fix:after { display:block; height:0; clear:both; visibility:hidden; }
|
||||||
|
.sf-reset .clear_fix { display:inline-block; }
|
||||||
|
.sf-reset * html .clear_fix { height:1%; }
|
||||||
|
.sf-reset .clear_fix { display:block; }
|
||||||
|
.sf-reset, .sf-reset .block { margin: auto }
|
||||||
|
.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 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 .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;
|
||||||
|
-webkit-border-bottom-left-radius: 16px;
|
||||||
|
-moz-border-radius-bottomright: 16px;
|
||||||
|
-moz-border-radius-bottomleft: 16px;
|
||||||
|
border-bottom-right-radius: 16px;
|
||||||
|
border-bottom-left-radius: 16px;
|
||||||
|
border-bottom:1px solid #ccc;
|
||||||
|
border-right:1px solid #ccc;
|
||||||
|
border-left:1px solid #ccc;
|
||||||
|
}
|
||||||
|
.sf-reset .block_exception { background-color:#ddd; color: #333; padding:20px;
|
||||||
|
-webkit-border-top-left-radius: 16px;
|
||||||
|
-webkit-border-top-right-radius: 16px;
|
||||||
|
-moz-border-radius-topleft: 16px;
|
||||||
|
-moz-border-radius-topright: 16px;
|
||||||
|
border-top-left-radius: 16px;
|
||||||
|
border-top-right-radius: 16px;
|
||||||
|
border-top:1px solid #ccc;
|
||||||
|
border-right:1px solid #ccc;
|
||||||
|
border-left:1px solid #ccc;
|
||||||
|
overflow: hidden;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
.sf-reset li a { background:none; color:#868686; text-decoration:none; }
|
||||||
|
.sf-reset li a:hover { background:none; color:#313131; text-decoration:underline; }
|
||||||
|
.sf-reset ol { padding: 10px 0; }
|
||||||
|
.sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px;
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function decorate($content, $css)
|
||||||
|
{
|
||||||
|
return <<<EOF
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
|
<style>
|
||||||
|
/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
|
||||||
|
html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
|
||||||
|
|
||||||
|
html { background: #eee; padding: 10px }
|
||||||
|
img { border: 0; }
|
||||||
|
#sf-resetcontent { width:970px; margin:0 auto; }
|
||||||
|
$css
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
$content
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function abbrClass($class)
|
||||||
|
{
|
||||||
|
$parts = explode('\\', $class);
|
||||||
|
|
||||||
|
return sprintf("<abbr title=\"%s\">%s</abbr>", $class, array_pop($parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats an array as a string.
|
||||||
|
*
|
||||||
|
* @param array $args The argument array
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function formatArgs(array $args)
|
||||||
|
{
|
||||||
|
$result = array();
|
||||||
|
foreach ($args as $key => $item) {
|
||||||
|
if ('object' === $item[0]) {
|
||||||
|
$formattedValue = sprintf("<em>object</em>(%s)", $this->abbrClass($item[1]));
|
||||||
|
} elseif ('array' === $item[0]) {
|
||||||
|
$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 | ENT_SUBSTITUTE, $this->charset));
|
||||||
|
} elseif ('null' === $item[0]) {
|
||||||
|
$formattedValue = '<em>null</em>';
|
||||||
|
} elseif ('boolean' === $item[0]) {
|
||||||
|
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
|
||||||
|
} elseif ('resource' === $item[0]) {
|
||||||
|
$formattedValue = '<em>resource</em>';
|
||||||
|
} else {
|
||||||
|
$formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(', ', $result);
|
||||||
|
}
|
||||||
|
}
|
19
src/Symfony/Component/Debug/LICENSE
Normal file
19
src/Symfony/Component/Debug/LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2004-2013 Fabien Potencier
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
40
src/Symfony/Component/Debug/README.md
Normal file
40
src/Symfony/Component/Debug/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Debug Component
|
||||||
|
===============
|
||||||
|
|
||||||
|
Debug provides tools to make debugging easier.
|
||||||
|
|
||||||
|
Enabling all debug tools is as easy as calling the `enable()` method on the
|
||||||
|
main `Debug` class:
|
||||||
|
|
||||||
|
use Symfony\Component\Debug\Debug;
|
||||||
|
|
||||||
|
Debug::enable();
|
||||||
|
|
||||||
|
You can also use the tools individually:
|
||||||
|
|
||||||
|
use Symfony\Component\Debug\ErrorHandler;
|
||||||
|
use Symfony\Component\Debug\ExceptionHandler;
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
|
||||||
|
ErrorHandler::register($errorReportingLevel);
|
||||||
|
if ('cli' !== php_sapi_name()) {
|
||||||
|
ExceptionHandler::register();
|
||||||
|
} elseif (!ini_get('log_errors') || ini_get('error_log')) {
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that the `Debug::enable()` call also registers the debug class loader
|
||||||
|
from the Symfony ClassLoader component when available.
|
||||||
|
|
||||||
|
This component can optionally take advantage of the features of the HttpKernel
|
||||||
|
and HttpFoundation components.
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
|
||||||
|
You can run the unit tests with the following command:
|
||||||
|
|
||||||
|
$ cd path/to/Symfony/Component/Debug/
|
||||||
|
$ composer.phar install --dev
|
||||||
|
$ phpunit
|
@ -9,9 +9,9 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Tests\Debug;
|
namespace Symfony\Component\Debug\Tests;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
|
use Symfony\Component\Debug\ErrorHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ErrorHandlerTest
|
* ErrorHandlerTest
|
@ -9,9 +9,9 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Tests\Exception;
|
namespace Symfony\Component\Debug\Tests\Exception;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
use Symfony\Component\Debug\Exception\FlattenException;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
@ -9,9 +9,9 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Tests\Debug;
|
namespace Symfony\Component\Debug\Tests;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Debug\ExceptionHandler;
|
use Symfony\Component\Debug\ExceptionHandler;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||||
|
|
40
src/Symfony/Component/Debug/composer.json
Normal file
40
src/Symfony/Component/Debug/composer.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "symfony/debug",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Symfony Debug Component",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "http://symfony.com",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "http://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/http-kernel": "~2.1",
|
||||||
|
"symfony/http-foundation": "~2.1"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/http-foundation": "~2.1",
|
||||||
|
"symfony/http-kernel": "~2.1",
|
||||||
|
"symfony/class-loader": "~2.1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": { "Symfony\\Component\\Debug\\": "" }
|
||||||
|
},
|
||||||
|
"target-dir": "Symfony/Component/Debug",
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.3-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/Symfony/Component/Debug/phpunit.xml.dist
Normal file
29
src/Symfony/Component/Debug/phpunit.xml.dist
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false"
|
||||||
|
syntaxCheck="false"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Symfony Debug Component Test Suite">
|
||||||
|
<directory>./Tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory>./</directory>
|
||||||
|
<exclude>
|
||||||
|
<directory>./Tests</directory>
|
||||||
|
<directory>./vendor</directory>
|
||||||
|
</exclude>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
@ -4,6 +4,9 @@ CHANGELOG
|
|||||||
2.3.0
|
2.3.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
* deprecated `Symfony\Component\HttpKernel\Debug\ErrorHandler`, `Symfony\Component\HttpKernel\Debug\ExceptionHandler`,
|
||||||
|
`Symfony\Component\HttpKernel\Exception\FatalErrorException`, and `Symfony\Component\HttpKernel\Exception\FlattenException`
|
||||||
|
* deprecated `Symfony\Component\HttpKernel\Kernel::init()``
|
||||||
* added the possibility to specify an id an extra attributes to hinclude tags
|
* added the possibility to specify an id an extra attributes to hinclude tags
|
||||||
* added the collect of data if a controller is a Closure in the Request collector
|
* added the collect of data if a controller is a Closure in the Request collector
|
||||||
|
|
||||||
|
@ -11,118 +11,15 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Debug;
|
namespace Symfony\Component\HttpKernel\Debug;
|
||||||
|
|
||||||
use Symfony\Component\HttpKernel\Exception\FatalErrorException;
|
use Symfony\Component\Debug\ErrorHandler as DebugErrorHandler;
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ErrorHandler.
|
* ErrorHandler.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
|
||||||
*/
|
*/
|
||||||
class ErrorHandler
|
class ErrorHandler extends DebugErrorHandler
|
||||||
{
|
{
|
||||||
const TYPE_DEPRECATION = -100;
|
|
||||||
|
|
||||||
private $levels = array(
|
|
||||||
E_WARNING => 'Warning',
|
|
||||||
E_NOTICE => 'Notice',
|
|
||||||
E_USER_ERROR => 'User Error',
|
|
||||||
E_USER_WARNING => 'User Warning',
|
|
||||||
E_USER_NOTICE => 'User Notice',
|
|
||||||
E_STRICT => 'Runtime Notice',
|
|
||||||
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
|
|
||||||
E_DEPRECATED => 'Deprecated',
|
|
||||||
E_USER_DEPRECATED => 'User Deprecated',
|
|
||||||
E_ERROR => 'Error',
|
|
||||||
E_CORE_ERROR => 'Core Error',
|
|
||||||
E_COMPILE_ERROR => 'Compile Error',
|
|
||||||
E_PARSE => 'Parse',
|
|
||||||
);
|
|
||||||
|
|
||||||
private $level;
|
|
||||||
|
|
||||||
private $reservedMemory;
|
|
||||||
|
|
||||||
/** @var LoggerInterface */
|
|
||||||
private static $logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the error handler.
|
|
||||||
*
|
|
||||||
* @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
|
|
||||||
*
|
|
||||||
* @return The registered error handler
|
|
||||||
*/
|
|
||||||
public static function register($level = null)
|
|
||||||
{
|
|
||||||
$handler = new static();
|
|
||||||
$handler->setLevel($level);
|
|
||||||
|
|
||||||
ini_set('display_errors', 0);
|
|
||||||
set_error_handler(array($handler, 'handle'));
|
|
||||||
register_shutdown_function(array($handler, 'handleFatal'));
|
|
||||||
$handler->reservedMemory = str_repeat('x', 10240);
|
|
||||||
|
|
||||||
return $handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLevel($level)
|
|
||||||
{
|
|
||||||
$this->level = null === $level ? error_reporting() : $level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function setLogger(LoggerInterface $logger)
|
|
||||||
{
|
|
||||||
self::$logger = $logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \ErrorException When error_reporting returns error
|
|
||||||
*/
|
|
||||||
public function handle($level, $message, $file, $line, $context)
|
|
||||||
{
|
|
||||||
if (0 === $this->level) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) {
|
|
||||||
if (null !== self::$logger) {
|
|
||||||
$stack = version_compare(PHP_VERSION, '5.4', '<') ? array_slice(debug_backtrace(false), 0, 10) : debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
|
|
||||||
|
|
||||||
self::$logger->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_reporting() & $level && $this->level & $level) {
|
|
||||||
throw new \ErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleFatal()
|
|
||||||
{
|
|
||||||
if (null === $error = error_get_last()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($this->reservedMemory);
|
|
||||||
$type = $error['type'];
|
|
||||||
if (0 === $this->level || !in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current exception handler
|
|
||||||
$exceptionHandler = set_exception_handler(function() {});
|
|
||||||
restore_exception_handler();
|
|
||||||
|
|
||||||
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {
|
|
||||||
$level = isset($this->levels[$type]) ? $this->levels[$type] : $type;
|
|
||||||
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
|
|
||||||
$exception = new FatalErrorException($message, 0, $type, $error['file'], $error['line']);
|
|
||||||
$exceptionHandler[0]->handle($exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,273 +11,15 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Debug;
|
namespace Symfony\Component\HttpKernel\Debug;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\Debug\ExceptionHandler as DebugExceptionHandler;
|
||||||
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
|
||||||
|
|
||||||
if (!defined('ENT_SUBSTITUTE')) {
|
|
||||||
define('ENT_SUBSTITUTE', 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ExceptionHandler converts an exception to a Response object.
|
* ExceptionHandler converts an exception to a Response object.
|
||||||
*
|
*
|
||||||
* It is mostly useful in debug mode to replace the default PHP/XDebug
|
|
||||||
* output with something prettier and more useful.
|
|
||||||
*
|
|
||||||
* As this class is mainly used during Kernel boot, where nothing is yet
|
|
||||||
* available, the Response content is always HTML.
|
|
||||||
*
|
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
|
||||||
*/
|
*/
|
||||||
class ExceptionHandler
|
class ExceptionHandler extends DebugExceptionHandler
|
||||||
{
|
{
|
||||||
private $debug;
|
|
||||||
private $charset;
|
|
||||||
|
|
||||||
public function __construct($debug = true, $charset = 'UTF-8')
|
|
||||||
{
|
|
||||||
$this->debug = $debug;
|
|
||||||
$this->charset = $charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the exception handler.
|
|
||||||
*
|
|
||||||
* @param Boolean $debug
|
|
||||||
*
|
|
||||||
* @return ExceptionHandler The registered exception handler
|
|
||||||
*/
|
|
||||||
public static function register($debug = true)
|
|
||||||
{
|
|
||||||
$handler = new static($debug);
|
|
||||||
|
|
||||||
set_exception_handler(array($handler, 'handle'));
|
|
||||||
|
|
||||||
return $handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a Response for the given Exception.
|
|
||||||
*
|
|
||||||
* @param \Exception $exception An \Exception instance
|
|
||||||
*/
|
|
||||||
public function handle(\Exception $exception)
|
|
||||||
{
|
|
||||||
$this->createResponse($exception)->send();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the error Response associated with the given Exception.
|
|
||||||
*
|
|
||||||
* @param \Exception|FlattenException $exception An \Exception instance
|
|
||||||
*
|
|
||||||
* @return Response A Response instance
|
|
||||||
*/
|
|
||||||
public function createResponse($exception)
|
|
||||||
{
|
|
||||||
if (!$exception instanceof FlattenException) {
|
|
||||||
$exception = FlattenException::create($exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response($this->decorate($this->getContent($exception), $this->getStylesheet($exception)), $exception->getStatusCode(), $exception->getHeaders());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the HTML content associated with the given exception.
|
|
||||||
*
|
|
||||||
* @param FlattenException $exception A FlattenException instance
|
|
||||||
*
|
|
||||||
* @return string The content as a string
|
|
||||||
*/
|
|
||||||
public function getContent(FlattenException $exception)
|
|
||||||
{
|
|
||||||
switch ($exception->getStatusCode()) {
|
|
||||||
case 404:
|
|
||||||
$title = 'Sorry, the page you are looking for could not be found.';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$title = 'Whoops, looks like something went wrong.';
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = '';
|
|
||||||
if ($this->debug) {
|
|
||||||
try {
|
|
||||||
$count = count($exception->getAllPrevious());
|
|
||||||
$total = $count + 1;
|
|
||||||
foreach ($exception->toArray() as $position => $e) {
|
|
||||||
$ind = $count - $position + 1;
|
|
||||||
$class = $this->abbrClass($e['class']);
|
|
||||||
$message = nl2br($e['message']);
|
|
||||||
$content .= sprintf(<<<EOF
|
|
||||||
<div class="block_exception clear_fix">
|
|
||||||
<h2><span>%d/%d</span> %s: %s</h2>
|
|
||||||
</div>
|
|
||||||
<div class="block">
|
|
||||||
<ol class="traces list_exception">
|
|
||||||
|
|
||||||
EOF
|
|
||||||
, $ind, $total, $class, $message);
|
|
||||||
foreach ($e['trace'] as $trace) {
|
|
||||||
$content .= ' <li>';
|
|
||||||
if ($trace['function']) {
|
|
||||||
$content .= sprintf('at %s%s%s(%s)', $this->abbrClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
|
|
||||||
}
|
|
||||||
if (isset($trace['file']) && isset($trace['line'])) {
|
|
||||||
if ($linkFormat = ini_get('xdebug.file_link_format')) {
|
|
||||||
$link = str_replace(array('%f', '%l'), array($trace['file'], $trace['line']), $linkFormat);
|
|
||||||
$content .= sprintf(' in <a href="%s" title="Go to source">%s line %s</a>', $link, $trace['file'], $trace['line']);
|
|
||||||
} else {
|
|
||||||
$content .= sprintf(' in %s line %s', $trace['file'], $trace['line']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$content .= "</li>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$content .= " </ol>\n</div>\n";
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
// something nasty happened and we cannot throw an exception anymore
|
|
||||||
if ($this->debug) {
|
|
||||||
$title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($exception), $exception->getMessage());
|
|
||||||
} else {
|
|
||||||
$title = 'Whoops, looks like something went wrong.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return <<<EOF
|
|
||||||
<div id="sf-resetcontent" class="sf-reset">
|
|
||||||
<h1>$title</h1>
|
|
||||||
$content
|
|
||||||
</div>
|
|
||||||
EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the stylesheet associated with the given exception.
|
|
||||||
*
|
|
||||||
* @param FlattenException $exception A FlattenException instance
|
|
||||||
*
|
|
||||||
* @return string The stylesheet as a string
|
|
||||||
*/
|
|
||||||
public function getStylesheet(FlattenException $exception)
|
|
||||||
{
|
|
||||||
return <<<EOF
|
|
||||||
.sf-reset { font: 11px Verdana, Arial, sans-serif; color: #333 }
|
|
||||||
.sf-reset .clear { clear:both; height:0; font-size:0; line-height:0; }
|
|
||||||
.sf-reset .clear_fix:after { display:block; height:0; clear:both; visibility:hidden; }
|
|
||||||
.sf-reset .clear_fix { display:inline-block; }
|
|
||||||
.sf-reset * html .clear_fix { height:1%; }
|
|
||||||
.sf-reset .clear_fix { display:block; }
|
|
||||||
.sf-reset, .sf-reset .block { margin: auto }
|
|
||||||
.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 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 .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;
|
|
||||||
-webkit-border-bottom-left-radius: 16px;
|
|
||||||
-moz-border-radius-bottomright: 16px;
|
|
||||||
-moz-border-radius-bottomleft: 16px;
|
|
||||||
border-bottom-right-radius: 16px;
|
|
||||||
border-bottom-left-radius: 16px;
|
|
||||||
border-bottom:1px solid #ccc;
|
|
||||||
border-right:1px solid #ccc;
|
|
||||||
border-left:1px solid #ccc;
|
|
||||||
}
|
|
||||||
.sf-reset .block_exception { background-color:#ddd; color: #333; padding:20px;
|
|
||||||
-webkit-border-top-left-radius: 16px;
|
|
||||||
-webkit-border-top-right-radius: 16px;
|
|
||||||
-moz-border-radius-topleft: 16px;
|
|
||||||
-moz-border-radius-topright: 16px;
|
|
||||||
border-top-left-radius: 16px;
|
|
||||||
border-top-right-radius: 16px;
|
|
||||||
border-top:1px solid #ccc;
|
|
||||||
border-right:1px solid #ccc;
|
|
||||||
border-left:1px solid #ccc;
|
|
||||||
overflow: hidden;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
.sf-reset li a { background:none; color:#868686; text-decoration:none; }
|
|
||||||
.sf-reset li a:hover { background:none; color:#313131; text-decoration:underline; }
|
|
||||||
.sf-reset ol { padding: 10px 0; }
|
|
||||||
.sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px;
|
|
||||||
-webkit-border-radius: 10px;
|
|
||||||
-moz-border-radius: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function decorate($content, $css)
|
|
||||||
{
|
|
||||||
return <<<EOF
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
|
||||||
<meta name="robots" content="noindex,nofollow" />
|
|
||||||
<style>
|
|
||||||
/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
|
|
||||||
html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
|
|
||||||
|
|
||||||
html { background: #eee; padding: 10px }
|
|
||||||
img { border: 0; }
|
|
||||||
#sf-resetcontent { width:970px; margin:0 auto; }
|
|
||||||
$css
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
$content
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function abbrClass($class)
|
|
||||||
{
|
|
||||||
$parts = explode('\\', $class);
|
|
||||||
|
|
||||||
return sprintf("<abbr title=\"%s\">%s</abbr>", $class, array_pop($parts));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats an array as a string.
|
|
||||||
*
|
|
||||||
* @param array $args The argument array
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function formatArgs(array $args)
|
|
||||||
{
|
|
||||||
$result = array();
|
|
||||||
foreach ($args as $key => $item) {
|
|
||||||
if ('object' === $item[0]) {
|
|
||||||
$formattedValue = sprintf("<em>object</em>(%s)", $this->abbrClass($item[1]));
|
|
||||||
} elseif ('array' === $item[0]) {
|
|
||||||
$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 | ENT_SUBSTITUTE, $this->charset));
|
|
||||||
} elseif ('null' === $item[0]) {
|
|
||||||
$formattedValue = '<em>null</em>';
|
|
||||||
} elseif ('boolean' === $item[0]) {
|
|
||||||
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
|
|
||||||
} elseif ('resource' === $item[0]) {
|
|
||||||
$formattedValue = '<em>resource</em>';
|
|
||||||
} else {
|
|
||||||
$formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), true));
|
|
||||||
}
|
|
||||||
|
|
||||||
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(', ', $result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,15 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Exception;
|
namespace Symfony\Component\HttpKernel\Exception;
|
||||||
|
|
||||||
|
use Symfony\Component\Debug\Exception\FatalErrorException as DebugFatalErrorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fatal Error Exception.
|
* Fatal Error Exception.
|
||||||
*
|
*
|
||||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||||
|
*
|
||||||
|
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
|
||||||
*/
|
*/
|
||||||
class FatalErrorException extends \ErrorException
|
class FatalErrorException extends DebugFatalErrorException
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,265 +11,17 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Exception;
|
namespace Symfony\Component\HttpKernel\Exception;
|
||||||
|
|
||||||
|
use Symfony\Component\Debug\Exception\FlattenException as DebugFlattenException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FlattenException wraps a PHP Exception to be able to serialize it.
|
* FlattenException wraps a PHP Exception to be able to serialize it.
|
||||||
*
|
*
|
||||||
* Basically, this class removes all objects from the trace.
|
* Basically, this class removes all objects from the trace.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
|
||||||
*/
|
*/
|
||||||
class FlattenException
|
class FlattenException extends DebugFlattenException
|
||||||
{
|
{
|
||||||
private $message;
|
|
||||||
private $code;
|
|
||||||
private $previous;
|
|
||||||
private $trace;
|
|
||||||
private $class;
|
|
||||||
private $statusCode;
|
|
||||||
private $headers;
|
|
||||||
private $file;
|
|
||||||
private $line;
|
|
||||||
|
|
||||||
public static function create(\Exception $exception, $statusCode = null, array $headers = array())
|
|
||||||
{
|
|
||||||
$e = new static();
|
|
||||||
$e->setMessage($exception->getMessage());
|
|
||||||
$e->setCode($exception->getCode());
|
|
||||||
|
|
||||||
if ($exception instanceof HttpExceptionInterface) {
|
|
||||||
$statusCode = $exception->getStatusCode();
|
|
||||||
$headers = array_merge($headers, $exception->getHeaders());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $statusCode) {
|
|
||||||
$statusCode = 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
$e->setStatusCode($statusCode);
|
|
||||||
$e->setHeaders($headers);
|
|
||||||
$e->setTraceFromException($exception);
|
|
||||||
$e->setClass(get_class($exception));
|
|
||||||
$e->setFile($exception->getFile());
|
|
||||||
$e->setLine($exception->getLine());
|
|
||||||
if ($exception->getPrevious()) {
|
|
||||||
$e->setPrevious(static::create($exception->getPrevious()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toArray()
|
|
||||||
{
|
|
||||||
$exceptions = array();
|
|
||||||
foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) {
|
|
||||||
$exceptions[] = array(
|
|
||||||
'message' => $exception->getMessage(),
|
|
||||||
'class' => $exception->getClass(),
|
|
||||||
'trace' => $exception->getTrace(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $exceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStatusCode()
|
|
||||||
{
|
|
||||||
return $this->statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setStatusCode($code)
|
|
||||||
{
|
|
||||||
$this->statusCode = $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHeaders()
|
|
||||||
{
|
|
||||||
return $this->headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setHeaders(array $headers)
|
|
||||||
{
|
|
||||||
$this->headers = $headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClass()
|
|
||||||
{
|
|
||||||
return $this->class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setClass($class)
|
|
||||||
{
|
|
||||||
$this->class = $class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFile()
|
|
||||||
{
|
|
||||||
return $this->file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setFile($file)
|
|
||||||
{
|
|
||||||
$this->file = $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLine()
|
|
||||||
{
|
|
||||||
return $this->line;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLine($line)
|
|
||||||
{
|
|
||||||
$this->line = $line;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMessage()
|
|
||||||
{
|
|
||||||
return $this->message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setMessage($message)
|
|
||||||
{
|
|
||||||
$this->message = $message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCode()
|
|
||||||
{
|
|
||||||
return $this->code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setCode($code)
|
|
||||||
{
|
|
||||||
$this->code = $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPrevious()
|
|
||||||
{
|
|
||||||
return $this->previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setPrevious(FlattenException $previous)
|
|
||||||
{
|
|
||||||
$this->previous = $previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAllPrevious()
|
|
||||||
{
|
|
||||||
$exceptions = array();
|
|
||||||
$e = $this;
|
|
||||||
while ($e = $e->getPrevious()) {
|
|
||||||
$exceptions[] = $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $exceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTrace()
|
|
||||||
{
|
|
||||||
return $this->trace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setTraceFromException(\Exception $exception)
|
|
||||||
{
|
|
||||||
$trace = $exception->getTrace();
|
|
||||||
|
|
||||||
if ($exception instanceof FatalErrorException) {
|
|
||||||
if (function_exists('xdebug_get_function_stack')) {
|
|
||||||
$trace = array_slice(array_reverse(xdebug_get_function_stack()), 4);
|
|
||||||
|
|
||||||
foreach ($trace as $i => $frame) {
|
|
||||||
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
|
|
||||||
if (!isset($frame['type'])) {
|
|
||||||
$trace[$i]['type'] = '??';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('dynamic' === $trace[$i]['type']) {
|
|
||||||
$trace[$i]['type'] = '->';
|
|
||||||
} elseif ('static' === $trace[$i]['type']) {
|
|
||||||
$trace[$i]['type'] = '::';
|
|
||||||
}
|
|
||||||
|
|
||||||
// XDebug also has a different name for the parameters array
|
|
||||||
if (isset($frame['params']) && !isset($frame['args'])) {
|
|
||||||
$trace[$i]['args'] = $frame['params'];
|
|
||||||
unset($trace[$i]['params']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$trace = array_slice(array_reverse($trace), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->setTrace($trace, $exception->getFile(), $exception->getLine());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setTrace($trace, $file, $line)
|
|
||||||
{
|
|
||||||
$this->trace = array();
|
|
||||||
$this->trace[] = array(
|
|
||||||
'namespace' => '',
|
|
||||||
'short_class' => '',
|
|
||||||
'class' => '',
|
|
||||||
'type' => '',
|
|
||||||
'function' => '',
|
|
||||||
'file' => $file,
|
|
||||||
'line' => $line,
|
|
||||||
'args' => array(),
|
|
||||||
);
|
|
||||||
foreach ($trace as $entry) {
|
|
||||||
$class = '';
|
|
||||||
$namespace = '';
|
|
||||||
if (isset($entry['class'])) {
|
|
||||||
$parts = explode('\\', $entry['class']);
|
|
||||||
$class = array_pop($parts);
|
|
||||||
$namespace = implode('\\', $parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->trace[] = array(
|
|
||||||
'namespace' => $namespace,
|
|
||||||
'short_class' => $class,
|
|
||||||
'class' => isset($entry['class']) ? $entry['class'] : '',
|
|
||||||
'type' => isset($entry['type']) ? $entry['type'] : '',
|
|
||||||
'function' => isset($entry['function']) ? $entry['function'] : null,
|
|
||||||
'file' => isset($entry['file']) ? $entry['file'] : null,
|
|
||||||
'line' => isset($entry['line']) ? $entry['line'] : null,
|
|
||||||
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function flattenArgs($args, $level = 0)
|
|
||||||
{
|
|
||||||
$result = array();
|
|
||||||
foreach ($args as $key => $value) {
|
|
||||||
if (is_object($value)) {
|
|
||||||
$result[$key] = array('object', get_class($value));
|
|
||||||
} elseif (is_array($value)) {
|
|
||||||
if ($level > 10) {
|
|
||||||
$result[$key] = array('array', '*DEEP NESTED ARRAY*');
|
|
||||||
} else {
|
|
||||||
$result[$key] = array('array', $this->flattenArgs($value, ++$level));
|
|
||||||
}
|
|
||||||
} elseif (null === $value) {
|
|
||||||
$result[$key] = array('null', null);
|
|
||||||
} elseif (is_bool($value)) {
|
|
||||||
$result[$key] = array('boolean', $value);
|
|
||||||
} elseif (is_resource($value)) {
|
|
||||||
$result[$key] = array('resource', get_resource_type($value));
|
|
||||||
} elseif ($value instanceof \__PHP_Incomplete_Class) {
|
|
||||||
// Special case of object, is_object will return false
|
|
||||||
$result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value));
|
|
||||||
} else {
|
|
||||||
$result[$key] = array('string', (string) $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
|
|
||||||
{
|
|
||||||
$array = new \ArrayObject($value);
|
|
||||||
|
|
||||||
return $array['__PHP_Incomplete_Class_Name'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,10 @@ use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
|||||||
use Symfony\Component\HttpKernel\Config\FileLocator;
|
use Symfony\Component\HttpKernel\Config\FileLocator;
|
||||||
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
|
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
|
||||||
use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass;
|
use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass;
|
||||||
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
|
|
||||||
use Symfony\Component\HttpKernel\Debug\ExceptionHandler;
|
|
||||||
use Symfony\Component\Config\Loader\LoaderResolver;
|
use Symfony\Component\Config\Loader\LoaderResolver;
|
||||||
use Symfony\Component\Config\Loader\DelegatingLoader;
|
use Symfony\Component\Config\Loader\DelegatingLoader;
|
||||||
use Symfony\Component\Config\ConfigCache;
|
use Symfony\Component\Config\ConfigCache;
|
||||||
use Symfony\Component\ClassLoader\ClassCollectionLoader;
|
use Symfony\Component\ClassLoader\ClassCollectionLoader;
|
||||||
use Symfony\Component\ClassLoader\DebugClassLoader;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Kernel is the heart of the Symfony system.
|
* The Kernel is the heart of the Symfony system.
|
||||||
@ -60,7 +57,6 @@ abstract class Kernel implements KernelInterface, TerminableInterface
|
|||||||
protected $name;
|
protected $name;
|
||||||
protected $startTime;
|
protected $startTime;
|
||||||
protected $classes;
|
protected $classes;
|
||||||
protected $errorReportingLevel;
|
|
||||||
|
|
||||||
const VERSION = '2.3.0-DEV';
|
const VERSION = '2.3.0-DEV';
|
||||||
const VERSION_ID = '20300';
|
const VERSION_ID = '20300';
|
||||||
@ -94,21 +90,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface
|
|||||||
$this->init();
|
$this->init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Deprecated since version 2.3, to be removed in 3.0. Move your logic in the constructor instead.
|
||||||
|
*/
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
ini_set('display_errors', 0);
|
|
||||||
|
|
||||||
if ($this->debug) {
|
|
||||||
error_reporting(-1);
|
|
||||||
|
|
||||||
DebugClassLoader::enable();
|
|
||||||
ErrorHandler::register($this->errorReportingLevel);
|
|
||||||
if ('cli' !== php_sapi_name()) {
|
|
||||||
ExceptionHandler::register();
|
|
||||||
} elseif (!ini_get('log_errors') || ini_get('error_log')) {
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __clone()
|
public function __clone()
|
||||||
|
Reference in New Issue
Block a user