mirror of
https://github.com/symfony/debug.git
synced 2026-03-25 09:42:20 +01:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4851a041c4 | ||
|
|
366f3fe9ea | ||
|
|
d59aecf5f4 | ||
|
|
c214e92851 | ||
|
|
dfe71c73cf | ||
|
|
ad4511a8fd | ||
|
|
8bc9390ba3 | ||
|
|
572006675e | ||
|
|
95a5aa2b72 | ||
|
|
289e4e76fd | ||
|
|
c1dc68518c | ||
|
|
cd19fabc2a | ||
|
|
eb7564c66d | ||
|
|
9cd1287f00 | ||
|
|
8f9d6bcc9b | ||
|
|
01c8bc9bac | ||
|
|
c39932e3e4 | ||
|
|
2b8d1207a7 | ||
|
|
21e1aa2ed7 | ||
|
|
76df7e93ea | ||
|
|
4bd15a76de | ||
|
|
d49a46a20a | ||
|
|
796dd4cd11 | ||
|
|
5c1570dea1 | ||
|
|
3e889e0489 | ||
|
|
692ba4b3dd | ||
|
|
95db75835d | ||
|
|
dfb9bf2e04 | ||
|
|
29a6fd38f3 | ||
|
|
712c58246b | ||
|
|
ee04626994 | ||
|
|
4ba2bda995 | ||
|
|
0a45b2cdbf | ||
|
|
24170a0eb7 | ||
|
|
b7c4104ea0 | ||
|
|
150c80059c | ||
|
|
4e45617592 | ||
|
|
ed3019589c | ||
|
|
ee29381719 | ||
|
|
dc705a40a6 | ||
|
|
5d7520c712 | ||
|
|
028a28b024 | ||
|
|
7213c8200d | ||
|
|
5763a1ce68 | ||
|
|
88f529be2c | ||
|
|
17cd4b901a | ||
|
|
363d28c3b6 | ||
|
|
672a71e723 | ||
|
|
6e5c2b9e1f | ||
|
|
53a89903d1 | ||
|
|
04dc3b229d | ||
|
|
dcd43ab175 | ||
|
|
bc5a43ca7c | ||
|
|
0021c1d0d2 | ||
|
|
783848f50f | ||
|
|
86497902eb | ||
|
|
b65a5ffc25 | ||
|
|
fe37874581 | ||
|
|
e6c079c4c1 | ||
|
|
62ebdfc7d6 | ||
|
|
7a2e2e7580 | ||
|
|
55281335c3 | ||
|
|
adc2e6d9b0 | ||
|
|
213f2cf05e | ||
|
|
9e11c84428 | ||
|
|
cfd2c55384 | ||
|
|
3db261c0bd | ||
|
|
065ba32a36 | ||
|
|
b65a554e0a | ||
|
|
b92c90f028 | ||
|
|
c80c19c531 | ||
|
|
de91f9cf14 | ||
|
|
a331253db6 | ||
|
|
1164432942 | ||
|
|
345dc81e70 | ||
|
|
610797d590 | ||
|
|
1c5c246195 | ||
|
|
87df61ef76 | ||
|
|
d99bc72746 | ||
|
|
ecc79100d3 | ||
|
|
c07010012d | ||
|
|
2394e45f56 | ||
|
|
08b529b4c0 | ||
|
|
9d3d6734bd | ||
|
|
49c9cb8adc | ||
|
|
542c721438 |
@@ -28,8 +28,8 @@ class Debug
|
||||
* If the Symfony ClassLoader component is available, a special
|
||||
* class loader is also registered.
|
||||
*
|
||||
* @param int $errorReportingLevel The level of error reporting you want
|
||||
* @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
|
||||
* @param int $errorReportingLevel The level of error reporting you want
|
||||
* @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
|
||||
*/
|
||||
public static function enable($errorReportingLevel = null, $displayErrors = true)
|
||||
{
|
||||
|
||||
@@ -65,11 +65,7 @@ class DebugClassLoader
|
||||
*/
|
||||
public function getClassLoader()
|
||||
{
|
||||
if ($this->wasFinder) {
|
||||
return $this->classLoader[0];
|
||||
} else {
|
||||
return $this->classLoader;
|
||||
}
|
||||
return $this->wasFinder ? $this->classLoader[0] : $this->classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +137,7 @@ class DebugClassLoader
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return bool|null True, if loaded
|
||||
* @return bool|null True, if loaded
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
@@ -214,7 +210,7 @@ class DebugClassLoader
|
||||
chdir($cwd);
|
||||
}
|
||||
|
||||
if ( 0 === substr_compare($real, $tail, -strlen($tail), strlen($tail), true)
|
||||
if (0 === substr_compare($real, $tail, -strlen($tail), strlen($tail), true)
|
||||
&& 0 !== substr_compare($real, $tail, -strlen($tail), strlen($tail), false)
|
||||
) {
|
||||
throw new \RuntimeException(sprintf('Case mismatch between class and source file names: %s vs %s', $class, $real));
|
||||
|
||||
211
ErrorHandler.php
211
ErrorHandler.php
@@ -14,6 +14,7 @@ namespace Symfony\Component\Debug;
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Debug\Exception\ContextErrorException;
|
||||
use Symfony\Component\Debug\Exception\FatalBaseException;
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
use Symfony\Component\Debug\Exception\OutOfMemoryException;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
||||
@@ -101,7 +102,7 @@ class ErrorHandler
|
||||
private static $stackedErrorLevels = array();
|
||||
|
||||
/**
|
||||
* Same init value as thrownErrors
|
||||
* Same init value as thrownErrors.
|
||||
*
|
||||
* @deprecated since 2.6, to be removed in 3.0.
|
||||
*/
|
||||
@@ -240,7 +241,7 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error levels that are to be thrown.
|
||||
* Sets the PHP error levels that throw an exception when a PHP error occurs.
|
||||
*
|
||||
* @param int $levels A bit field of E_* constants for thrown errors
|
||||
* @param bool $replace Replace or amend the previous value
|
||||
@@ -263,7 +264,7 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error levels that are logged or thrown with their local scope.
|
||||
* Sets the PHP error levels for which local variables are preserved.
|
||||
*
|
||||
* @param int $levels A bit field of E_* constants for scoped errors
|
||||
* @param bool $replace Replace or amend the previous value
|
||||
@@ -282,7 +283,7 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error levels that are logged with their stack trace.
|
||||
* Sets the PHP error levels for which the stack trace is preserved.
|
||||
*
|
||||
* @param int $levels A bit field of E_* constants for traced errors
|
||||
* @param bool $replace Replace or amend the previous value
|
||||
@@ -338,7 +339,7 @@ class ErrorHandler
|
||||
/**
|
||||
* Handles errors by filtering then logging them according to the configured bit fields.
|
||||
*
|
||||
* @param int $type One of the E_* constants
|
||||
* @param int $type One of the E_* constants
|
||||
* @param string $file
|
||||
* @param int $line
|
||||
* @param array $context
|
||||
@@ -356,69 +357,71 @@ class ErrorHandler
|
||||
$throw = $this->thrownErrors & $type & $level;
|
||||
$type &= $level | $this->screamedErrors;
|
||||
|
||||
if ($type && ($log || $throw)) {
|
||||
if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
|
||||
$e = $context; // Whatever the signature of the method,
|
||||
unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
|
||||
$context = $e;
|
||||
}
|
||||
if (!$type || (!$log && !$throw)) {
|
||||
return $type && $log;
|
||||
}
|
||||
|
||||
if ($throw) {
|
||||
if (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
|
||||
// Checking for class existence is a work around for https://bugs.php.net/42098
|
||||
$throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
|
||||
} else {
|
||||
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
|
||||
}
|
||||
if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
|
||||
$e = $context; // Whatever the signature of the method,
|
||||
unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
|
||||
$context = $e;
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
|
||||
// Exceptions thrown from error handlers are sometimes not caught by the exception
|
||||
// handler and shutdown handlers are bypassed before 5.4.8/5.3.18.
|
||||
// We temporarily re-enable display_errors to prevent any blank page related to this bug.
|
||||
|
||||
$throw->errorHandlerCanary = new ErrorHandlerCanary();
|
||||
}
|
||||
|
||||
throw $throw;
|
||||
}
|
||||
|
||||
// For duplicated errors, log the trace only once
|
||||
$e = md5("{$type}/{$line}/{$file}\x00{$message}", true);
|
||||
$trace = true;
|
||||
|
||||
if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) {
|
||||
$trace = false;
|
||||
if ($throw) {
|
||||
if (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
|
||||
// Checking for class existence is a work around for https://bugs.php.net/42098
|
||||
$throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
|
||||
} else {
|
||||
$this->loggedTraces[$e] = 1;
|
||||
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
|
||||
}
|
||||
|
||||
$e = compact('type', 'file', 'line', 'level');
|
||||
if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
|
||||
// Exceptions thrown from error handlers are sometimes not caught by the exception
|
||||
// handler and shutdown handlers are bypassed before 5.4.8/5.3.18.
|
||||
// We temporarily re-enable display_errors to prevent any blank page related to this bug.
|
||||
|
||||
if ($type & $level) {
|
||||
if ($this->scopedErrors & $type) {
|
||||
$e['context'] = $context;
|
||||
if ($trace) {
|
||||
$e['stack'] = debug_backtrace(true); // Provide object
|
||||
}
|
||||
} elseif ($trace) {
|
||||
$e['stack'] = debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false);
|
||||
}
|
||||
$throw->errorHandlerCanary = new ErrorHandlerCanary();
|
||||
}
|
||||
|
||||
if ($this->isRecursive) {
|
||||
$log = 0;
|
||||
} elseif (self::$stackedErrorLevels) {
|
||||
self::$stackedErrors[] = array($this->loggers[$type], $message, $e);
|
||||
} else {
|
||||
try {
|
||||
$this->isRecursive = true;
|
||||
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
|
||||
$this->isRecursive = false;
|
||||
} catch (\Exception $e) {
|
||||
$this->isRecursive = false;
|
||||
throw $throw;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
// For duplicated errors, log the trace only once
|
||||
$e = md5("{$type}/{$line}/{$file}\x00{$message}", true);
|
||||
$trace = true;
|
||||
|
||||
if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) {
|
||||
$trace = false;
|
||||
} else {
|
||||
$this->loggedTraces[$e] = 1;
|
||||
}
|
||||
|
||||
$e = compact('type', 'file', 'line', 'level');
|
||||
|
||||
if ($type & $level) {
|
||||
if ($this->scopedErrors & $type) {
|
||||
$e['scope_vars'] = $context;
|
||||
if ($trace) {
|
||||
$e['stack'] = debug_backtrace(true); // Provide object
|
||||
}
|
||||
} elseif ($trace) {
|
||||
$e['stack'] = debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isRecursive) {
|
||||
$log = 0;
|
||||
} elseif (self::$stackedErrorLevels) {
|
||||
self::$stackedErrors[] = array($this->loggers[$type], $message, $e);
|
||||
} else {
|
||||
try {
|
||||
$this->isRecursive = true;
|
||||
$this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
|
||||
$this->isRecursive = false;
|
||||
} catch (\Exception $e) {
|
||||
$this->isRecursive = false;
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,23 +431,34 @@ class ErrorHandler
|
||||
/**
|
||||
* Handles an exception by logging then forwarding it to an other handler.
|
||||
*
|
||||
* @param \Exception $exception An exception to handle
|
||||
* @param array $error An array as returned by error_get_last()
|
||||
* @param \Exception|\BaseException $exception An exception to handle
|
||||
* @param array $error An array as returned by error_get_last()
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function handleException(\Exception $exception, array $error = null)
|
||||
public function handleException($exception, array $error = null)
|
||||
{
|
||||
$level = error_reporting();
|
||||
if ($this->loggedErrors & E_ERROR & ($level | $this->screamedErrors)) {
|
||||
if (!$exception instanceof \Exception) {
|
||||
$exception = new FatalBaseException($exception);
|
||||
}
|
||||
$type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
|
||||
|
||||
if ($this->loggedErrors & $type) {
|
||||
$e = array(
|
||||
'type' => E_ERROR,
|
||||
'type' => $type,
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'level' => $level,
|
||||
'level' => error_reporting(),
|
||||
'stack' => $exception->getTrace(),
|
||||
);
|
||||
if ($exception instanceof FatalErrorException) {
|
||||
if ($exception instanceof FatalBaseException) {
|
||||
$error = array(
|
||||
'type' => $type,
|
||||
'message' => $message = $exception->getMessage(),
|
||||
'file' => $e['file'],
|
||||
'line' => $e['line'],
|
||||
);
|
||||
} elseif ($exception instanceof FatalErrorException) {
|
||||
$message = 'Fatal '.$exception->getMessage();
|
||||
} elseif ($exception instanceof \ErrorException) {
|
||||
$message = 'Uncaught '.$exception->getMessage();
|
||||
@@ -474,6 +488,9 @@ class ErrorHandler
|
||||
} catch (\Exception $handlerException) {
|
||||
$this->exceptionHandler = null;
|
||||
$this->handleException($handlerException);
|
||||
} catch (\BaseException $handlerException) {
|
||||
$this->exceptionHandler = null;
|
||||
$this->handleException($handlerException);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,40 +504,44 @@ class ErrorHandler
|
||||
public static function handleFatalError(array $error = null)
|
||||
{
|
||||
self::$reservedMemory = '';
|
||||
|
||||
$handler = set_error_handler('var_dump', 0);
|
||||
$handler = is_array($handler) ? $handler[0] : null;
|
||||
restore_error_handler();
|
||||
if ($handler instanceof self) {
|
||||
if (null === $error) {
|
||||
$error = error_get_last();
|
||||
}
|
||||
|
||||
try {
|
||||
while (self::$stackedErrorLevels) {
|
||||
static::unstackErrors();
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
// Handled below
|
||||
}
|
||||
if (!$handler instanceof self) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($error && ($error['type'] & (E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR))) {
|
||||
// Let's not throw anymore but keep logging
|
||||
$handler->throwAt(0, true);
|
||||
if (null === $error) {
|
||||
$error = error_get_last();
|
||||
}
|
||||
|
||||
if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
|
||||
$exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false);
|
||||
} else {
|
||||
$exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true);
|
||||
}
|
||||
} elseif (!isset($exception)) {
|
||||
return;
|
||||
try {
|
||||
while (self::$stackedErrorLevels) {
|
||||
static::unstackErrors();
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
// Handled below
|
||||
}
|
||||
|
||||
try {
|
||||
$handler->handleException($exception, $error);
|
||||
} catch (FatalErrorException $e) {
|
||||
// Ignore this re-throw
|
||||
if ($error && ($error['type'] & (E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR))) {
|
||||
// Let's not throw anymore but keep logging
|
||||
$handler->throwAt(0, true);
|
||||
|
||||
if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
|
||||
$exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false);
|
||||
} else {
|
||||
$exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true);
|
||||
}
|
||||
} elseif (!isset($exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$handler->handleException($exception, $error);
|
||||
} catch (FatalErrorException $e) {
|
||||
// Ignore this re-throw
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +562,7 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Unstacks stacked errors and forwards to the logger
|
||||
* Unstacks stacked errors and forwards to the logger.
|
||||
*/
|
||||
public static function unstackErrors()
|
||||
{
|
||||
@@ -584,7 +605,7 @@ class ErrorHandler
|
||||
/**
|
||||
* Sets the level at which the conversion to Exception is done.
|
||||
*
|
||||
* @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
|
||||
* @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
|
||||
*
|
||||
* @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead.
|
||||
*/
|
||||
@@ -597,7 +618,7 @@ class ErrorHandler
|
||||
/**
|
||||
* Sets the display_errors flag value.
|
||||
*
|
||||
* @param int $displayErrors The display_errors flag value
|
||||
* @param int $displayErrors The display_errors flag value
|
||||
*
|
||||
* @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead.
|
||||
*/
|
||||
@@ -660,7 +681,7 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Private class used to work around https://bugs.php.net/54275
|
||||
* Private class used to work around https://bugs.php.net/54275.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
|
||||
44
Exception/FatalBaseException.php
Normal file
44
Exception/FatalBaseException.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Base Fatal Error Exception.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class FatalBaseException extends FatalErrorException
|
||||
{
|
||||
public function __construct(\BaseException $e)
|
||||
{
|
||||
if ($e instanceof \ParseException) {
|
||||
$message = 'Parse error: '.$e->getMessage();
|
||||
$severity = E_PARSE;
|
||||
} elseif ($e instanceof \TypeException) {
|
||||
$message = 'Type error: '.$e->getMessage();
|
||||
$severity = E_RECOVERABLE_ERROR;
|
||||
} else {
|
||||
$message = 'Fatal error: '.$e->getMessage();
|
||||
$severity = E_ERROR;
|
||||
}
|
||||
|
||||
\ErrorException::__construct(
|
||||
$message,
|
||||
$e->getCode(),
|
||||
$severity,
|
||||
$e->getFile(),
|
||||
$e->getLine()
|
||||
);
|
||||
|
||||
$this->setTrace($e->getTrace());
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Debug\Exception;
|
||||
namespace Symfony\Component\HttpKernel\Exception;
|
||||
|
||||
/**
|
||||
* Fatal Error Exception.
|
||||
@@ -17,8 +17,23 @@ namespace Symfony\Component\Debug\Exception;
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
|
||||
*/
|
||||
class FatalErrorException extends \ErrorException
|
||||
{
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Debug\Exception;
|
||||
|
||||
use Symfony\Component\HttpKernel\Exception\FatalErrorException as LegacyFatalErrorException;
|
||||
|
||||
/**
|
||||
* Fatal Error Exception.
|
||||
*
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
*/
|
||||
class FatalErrorException extends LegacyFatalErrorException
|
||||
{
|
||||
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true)
|
||||
{
|
||||
|
||||
@@ -9,8 +9,49 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Exception;
|
||||
|
||||
use Symfony\Component\Debug\Exception\FlattenException as DebugFlattenException;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
|
||||
*/
|
||||
class FlattenException
|
||||
{
|
||||
private $handler;
|
||||
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
if (!method_exists('Symfony\Component\Debug\Exception\FlattenException', $method)) {
|
||||
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_called_class(), $method));
|
||||
}
|
||||
|
||||
return call_user_func_array(array('Symfony\Component\Debug\Exception\FlattenException', $method), $args);
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if (!isset($this->handler)) {
|
||||
$this->handler = new DebugFlattenException();
|
||||
}
|
||||
|
||||
if (!method_exists($this->handler, $method)) {
|
||||
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_class($this), $method));
|
||||
}
|
||||
|
||||
return call_user_func_array(array($this->handler, $method), $args);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Debug\Exception;
|
||||
|
||||
use Symfony\Component\HttpKernel\Exception\FlattenException as LegacyFlattenException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
|
||||
/**
|
||||
@@ -20,7 +61,7 @@ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FlattenException
|
||||
class FlattenException extends LegacyFlattenException
|
||||
{
|
||||
private $message;
|
||||
private $code;
|
||||
@@ -210,17 +251,20 @@ class FlattenException
|
||||
}
|
||||
}
|
||||
|
||||
private function flattenArgs($args, $level = 0)
|
||||
private function flattenArgs($args, $level = 0, &$count = 0)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($args as $key => $value) {
|
||||
if (++$count > 1e4) {
|
||||
return array('array', '*SKIPPED over 10000 entries*');
|
||||
}
|
||||
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 + 1));
|
||||
$result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count));
|
||||
}
|
||||
} elseif (null === $value) {
|
||||
$result[$key] = array('null', null);
|
||||
|
||||
@@ -30,27 +30,37 @@ use Symfony\Component\Debug\Exception\OutOfMemoryException;
|
||||
class ExceptionHandler
|
||||
{
|
||||
private $debug;
|
||||
private $charset;
|
||||
private $handler;
|
||||
private $caughtBuffer;
|
||||
private $caughtLength;
|
||||
private $fileLinkFormat;
|
||||
|
||||
public function __construct($debug = true, $fileLinkFormat = null)
|
||||
public function __construct($debug = true, $charset = null, $fileLinkFormat = null)
|
||||
{
|
||||
if (false !== strpos($charset, '%') xor false === strpos($fileLinkFormat, '%')) {
|
||||
// Swap $charset and $fileLinkFormat for BC reasons
|
||||
$pivot = $fileLinkFormat;
|
||||
$fileLinkFormat = $charset;
|
||||
$charset = $pivot;
|
||||
}
|
||||
$this->debug = $debug;
|
||||
$this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8';
|
||||
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the exception handler.
|
||||
*
|
||||
* @param bool $debug
|
||||
* @param bool $debug Enable/disable debug mode, where the stack trace is displayed
|
||||
* @param string|null $charset The charset used by exception messages
|
||||
* @param string|null $fileLinkFormat The IDE link template
|
||||
*
|
||||
* @return ExceptionHandler The registered exception handler
|
||||
*/
|
||||
public static function register($debug = true, $fileLinkFormat = null)
|
||||
public static function register($debug = true, $charset = null, $fileLinkFormat = null)
|
||||
{
|
||||
$handler = new static($debug, $fileLinkFormat = null);
|
||||
$handler = new static($debug, $charset, $fileLinkFormat);
|
||||
|
||||
$prev = set_exception_handler(array($handler, 'handle'));
|
||||
if (is_array($prev) && $prev[0] instanceof ErrorHandler) {
|
||||
@@ -142,8 +152,10 @@ class ExceptionHandler
|
||||
* this method will use it to create and send the response. If not,
|
||||
* it will fallback to plain PHP functions.
|
||||
*
|
||||
* @see sendPhpResponse
|
||||
* @see createResponse
|
||||
* @param \Exception $exception An \Exception instance
|
||||
*
|
||||
* @see sendPhpResponse()
|
||||
* @see createResponse()
|
||||
*/
|
||||
private function failSafeHandle(\Exception $exception)
|
||||
{
|
||||
@@ -175,6 +187,7 @@ class ExceptionHandler
|
||||
foreach ($exception->getHeaders() as $name => $value) {
|
||||
header($name.': '.$value, false);
|
||||
}
|
||||
header('Content-Type: text/html; charset='.$this->charset);
|
||||
}
|
||||
|
||||
echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
|
||||
@@ -193,7 +206,7 @@ class ExceptionHandler
|
||||
$exception = FlattenException::create($exception);
|
||||
}
|
||||
|
||||
return new Response($this->decorate($this->getContent($exception), $this->getStylesheet($exception)), $exception->getStatusCode(), $exception->getHeaders());
|
||||
return Response::create($this->decorate($this->getContent($exception), $this->getStylesheet($exception)), $exception->getStatusCode(), $exception->getHeaders())->setCharset($this->charset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,7 +234,7 @@ class ExceptionHandler
|
||||
foreach ($exception->toArray() as $position => $e) {
|
||||
$ind = $count - $position + 1;
|
||||
$class = $this->formatClass($e['class']);
|
||||
$message = nl2br(self::utf8Htmlize($e['message']));
|
||||
$message = nl2br($this->escapeHtml($e['message']));
|
||||
$content .= sprintf(<<<EOF
|
||||
<h2 class="block_exception clear_fix">
|
||||
<span class="exception_counter">%d/%d</span>
|
||||
@@ -249,7 +262,7 @@ EOF
|
||||
} 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($e), $e->getMessage());
|
||||
$title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage()));
|
||||
} else {
|
||||
$title = 'Whoops, looks like something went wrong.';
|
||||
}
|
||||
@@ -335,7 +348,7 @@ EOF;
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta charset="{$this->charset}" />
|
||||
<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 */
|
||||
@@ -358,16 +371,16 @@ EOF;
|
||||
{
|
||||
$parts = explode('\\', $class);
|
||||
|
||||
return sprintf("<abbr title=\"%s\">%s</abbr>", $class, array_pop($parts));
|
||||
return sprintf('<abbr title="%s">%s</abbr>', $class, array_pop($parts));
|
||||
}
|
||||
|
||||
private function formatPath($path, $line)
|
||||
{
|
||||
$path = self::utf8Htmlize($path);
|
||||
$path = $this->escapeHtml($path);
|
||||
$file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path;
|
||||
|
||||
if ($linkFormat = $this->fileLinkFormat) {
|
||||
$link = str_replace(array('%f', '%l'), array($path, $line), $linkFormat);
|
||||
$link = strtr($this->escapeHtml($linkFormat), array('%f' => $path, '%l' => (int) $line));
|
||||
|
||||
return sprintf(' in <a href="%s" title="Go to source">%s line %d</a>', $link, $file, $line);
|
||||
}
|
||||
@@ -387,11 +400,11 @@ EOF;
|
||||
$result = array();
|
||||
foreach ($args as $key => $item) {
|
||||
if ('object' === $item[0]) {
|
||||
$formattedValue = sprintf("<em>object</em>(%s)", $this->formatClass($item[1]));
|
||||
$formattedValue = sprintf('<em>object</em>(%s)', $this->formatClass($item[1]));
|
||||
} 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'", self::utf8Htmlize($item[1]));
|
||||
$formattedValue = sprintf("'%s'", $this->escapeHtml($item[1]));
|
||||
} elseif ('null' === $item[0]) {
|
||||
$formattedValue = '<em>null</em>';
|
||||
} elseif ('boolean' === $item[0]) {
|
||||
@@ -399,7 +412,7 @@ EOF;
|
||||
} elseif ('resource' === $item[0]) {
|
||||
$formattedValue = '<em>resource</em>';
|
||||
} else {
|
||||
$formattedValue = str_replace("\n", '', var_export(self::utf8Htmlize((string) $item[1]), true));
|
||||
$formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true));
|
||||
}
|
||||
|
||||
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
|
||||
@@ -409,7 +422,7 @@ EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an UTF-8 and HTML encoded string
|
||||
* Returns an UTF-8 and HTML encoded string.
|
||||
*/
|
||||
protected static function utf8Htmlize($str)
|
||||
{
|
||||
@@ -427,6 +440,14 @@ EOF;
|
||||
return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML-encodes a string.
|
||||
*/
|
||||
private function escapeHtml($str)
|
||||
{
|
||||
return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), $this->charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,8 @@ use Symfony\Component\Debug\Exception\ClassNotFoundException;
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
use Symfony\Component\Debug\DebugClassLoader;
|
||||
use Composer\Autoload\ClassLoader as ComposerClassLoader;
|
||||
use Symfony\Component\ClassLoader as SymfonyClassLoader;
|
||||
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
|
||||
use Symfony\Component\ClassLoader\UniversalClassLoader as SymfonyUniversalClassLoader;
|
||||
|
||||
/**
|
||||
* ErrorHandler for classes that do not exist.
|
||||
@@ -76,7 +77,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
/**
|
||||
* Tries to guess the full namespace for a given class name.
|
||||
*
|
||||
* By default, it looks for PSR-0 classes registered via a Symfony or a Composer
|
||||
* By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer
|
||||
* autoloader (that should cover all common cases).
|
||||
*
|
||||
* @param string $class A class name (without its namespace)
|
||||
@@ -91,16 +92,16 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
|
||||
// find Symfony and Composer autoloaders
|
||||
$classes = array();
|
||||
|
||||
foreach ($functions as $function) {
|
||||
if (!is_array($function)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get class loaders wrapped by DebugClassLoader
|
||||
if ($function[0] instanceof DebugClassLoader) {
|
||||
$function = $function[0]->getClassLoader();
|
||||
|
||||
// Since 2.5, returning an object from DebugClassLoader::getClassLoader() is @deprecated
|
||||
// @deprecated since version 2.5. Returning an object from DebugClassLoader::getClassLoader() is deprecated.
|
||||
if (is_object($function)) {
|
||||
$function = array($function);
|
||||
}
|
||||
@@ -110,16 +111,23 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
}
|
||||
}
|
||||
|
||||
if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
|
||||
if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader || $function[0] instanceof SymfonyUniversalClassLoader) {
|
||||
foreach ($function[0]->getPrefixes() as $prefix => $paths) {
|
||||
foreach ($paths as $path) {
|
||||
$classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($function[0] instanceof ComposerClassLoader) {
|
||||
foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) {
|
||||
foreach ($paths as $path) {
|
||||
$classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
return array_unique($classes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,13 +139,13 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
*/
|
||||
private function findClassInPath($path, $class, $prefix)
|
||||
{
|
||||
if (!$path = realpath($path)) {
|
||||
if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
$filename = $class.'.php';
|
||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
||||
if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) {
|
||||
$classes[] = $class;
|
||||
}
|
||||
@@ -159,13 +167,21 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
// namespaced class
|
||||
$namespacedClass = str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file),
|
||||
// namespaced class (with target dir)
|
||||
$namespacedClassTargetDir = $prefix.str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file),
|
||||
$prefix.$namespacedClass,
|
||||
// namespaced class (with target dir and separator)
|
||||
$prefix.'\\'.$namespacedClass,
|
||||
// PEAR class
|
||||
str_replace('\\', '_', $namespacedClass),
|
||||
// PEAR class (with target dir)
|
||||
str_replace('\\', '_', $namespacedClassTargetDir),
|
||||
str_replace('\\', '_', $prefix.$namespacedClass),
|
||||
// PEAR class (with target dir and separator)
|
||||
str_replace('\\', '_', $prefix.'\\'.$namespacedClass),
|
||||
);
|
||||
|
||||
if ($prefix) {
|
||||
$candidates = array_filter($candidates, function ($candidate) use ($prefix) {return 0 === strpos($candidate, $prefix);});
|
||||
}
|
||||
|
||||
// We cannot use the autoloader here as most of them use require; but if the class
|
||||
// is not found, the new autoloader call will require the file again leading to a
|
||||
// "cannot redeclare class" error.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2014 Fabien Potencier
|
||||
Copyright (c) 2004-2015 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
|
||||
|
||||
@@ -24,7 +24,7 @@ if ('cli' !== php_sapi_name()) {
|
||||
} elseif (!ini_get('log_errors') || ini_get('error_log')) {
|
||||
ini_set('display_errors', 1);
|
||||
}
|
||||
ErrorHandler::register($errorReportingLevel);
|
||||
ErrorHandler::register();
|
||||
```
|
||||
|
||||
Note that the `Debug::enable()` call also registers the debug class loader
|
||||
@@ -39,5 +39,5 @@ Resources
|
||||
You can run the unit tests with the following command:
|
||||
|
||||
$ cd path/to/Symfony/Component/Debug/
|
||||
$ composer.phar install --dev
|
||||
$ composer install
|
||||
$ phpunit
|
||||
|
||||
@@ -61,18 +61,19 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testUnsilencing()
|
||||
{
|
||||
if (PHP_VERSION_ID >= 70000) {
|
||||
$this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.');
|
||||
}
|
||||
|
||||
ob_start();
|
||||
$bak = array(
|
||||
ini_set('log_errors', 0),
|
||||
ini_set('display_errors', 1),
|
||||
);
|
||||
|
||||
$this->iniSet('log_errors', 0);
|
||||
$this->iniSet('display_errors', 1);
|
||||
|
||||
// See below: this will fail with parse error
|
||||
// but this should not be @-silenced.
|
||||
@class_exists(__NAMESPACE__.'\TestingUnsilencing', true);
|
||||
|
||||
ini_set('log_errors', $bak[0]);
|
||||
ini_set('display_errors', $bak[1]);
|
||||
$output = ob_get_clean();
|
||||
|
||||
$this->assertStringMatchesFormat('%aParse error%a', $output);
|
||||
@@ -103,9 +104,14 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
// if an exception is thrown, the test passed
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
$this->assertEquals(E_STRICT, $exception->getSeverity());
|
||||
$this->assertStringStartsWith(__FILE__, $exception->getFile());
|
||||
$this->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage());
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
$this->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage());
|
||||
$this->assertEquals(E_STRICT, $exception->getSeverity());
|
||||
} else {
|
||||
$this->assertRegexp('/^Warning: Declaration/', $exception->getMessage());
|
||||
$this->assertEquals(E_WARNING, $exception->getSeverity());
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
|
||||
@@ -16,36 +16,13 @@ use Symfony\Component\Debug\ErrorHandler;
|
||||
use Symfony\Component\Debug\Exception\ContextErrorException;
|
||||
|
||||
/**
|
||||
* ErrorHandlerTest
|
||||
* ErrorHandlerTest.
|
||||
*
|
||||
* @author Robert Schönthal <seroscho@googlemail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var int Error reporting level before running tests.
|
||||
*/
|
||||
protected $errorReporting;
|
||||
|
||||
/**
|
||||
* @var string Display errors setting before running tests.
|
||||
*/
|
||||
protected $displayErrors;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->errorReporting = error_reporting(E_ALL | E_STRICT);
|
||||
$this->displayErrors = ini_get('display_errors');
|
||||
ini_set('display_errors', '1');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
ini_set('display_errors', $this->displayErrors);
|
||||
error_reporting($this->errorReporting);
|
||||
}
|
||||
|
||||
public function testRegister()
|
||||
{
|
||||
$handler = ErrorHandler::register();
|
||||
@@ -192,6 +169,8 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testHandleError()
|
||||
{
|
||||
$this->iniSet('error_reporting', -1);
|
||||
|
||||
try {
|
||||
$handler = ErrorHandler::register();
|
||||
$handler->throwAt(0, true);
|
||||
@@ -357,7 +336,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$logArgCheck = function ($level, $message, $context) use ($that) {
|
||||
$that->assertEquals('Fatal Parse Error: foo', $message);
|
||||
$that->assertArrayHasKey('type', $context);
|
||||
$that->assertEquals($context['type'], E_ERROR);
|
||||
$that->assertEquals($context['type'], E_PARSE);
|
||||
};
|
||||
|
||||
$logger
|
||||
@@ -366,7 +345,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
;
|
||||
|
||||
$handler->setDefaultLogger($logger, E_ERROR);
|
||||
$handler->setDefaultLogger($logger, E_PARSE);
|
||||
|
||||
$handler->handleFatalError($error);
|
||||
|
||||
@@ -380,8 +359,13 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testDeprecatedInterface()
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testLegacyInterface()
|
||||
{
|
||||
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
|
||||
|
||||
try {
|
||||
$handler = ErrorHandler::register(0);
|
||||
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
|
||||
|
||||
@@ -126,9 +126,9 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$flattened->setPrevious($flattened2);
|
||||
|
||||
$this->assertSame($flattened2,$flattened->getPrevious());
|
||||
$this->assertSame($flattened2, $flattened->getPrevious());
|
||||
|
||||
$this->assertSame(array($flattened2),$flattened->getAllPrevious());
|
||||
$this->assertSame(array($flattened2), $flattened->getAllPrevious());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
'message' => 'test',
|
||||
'class' => 'Exception',
|
||||
'trace' => array(array(
|
||||
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '', 'file' => 'foo.php', 'line' => 123,
|
||||
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123,
|
||||
'args' => array(),
|
||||
)),
|
||||
),
|
||||
@@ -186,6 +186,28 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertContains('*DEEP NESTED ARRAY*', serialize($trace));
|
||||
}
|
||||
|
||||
public function testTooBigArray()
|
||||
{
|
||||
$a = array();
|
||||
for ($i = 0; $i < 20; ++$i) {
|
||||
for ($j = 0; $j < 50; ++$j) {
|
||||
for ($k = 0; $k < 10; ++$k) {
|
||||
$a[$i][$j][$k] = 'value';
|
||||
}
|
||||
}
|
||||
}
|
||||
$a[20] = 'value';
|
||||
$a[21] = 'value1';
|
||||
$exception = $this->createException($a);
|
||||
|
||||
$flattened = FlattenException::create($exception);
|
||||
$trace = $flattened->getTrace();
|
||||
$serializeTrace = serialize($trace);
|
||||
|
||||
$this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace);
|
||||
$this->assertNotContains('*value1*', $serializeTrace);
|
||||
}
|
||||
|
||||
private function createException($foo)
|
||||
{
|
||||
return new \Exception();
|
||||
@@ -214,12 +236,12 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
'class' => 'Exception',
|
||||
'trace' => array(
|
||||
array(
|
||||
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '',
|
||||
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '',
|
||||
'file' => 'foo.php', 'line' => 123,
|
||||
'args' => array(),
|
||||
),
|
||||
array(
|
||||
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => 'test',
|
||||
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => 'test',
|
||||
'file' => __FILE__, 'line' => 123,
|
||||
'args' => array(
|
||||
array(
|
||||
|
||||
@@ -11,19 +11,56 @@
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
|
||||
|
||||
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
|
||||
use Symfony\Component\ClassLoader\UniversalClassLoader as SymfonyUniversalClassLoader;
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
|
||||
use Symfony\Component\Debug\DebugClassLoader;
|
||||
use Composer\Autoload\ClassLoader as ComposerClassLoader;
|
||||
|
||||
class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
foreach (spl_autoload_functions() as $function) {
|
||||
if (!is_array($function)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get class loaders wrapped by DebugClassLoader
|
||||
if ($function[0] instanceof DebugClassLoader) {
|
||||
$function = $function[0]->getClassLoader();
|
||||
}
|
||||
|
||||
if ($function[0] instanceof ComposerClassLoader) {
|
||||
$function[0]->add('Symfony_Component_Debug_Tests_Fixtures', dirname(dirname(dirname(dirname(dirname(__DIR__))))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideClassNotFoundData
|
||||
*/
|
||||
public function testClassNotFound($error, $translatedMessage)
|
||||
public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null)
|
||||
{
|
||||
if ($autoloader) {
|
||||
// Unregister all autoloaders to ensure the custom provided
|
||||
// autoloader is the only one to be used during the test run.
|
||||
$autoloaders = spl_autoload_functions();
|
||||
array_map('spl_autoload_unregister', $autoloaders);
|
||||
spl_autoload_register($autoloader);
|
||||
}
|
||||
|
||||
$handler = new ClassNotFoundFatalErrorHandler();
|
||||
|
||||
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
|
||||
|
||||
if ($autoloader) {
|
||||
spl_autoload_unregister($autoloader);
|
||||
array_map('spl_autoload_register', $autoloaders);
|
||||
}
|
||||
|
||||
$this->assertInstanceof('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
|
||||
$this->assertSame($translatedMessage, $exception->getMessage());
|
||||
$this->assertSame($error['type'], $exception->getSeverity());
|
||||
@@ -31,8 +68,38 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame($error['line'], $exception->getLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testLegacyHandleClassNotFound()
|
||||
{
|
||||
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);
|
||||
|
||||
$prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception'));
|
||||
$symfonyUniversalClassLoader = new SymfonyUniversalClassLoader();
|
||||
$symfonyUniversalClassLoader->registerPrefixes($prefixes);
|
||||
|
||||
$this->testHandleClassNotFound(
|
||||
array(
|
||||
'type' => 1,
|
||||
'line' => 12,
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
|
||||
),
|
||||
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
|
||||
array($symfonyUniversalClassLoader, 'loadClass')
|
||||
);
|
||||
}
|
||||
|
||||
public function provideClassNotFoundData()
|
||||
{
|
||||
$prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception'));
|
||||
|
||||
$symfonyAutoloader = new SymfonyClassLoader();
|
||||
$symfonyAutoloader->addPrefixes($prefixes);
|
||||
|
||||
$debugClassLoader = new DebugClassLoader(array($symfonyAutoloader, 'loadClass'));
|
||||
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
@@ -79,16 +146,46 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
),
|
||||
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'type' => 1,
|
||||
'line' => 12,
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
|
||||
),
|
||||
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
|
||||
array($symfonyAutoloader, 'loadClass'),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'type' => 1,
|
||||
'line' => 12,
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
|
||||
),
|
||||
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
|
||||
array($debugClassLoader, 'loadClass'),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'type' => 1,
|
||||
'line' => 12,
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
|
||||
),
|
||||
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
|
||||
function ($className) { /* do nothing here */ },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testCannotRedeclareClass()
|
||||
{
|
||||
if (!file_exists(__DIR__.'/../FIXTURES/REQUIREDTWICE.PHP')) {
|
||||
if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) {
|
||||
$this->markTestSkipped('Can only be run on case insensitive filesystems');
|
||||
}
|
||||
|
||||
require_once __DIR__.'/../FIXTURES/REQUIREDTWICE.PHP';
|
||||
require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP';
|
||||
|
||||
$error = array(
|
||||
'type' => 1,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
class RequiredTwice
|
||||
{
|
||||
}
|
||||
7
Tests/Fixtures2/RequiredTwice.php
Normal file
7
Tests/Fixtures2/RequiredTwice.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures2;
|
||||
|
||||
class RequiredTwice
|
||||
{
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"type": "library",
|
||||
"description": "Symfony Debug Component",
|
||||
"keywords": [],
|
||||
"homepage": "http://symfony.com",
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
@@ -12,15 +12,20 @@
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/http-kernel": "~2.1",
|
||||
"symfony/phpunit-bridge": "~2.7",
|
||||
"symfony/class-loader": "~2.2",
|
||||
"symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2",
|
||||
"symfony/http-foundation": "~2.1"
|
||||
},
|
||||
"suggest": {
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
<testsuites>
|
||||
<testsuite name="Symfony Debug Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
|
||||
Reference in New Issue
Block a user