mirror of
https://github.com/symfony/debug.git
synced 2026-03-25 17:52:07 +01:00
Compare commits
22 Commits
2.4
...
v2.5.0-BET
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eec1e0a1f | ||
|
|
efb365b644 | ||
|
|
3d839f44bd | ||
|
|
cff0a2e884 | ||
|
|
8b0839bb30 | ||
|
|
d0dc588a46 | ||
|
|
34a8ad8509 | ||
|
|
57ad6f754e | ||
|
|
cb1965864a | ||
|
|
429b394c16 | ||
|
|
a2c111a569 | ||
|
|
c3cfefd38b | ||
|
|
ffad875704 | ||
|
|
ac63c60169 | ||
|
|
5d95110638 | ||
|
|
dca6697bea | ||
|
|
a1dd7dc6d7 | ||
|
|
9566ace63d | ||
|
|
0000f11398 | ||
|
|
b7682104fa | ||
|
|
9e718c0517 | ||
|
|
598ba50d67 |
@@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.5.0
|
||||
-----
|
||||
|
||||
* added UndefinedMethodFatalErrorHandler
|
||||
|
||||
2.4.0
|
||||
-----
|
||||
|
||||
|
||||
@@ -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 integer $errorReportingLevel The level of error reporting you want
|
||||
* @param Boolean $displayErrors Whether to display errors (for development) or just log them (for production)
|
||||
*/
|
||||
public static function enable($errorReportingLevel = null, $displayErrors = true)
|
||||
{
|
||||
|
||||
@@ -14,46 +14,67 @@ namespace Symfony\Component\Debug;
|
||||
/**
|
||||
* Autoloader checking if the class is really defined in the file found.
|
||||
*
|
||||
* The ClassLoader will wrap all registered autoloaders providing a
|
||||
* findFile method and will throw an exception if a file is found but does
|
||||
* The ClassLoader will wrap all registered autoloaders
|
||||
* and will throw an exception if a file is found but does
|
||||
* not declare the class.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christophe Coevoet <stof@notk.org>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class DebugClassLoader
|
||||
{
|
||||
private $classFinder;
|
||||
private $classLoader;
|
||||
private $isFinder;
|
||||
private $wasFinder;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param object $classFinder
|
||||
* @param callable|object $classLoader
|
||||
*
|
||||
* @api
|
||||
* @deprecated since 2.5, passing an object is deprecated and support for it will be removed in 3.0
|
||||
*/
|
||||
public function __construct($classFinder)
|
||||
public function __construct($classLoader)
|
||||
{
|
||||
$this->classFinder = $classFinder;
|
||||
$this->wasFinder = is_object($classLoader) && method_exists($classLoader, 'findFile');
|
||||
|
||||
if ($this->wasFinder) {
|
||||
$this->classLoader = array($classLoader, 'loadClass');
|
||||
$this->isFinder = true;
|
||||
} else {
|
||||
$this->classLoader = $classLoader;
|
||||
$this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the wrapped class loader.
|
||||
*
|
||||
* @return object a class loader instance
|
||||
* @return callable|object a class loader
|
||||
*
|
||||
* @deprecated since 2.5, returning an object is deprecated and support for it will be removed in 3.0
|
||||
*/
|
||||
public function getClassLoader()
|
||||
{
|
||||
return $this->classFinder;
|
||||
if ($this->wasFinder) {
|
||||
return $this->classLoader[0];
|
||||
} else {
|
||||
return $this->classLoader;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all autoloaders implementing a findFile method by a DebugClassLoader wrapper.
|
||||
* Wraps all autoloaders
|
||||
*/
|
||||
public static function enable()
|
||||
{
|
||||
// Ensures we don't hit https://bugs.php.net/42098
|
||||
class_exists(__NAMESPACE__.'\ErrorHandler', true);
|
||||
|
||||
if (!is_array($functions = spl_autoload_functions())) {
|
||||
return;
|
||||
}
|
||||
@@ -63,8 +84,8 @@ class DebugClassLoader
|
||||
}
|
||||
|
||||
foreach ($functions as $function) {
|
||||
if (is_array($function) && !$function[0] instanceof self && method_exists($function[0], 'findFile')) {
|
||||
$function = array(new static($function[0]), 'loadClass');
|
||||
if (!is_array($function) || !$function[0] instanceof self) {
|
||||
$function = array(new static($function), 'loadClass');
|
||||
}
|
||||
|
||||
spl_autoload_register($function);
|
||||
@@ -86,7 +107,7 @@ class DebugClassLoader
|
||||
|
||||
foreach ($functions as $function) {
|
||||
if (is_array($function) && $function[0] instanceof self) {
|
||||
$function[0] = $function[0]->getClassLoader();
|
||||
$function = $function[0]->getClassLoader();
|
||||
}
|
||||
|
||||
spl_autoload_register($function);
|
||||
@@ -99,10 +120,14 @@ class DebugClassLoader
|
||||
* @param string $class A class name to resolve to file
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @deprecated Deprecated since 2.5, to be removed in 3.0.
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
return $this->classFinder->findFile($class);
|
||||
if ($this->wasFinder) {
|
||||
return $this->classLoader[0]->findFile($class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,16 +135,73 @@ class DebugClassLoader
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return bool|null True, if loaded
|
||||
* @return Boolean|null True, if loaded
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->classFinder->findFile($class)) {
|
||||
require $file;
|
||||
ErrorHandler::stackErrors();
|
||||
|
||||
if (!class_exists($class, false) && !interface_exists($class, false) && (!function_exists('trait_exists') || !trait_exists($class, false))) {
|
||||
try {
|
||||
if ($this->isFinder) {
|
||||
if ($file = $this->classLoader[0]->findFile($class)) {
|
||||
require $file;
|
||||
}
|
||||
} else {
|
||||
call_user_func($this->classLoader, $class);
|
||||
$file = false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
ErrorHandler::unstackErrors();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
ErrorHandler::unstackErrors();
|
||||
|
||||
$exists = class_exists($class, false) || interface_exists($class, false) || (function_exists('trait_exists') && trait_exists($class, false));
|
||||
|
||||
if ($exists) {
|
||||
$name = new \ReflectionClass($class);
|
||||
$name = $name->getName();
|
||||
|
||||
if ($name !== $class) {
|
||||
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name));
|
||||
}
|
||||
}
|
||||
|
||||
if ($file) {
|
||||
if ('\\' == $class[0]) {
|
||||
$class = substr($class, 1);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$tail = DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
|
||||
$len = strlen($tail);
|
||||
|
||||
do {
|
||||
$tail = substr($tail, $i);
|
||||
$len -= $i;
|
||||
|
||||
if (0 === substr_compare($file, $tail, -$len, $len, true)) {
|
||||
if (0 !== substr_compare($file, $tail, -$len, $len, false)) {
|
||||
if (method_exists($this->classLoader[0], 'getClassMap')) {
|
||||
$map = $this->classLoader[0]->getClassMap();
|
||||
} else {
|
||||
$map = array();
|
||||
}
|
||||
|
||||
if (! isset($map[$class])) {
|
||||
throw new \RuntimeException(sprintf('Case mismatch between class and source file names: %s vs %s', $class, $file));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} while (false !== $i = strpos($tail, DIRECTORY_SEPARATOR, 1));
|
||||
|
||||
if (! $exists) {
|
||||
if (false !== strpos($class, '/')) {
|
||||
throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class));
|
||||
}
|
||||
|
||||
169
ErrorHandler.php
169
ErrorHandler.php
@@ -11,11 +11,13 @@
|
||||
|
||||
namespace Symfony\Component\Debug;
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Debug\Exception\ContextErrorException;
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
use Symfony\Component\Debug\Exception\DummyException;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
|
||||
|
||||
@@ -24,6 +26,7 @@ use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Konstantin Myakshin <koc-dp@yandex.ru>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ErrorHandler
|
||||
{
|
||||
@@ -42,7 +45,7 @@ class ErrorHandler
|
||||
E_ERROR => 'Error',
|
||||
E_CORE_ERROR => 'Core Error',
|
||||
E_COMPILE_ERROR => 'Compile Error',
|
||||
E_PARSE => 'Parse',
|
||||
E_PARSE => 'Parse Error',
|
||||
);
|
||||
|
||||
private $level;
|
||||
@@ -56,11 +59,15 @@ class ErrorHandler
|
||||
*/
|
||||
private static $loggers = array();
|
||||
|
||||
private static $stackedErrors = array();
|
||||
|
||||
private static $stackedErrorLevels = array();
|
||||
|
||||
/**
|
||||
* Registers the error handler.
|
||||
*
|
||||
* @param int $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
|
||||
* @param bool $displayErrors Display errors (for dev environment) or just log them (production usage)
|
||||
* @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
|
||||
* @param Boolean $displayErrors Display errors (for dev environment) or just log them (production usage)
|
||||
*
|
||||
* @return ErrorHandler The registered error handler
|
||||
*/
|
||||
@@ -81,7 +88,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 integer|null $level The level (null to use the error_reporting() value and 0 to disable)
|
||||
*/
|
||||
public function setLevel($level)
|
||||
{
|
||||
@@ -91,7 +98,7 @@ class ErrorHandler
|
||||
/**
|
||||
* Sets the display_errors flag value.
|
||||
*
|
||||
* @param int $displayErrors The display_errors flag value
|
||||
* @param integer $displayErrors The display_errors flag value
|
||||
*/
|
||||
public function setDisplayErrors($displayErrors)
|
||||
{
|
||||
@@ -102,7 +109,7 @@ class ErrorHandler
|
||||
* Sets a logger for the given channel.
|
||||
*
|
||||
* @param LoggerInterface $logger A logger interface
|
||||
* @param string $channel The channel associated with the logger (deprecation or emergency)
|
||||
* @param string $channel The channel associated with the logger (deprecation, emergency or scream)
|
||||
*/
|
||||
public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
|
||||
{
|
||||
@@ -114,58 +121,48 @@ class ErrorHandler
|
||||
*/
|
||||
public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
|
||||
{
|
||||
if (0 === $this->level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) {
|
||||
if (isset(self::$loggers['deprecation'])) {
|
||||
if (version_compare(PHP_VERSION, '5.4', '<')) {
|
||||
$stack = array_map(
|
||||
function ($row) {
|
||||
unset($row['args']);
|
||||
|
||||
return $row;
|
||||
},
|
||||
array_slice(debug_backtrace(false), 0, 10)
|
||||
);
|
||||
if (self::$stackedErrorLevels) {
|
||||
self::$stackedErrors[] = func_get_args();
|
||||
} else {
|
||||
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
|
||||
}
|
||||
if (version_compare(PHP_VERSION, '5.4', '<')) {
|
||||
$stack = array_map(
|
||||
function ($row) {
|
||||
unset($row['args']);
|
||||
|
||||
self::$loggers['deprecation']->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack));
|
||||
return $row;
|
||||
},
|
||||
array_slice(debug_backtrace(false), 0, 10)
|
||||
);
|
||||
} else {
|
||||
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
|
||||
}
|
||||
|
||||
self::$loggers['deprecation']->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->displayErrors && error_reporting() & $level && $this->level & $level) {
|
||||
// make sure the ContextErrorException class is loaded (https://bugs.php.net/bug.php?id=65322)
|
||||
if (!class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
|
||||
require __DIR__.'/Exception/ContextErrorException.php';
|
||||
}
|
||||
if (!class_exists('Symfony\Component\Debug\Exception\FlattenException')) {
|
||||
require __DIR__.'/Exception/FlattenException.php';
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && is_array($context)) {
|
||||
unset($context['GLOBALS']);
|
||||
}
|
||||
|
||||
$exception = new ContextErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line, $context);
|
||||
|
||||
// Exceptions thrown from error handlers are sometimes not caught by the exception
|
||||
// handler, so we invoke it directly (https://bugs.php.net/bug.php?id=54275)
|
||||
$exceptionHandler = set_exception_handler(function () {});
|
||||
$exceptionHandler = set_exception_handler('var_dump');
|
||||
restore_exception_handler();
|
||||
|
||||
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {
|
||||
$exceptionHandler[0]->handle($exception);
|
||||
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandlerInterface) {
|
||||
if (self::$stackedErrorLevels) {
|
||||
self::$stackedErrors[] = func_get_args();
|
||||
|
||||
if (!class_exists('Symfony\Component\Debug\Exception\DummyException')) {
|
||||
require __DIR__.'/Exception/DummyException.php';
|
||||
return true;
|
||||
}
|
||||
|
||||
$exception = sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line);
|
||||
$exception = new ContextErrorException($exception, 0, $level, $file, $line, $context);
|
||||
$exceptionHandler[0]->handle($exception);
|
||||
|
||||
// we must stop the PHP script execution, as the exception has
|
||||
// already been dealt with, so, let's throw an exception that
|
||||
// will be caught by a dummy exception handler
|
||||
@@ -182,16 +179,93 @@ class ErrorHandler
|
||||
}
|
||||
}
|
||||
|
||||
if (isset(self::$loggers['scream']) && !(error_reporting() & $level)) {
|
||||
if (self::$stackedErrorLevels) {
|
||||
self::$stackedErrors[] = func_get_args();
|
||||
} else {
|
||||
switch ($level) {
|
||||
case E_USER_ERROR:
|
||||
case E_RECOVERABLE_ERROR:
|
||||
$logLevel = LogLevel::ERROR;
|
||||
break;
|
||||
|
||||
case E_WARNING:
|
||||
case E_USER_WARNING:
|
||||
$logLevel = LogLevel::WARNING;
|
||||
break;
|
||||
|
||||
default:
|
||||
$logLevel = LogLevel::NOTICE;
|
||||
break;
|
||||
}
|
||||
|
||||
self::$loggers['scream']->log($logLevel, $message, array(
|
||||
'type' => $level,
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
'scream' => error_reporting(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the error handler for delayed handling.
|
||||
* Ensures also that non-catchable fatal errors are never silenced.
|
||||
*
|
||||
* As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724
|
||||
* PHP has a compile stage where it behaves unusually. To workaround it,
|
||||
* we plug an error handler that only stacks errors for later.
|
||||
*
|
||||
* The most important feature of this is to prevent
|
||||
* autoloading until unstackErrors() is called.
|
||||
*/
|
||||
public static function stackErrors()
|
||||
{
|
||||
self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unstacks stacked errors and forwards to the regular handler
|
||||
*/
|
||||
public static function unstackErrors()
|
||||
{
|
||||
$level = array_pop(self::$stackedErrorLevels);
|
||||
|
||||
if (null !== $level) {
|
||||
error_reporting($level);
|
||||
}
|
||||
|
||||
if (empty(self::$stackedErrorLevels)) {
|
||||
$errors = self::$stackedErrors;
|
||||
self::$stackedErrors = array();
|
||||
|
||||
$errorHandler = set_error_handler('var_dump');
|
||||
restore_error_handler();
|
||||
|
||||
if ($errorHandler) {
|
||||
foreach ($errors as $e) {
|
||||
call_user_func_array($errorHandler, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function handleFatal()
|
||||
{
|
||||
if (null === $error = error_get_last()) {
|
||||
$this->reservedMemory = '';
|
||||
$error = error_get_last();
|
||||
|
||||
while (self::$stackedErrorLevels) {
|
||||
static::unstackErrors();
|
||||
}
|
||||
|
||||
if (null === $error) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->reservedMemory = '';
|
||||
$type = $error['type'];
|
||||
if (0 === $this->level || !in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
|
||||
return;
|
||||
@@ -204,7 +278,7 @@ class ErrorHandler
|
||||
'line' => $error['line'],
|
||||
);
|
||||
|
||||
self::$loggers['emergency']->emerg($error['message'], $fatal);
|
||||
self::$loggers['emergency']->emergency($error['message'], $fatal);
|
||||
}
|
||||
|
||||
if (!$this->displayErrors) {
|
||||
@@ -212,10 +286,10 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
// get current exception handler
|
||||
$exceptionHandler = set_exception_handler(function () {});
|
||||
$exceptionHandler = set_exception_handler('var_dump');
|
||||
restore_exception_handler();
|
||||
|
||||
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {
|
||||
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandlerInterface) {
|
||||
$this->handleFatalError($exceptionHandler[0], $error);
|
||||
}
|
||||
}
|
||||
@@ -231,11 +305,12 @@ class ErrorHandler
|
||||
{
|
||||
return array(
|
||||
new UndefinedFunctionFatalErrorHandler(),
|
||||
new UndefinedMethodFatalErrorHandler(),
|
||||
new ClassNotFoundFatalErrorHandler(),
|
||||
);
|
||||
}
|
||||
|
||||
private function handleFatalError(ExceptionHandler $exceptionHandler, array $error)
|
||||
private function handleFatalError(ExceptionHandlerInterface $exceptionHandler, array $error)
|
||||
{
|
||||
$level = isset($this->levels[$error['type']]) ? $this->levels[$error['type']] : $error['type'];
|
||||
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
|
||||
|
||||
25
Exception/UndefinedMethodException.php
Normal file
25
Exception/UndefinedMethodException.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Undefined Method Exception.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
class UndefinedMethodException extends FatalErrorException
|
||||
{
|
||||
public function __construct($message, \ErrorException $previous)
|
||||
{
|
||||
parent::__construct($message, $previous->getCode(), $previous->getSeverity(), $previous->getFile(), $previous->getLine(), $previous->getPrevious());
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ if (!defined('ENT_SUBSTITUTE')) {
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ExceptionHandler
|
||||
class ExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
private $debug;
|
||||
private $charset;
|
||||
@@ -43,7 +43,7 @@ class ExceptionHandler
|
||||
/**
|
||||
* Registers the exception handler.
|
||||
*
|
||||
* @param bool $debug
|
||||
* @param Boolean $debug
|
||||
*
|
||||
* @return ExceptionHandler The registered exception handler
|
||||
*/
|
||||
@@ -57,14 +57,14 @@ class ExceptionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
27
ExceptionHandlerInterface.php
Normal file
27
ExceptionHandlerInterface.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* An ExceptionHandler does something useful with an exception.
|
||||
*
|
||||
* @author Andrew Moore <me@andrewmoore.ca>
|
||||
*/
|
||||
interface ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Handles an exception.
|
||||
*
|
||||
* @param \Exception $exception An \Exception instance
|
||||
*/
|
||||
public function handle(\Exception $exception);
|
||||
}
|
||||
@@ -103,14 +103,23 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
}
|
||||
|
||||
// get class loaders wrapped by DebugClassLoader
|
||||
if ($function[0] instanceof DebugClassLoader && method_exists($function[0], 'getClassLoader')) {
|
||||
$function[0] = $function[0]->getClassLoader();
|
||||
if ($function[0] instanceof DebugClassLoader) {
|
||||
$function = $function[0]->getClassLoader();
|
||||
|
||||
// Since 2.5, returning an object from DebugClassLoader::getClassLoader() is @deprecated
|
||||
if (is_object($function)) {
|
||||
$function = array($function);
|
||||
}
|
||||
|
||||
if (!is_array($function)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
|
||||
foreach ($function[0]->getPrefixes() as $prefix => $paths) {
|
||||
foreach ($function[0]->getPrefixes() as $paths) {
|
||||
foreach ($paths as $path) {
|
||||
$classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
|
||||
$classes = array_merge($classes, $this->findClassInPath($path, $class));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,11 +131,10 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $class
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function findClassInPath($path, $class, $prefix)
|
||||
private function findClassInPath($path, $class)
|
||||
{
|
||||
if (!$path = realpath($path)) {
|
||||
return array();
|
||||
@@ -135,7 +143,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
$classes = array();
|
||||
$filename = $class.'.php';
|
||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
||||
if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) {
|
||||
if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName())) {
|
||||
$classes[] = $class;
|
||||
}
|
||||
}
|
||||
@@ -146,45 +154,34 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $file
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function convertFileToClass($path, $file, $prefix)
|
||||
private function convertFileToClass($path, $file)
|
||||
{
|
||||
$candidates = array(
|
||||
// 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),
|
||||
// PEAR class
|
||||
str_replace('\\', '_', $namespacedClass),
|
||||
// PEAR class (with target dir)
|
||||
str_replace('\\', '_', $namespacedClassTargetDir),
|
||||
);
|
||||
$namespacedClass = str_replace(array($path.'/', '.php', '/'), array('', '', '\\'), $file);
|
||||
$pearClass = str_replace('\\', '_', $namespacedClass);
|
||||
|
||||
// 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.
|
||||
foreach ($candidates as $candidate) {
|
||||
if ($this->classExists($candidate)) {
|
||||
return $candidate;
|
||||
}
|
||||
if (!$this->classExists($namespacedClass) && !$this->classExists($pearClass)) {
|
||||
require_once $file;
|
||||
}
|
||||
|
||||
require_once $file;
|
||||
if ($this->classExists($namespacedClass)) {
|
||||
return $namespacedClass;
|
||||
}
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
if ($this->classExists($candidate)) {
|
||||
return $candidate;
|
||||
}
|
||||
if ($this->classExists($pearClass)) {
|
||||
return $pearClass;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
*
|
||||
* @return bool
|
||||
* @return Boolean
|
||||
*/
|
||||
private function classExists($class)
|
||||
{
|
||||
|
||||
@@ -80,6 +80,7 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
}
|
||||
|
||||
if ($candidates) {
|
||||
sort($candidates);
|
||||
$message .= ' Did you mean to call: '.implode(', ', array_map(function ($val) {
|
||||
return '"'.$val.'"';
|
||||
}, $candidates)).'?';
|
||||
|
||||
54
FatalErrorHandler/UndefinedMethodFatalErrorHandler.php
Normal file
54
FatalErrorHandler/UndefinedMethodFatalErrorHandler.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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\FatalErrorHandler;
|
||||
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
use Symfony\Component\Debug\Exception\UndefinedMethodException;
|
||||
|
||||
/**
|
||||
* ErrorHandler for undefined methods.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handleError(array $error, FatalErrorException $exception)
|
||||
{
|
||||
preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches);
|
||||
if (!$matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
$className = $matches[1];
|
||||
$methodName = $matches[2];
|
||||
|
||||
$message = sprintf('Attempted to call method "%s" on class "%s" in %s line %d.', $methodName, $className, $error['file'], $error['line']);
|
||||
|
||||
$candidates = array();
|
||||
foreach (get_class_methods($className) as $definedMethodName) {
|
||||
$lev = levenshtein($methodName, $definedMethodName);
|
||||
if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) {
|
||||
$candidates[] = $definedMethodName;
|
||||
}
|
||||
}
|
||||
|
||||
if ($candidates) {
|
||||
sort($candidates);
|
||||
$message .= sprintf(' Did you mean to call: "%s"?', implode('", "', $candidates));
|
||||
}
|
||||
|
||||
return new UndefinedMethodException($message, $exception);
|
||||
}
|
||||
}
|
||||
@@ -12,46 +12,152 @@
|
||||
namespace Symfony\Component\Debug\Tests;
|
||||
|
||||
use Symfony\Component\Debug\DebugClassLoader;
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
|
||||
class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var int Error reporting level before running tests.
|
||||
*/
|
||||
private $errorReporting;
|
||||
|
||||
private $loader;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->errorReporting = error_reporting(E_ALL | E_STRICT);
|
||||
$this->loader = new ClassLoader();
|
||||
spl_autoload_register(array($this->loader, 'loadClass'));
|
||||
DebugClassLoader::enable();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
DebugClassLoader::disable();
|
||||
spl_autoload_unregister(array($this->loader, 'loadClass'));
|
||||
error_reporting($this->errorReporting);
|
||||
}
|
||||
|
||||
public function testIdempotence()
|
||||
{
|
||||
DebugClassLoader::enable();
|
||||
DebugClassLoader::enable();
|
||||
|
||||
$functions = spl_autoload_functions();
|
||||
foreach ($functions as $function) {
|
||||
if (is_array($function) && $function[0] instanceof DebugClassLoader) {
|
||||
$reflClass = new \ReflectionClass($function[0]);
|
||||
$reflProp = $reflClass->getProperty('classFinder');
|
||||
$reflProp = $reflClass->getProperty('classLoader');
|
||||
$reflProp->setAccessible(true);
|
||||
|
||||
$this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0]));
|
||||
|
||||
DebugClassLoader::disable();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DebugClassLoader::disable();
|
||||
|
||||
$this->fail('DebugClassLoader did not register');
|
||||
}
|
||||
|
||||
public function testUnsilencing()
|
||||
{
|
||||
ob_start();
|
||||
$bak = array(
|
||||
ini_set('log_errors', 0),
|
||||
ini_set('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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Debug\Exception\DummyException
|
||||
*/
|
||||
public function testStacking()
|
||||
{
|
||||
// the ContextErrorException must not be loaded to test the workaround
|
||||
// for https://bugs.php.net/65322.
|
||||
if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) {
|
||||
$this->markTestSkipped('The ContextErrorException class is already loaded.');
|
||||
}
|
||||
|
||||
$exceptionHandler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('handle'));
|
||||
set_exception_handler(array($exceptionHandler, 'handle'));
|
||||
|
||||
$that = $this;
|
||||
$exceptionCheck = function ($exception) use ($that) {
|
||||
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
|
||||
$that->assertEquals(E_STRICT, $exception->getSeverity());
|
||||
$that->assertStringStartsWith(__FILE__, $exception->getFile());
|
||||
$that->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage());
|
||||
};
|
||||
|
||||
$exceptionHandler->expects($this->once())
|
||||
->method('handle')
|
||||
->will($this->returnCallback($exceptionCheck));
|
||||
ErrorHandler::register();
|
||||
|
||||
try {
|
||||
// Trigger autoloading + E_STRICT at compile time
|
||||
// which in turn triggers $errorHandler->handle()
|
||||
// that again triggers autoloading for ContextErrorException.
|
||||
// Error stacking works around the bug above and everything is fine.
|
||||
|
||||
eval('
|
||||
namespace '.__NAMESPACE__.';
|
||||
class ChildTestingStacking extends TestingStacking { function foo($bar) {} }
|
||||
');
|
||||
} catch (\Exception $e) {
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testNameCaseMismatch()
|
||||
{
|
||||
class_exists(__NAMESPACE__.'\TestingCaseMismatch', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testFileCaseMismatch()
|
||||
{
|
||||
class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testPsr4CaseMismatch()
|
||||
{
|
||||
class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true);
|
||||
}
|
||||
|
||||
public function testNotPsr0()
|
||||
{
|
||||
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true));
|
||||
}
|
||||
|
||||
public function testNotPsr0Bis()
|
||||
{
|
||||
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true));
|
||||
}
|
||||
}
|
||||
|
||||
class ClassLoader
|
||||
@@ -60,7 +166,27 @@ class ClassLoader
|
||||
{
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__ . '/Fixtures/notPsr0Bis.php');
|
||||
}
|
||||
|
||||
public function findFile($class)
|
||||
{
|
||||
if (__NAMESPACE__.'\TestingUnsilencing' === $class) {
|
||||
eval('-- parse error --');
|
||||
} elseif (__NAMESPACE__.'\TestingStacking' === $class) {
|
||||
eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }');
|
||||
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
|
||||
eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
|
||||
} elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) {
|
||||
return __DIR__ . '/Fixtures/casemismatch.php';
|
||||
} elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
|
||||
return __DIR__ . '/Fixtures/psr4/Psr4CaseMismatch.php';
|
||||
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
|
||||
return __DIR__ . '/Fixtures/reallyNotPsr0.php';
|
||||
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
|
||||
return __DIR__ . '/Fixtures/notPsr0Bis.php';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,65 +44,6 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
error_reporting($this->errorReporting);
|
||||
}
|
||||
|
||||
public function testCompileTimeError()
|
||||
{
|
||||
// the ContextErrorException must not be loaded to test the workaround
|
||||
// for https://bugs.php.net/bug.php?id=65322.
|
||||
if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) {
|
||||
$this->markTestSkipped('The ContextErrorException class is already loaded.');
|
||||
}
|
||||
|
||||
$exceptionHandler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('handle'));
|
||||
|
||||
// the following code forces some PHPUnit classes to be loaded
|
||||
// so that they will be available in the exception handler
|
||||
// as they won't be autoloaded by PHP
|
||||
class_exists('PHPUnit_Framework_MockObject_Invocation_Object');
|
||||
$this->assertInstanceOf('stdClass', new \stdClass());
|
||||
$this->assertEquals(1, 1);
|
||||
$this->assertStringStartsWith('foo', 'foobar');
|
||||
$this->assertArrayHasKey('bar', array('bar' => 'foo'));
|
||||
|
||||
$that = $this;
|
||||
$exceptionCheck = function ($exception) use ($that) {
|
||||
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
|
||||
$that->assertEquals(E_STRICT, $exception->getSeverity());
|
||||
$that->assertEquals(2, $exception->getLine());
|
||||
$that->assertStringStartsWith('Runtime Notice: Declaration of _CompileTimeError::foo() should be compatible with', $exception->getMessage());
|
||||
$that->assertArrayHasKey('bar', $exception->getContext());
|
||||
};
|
||||
|
||||
$exceptionHandler->expects($this->once())
|
||||
->method('handle')
|
||||
->will($this->returnCallback($exceptionCheck))
|
||||
;
|
||||
|
||||
ErrorHandler::register();
|
||||
set_exception_handler(array($exceptionHandler, 'handle'));
|
||||
|
||||
// dummy variable to check for in error handler.
|
||||
$bar = 123;
|
||||
|
||||
// trigger compile time error
|
||||
try {
|
||||
eval(<<<'PHP'
|
||||
class _BaseCompileTimeError { function foo() {} }
|
||||
class _CompileTimeError extends _BaseCompileTimeError { function foo($invalid) {} }
|
||||
PHP
|
||||
);
|
||||
} catch (DummyException $e) {
|
||||
// if an exception is thrown, the test passed
|
||||
} catch (\Exception $e) {
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
}
|
||||
|
||||
public function testNotice()
|
||||
{
|
||||
$exceptionHandler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('handle'));
|
||||
@@ -112,7 +53,6 @@ PHP
|
||||
$exceptionCheck = function ($exception) use ($that) {
|
||||
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
|
||||
$that->assertEquals(E_NOTICE, $exception->getSeverity());
|
||||
$that->assertEquals(__LINE__ + 44, $exception->getLine());
|
||||
$that->assertEquals(__FILE__, $exception->getFile());
|
||||
$that->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
|
||||
$that->assertArrayHasKey('foobar', $exception->getContext());
|
||||
@@ -181,18 +121,18 @@ PHP
|
||||
{
|
||||
try {
|
||||
$handler = ErrorHandler::register(0);
|
||||
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
|
||||
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, 'foo'));
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
$handler = ErrorHandler::register(3);
|
||||
$this->assertFalse($handler->handle(4, 'foo', 'foo.php', 12, array()));
|
||||
$this->assertFalse($handler->handle(4, 'foo', 'foo.php', 12, 'foo'));
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
$handler = ErrorHandler::register(3);
|
||||
try {
|
||||
$handler->handle(111, 'foo', 'foo.php', 12, array());
|
||||
$handler->handle(111, 'foo', 'foo.php', 12, 'foo');
|
||||
} catch (\ErrorException $e) {
|
||||
$this->assertSame('111: foo in foo.php line 12', $e->getMessage());
|
||||
$this->assertSame(111, $e->getSeverity());
|
||||
@@ -203,12 +143,12 @@ PHP
|
||||
restore_error_handler();
|
||||
|
||||
$handler = ErrorHandler::register(E_USER_DEPRECATED);
|
||||
$this->assertTrue($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
|
||||
$this->assertTrue($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, 'foo'));
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
$handler = ErrorHandler::register(E_DEPRECATED);
|
||||
$this->assertTrue($handler->handle(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
|
||||
$this->assertTrue($handler->handle(E_DEPRECATED, 'foo', 'foo.php', 12, 'foo'));
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
@@ -231,7 +171,29 @@ PHP
|
||||
|
||||
$handler = ErrorHandler::register(E_USER_DEPRECATED);
|
||||
$handler->setLogger($logger);
|
||||
$handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array());
|
||||
$handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, 'foo');
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
$logger = $this->getMock('Psr\Log\LoggerInterface');
|
||||
|
||||
$that = $this;
|
||||
$logArgCheck = function ($level, $message, $context) use ($that) {
|
||||
$that->assertEquals('Undefined variable: undefVar', $message);
|
||||
$that->assertArrayHasKey('type', $context);
|
||||
$that->assertEquals($context['type'], E_NOTICE);
|
||||
};
|
||||
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('log')
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
;
|
||||
|
||||
$handler = ErrorHandler::register(E_NOTICE);
|
||||
$handler->setLogger($logger, 'scream');
|
||||
unset($undefVar);
|
||||
@$undefVar++;
|
||||
|
||||
restore_error_handler();
|
||||
} catch (\Exception $e) {
|
||||
@@ -254,7 +216,8 @@ PHP
|
||||
$m->invoke($handler, $exceptionHandler, $error);
|
||||
|
||||
$this->assertInstanceof($class, $exceptionHandler->e);
|
||||
$this->assertSame($translatedMessage, $exceptionHandler->e->getMessage());
|
||||
// class names are case insensitive and PHP/HHVM do not return the same
|
||||
$this->assertSame(strtolower($translatedMessage), strtolower($exceptionHandler->e->getMessage()));
|
||||
$this->assertSame($error['type'], $exceptionHandler->e->getSeverity());
|
||||
$this->assertSame($error['file'], $exceptionHandler->e->getFile());
|
||||
$this->assertSame($error['line'], $exceptionHandler->e->getLine());
|
||||
|
||||
@@ -114,6 +114,7 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.');
|
||||
$this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.');
|
||||
$this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,13 +160,13 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertEquals(array(
|
||||
array(
|
||||
'message' => 'test',
|
||||
'class' => 'Exception',
|
||||
'trace' => array(array(
|
||||
'message'=> 'test',
|
||||
'class'=>'Exception',
|
||||
'trace'=>array(array(
|
||||
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '', 'file' => 'foo.php', 'line' => 123,
|
||||
'args' => array(),
|
||||
'args' => array()
|
||||
)),
|
||||
),
|
||||
)
|
||||
), $flattened->toArray());
|
||||
}
|
||||
|
||||
@@ -201,7 +202,7 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
'line' => 123,
|
||||
'function' => 'test',
|
||||
'args' => array(
|
||||
unserialize('O:14:"BogusTestClass":0:{}'),
|
||||
unserialize('O:14:"BogusTestClass":0:{}')
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -210,9 +211,9 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertEquals(array(
|
||||
array(
|
||||
'message' => 'test',
|
||||
'class' => 'Exception',
|
||||
'trace' => array(
|
||||
'message'=> 'test',
|
||||
'class'=>'Exception',
|
||||
'trace'=>array(
|
||||
array(
|
||||
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '',
|
||||
'file' => 'foo.php', 'line' => 123,
|
||||
@@ -223,12 +224,12 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
'file' => __FILE__, 'line' => 123,
|
||||
'args' => array(
|
||||
array(
|
||||
'incomplete-object', 'BogusTestClass',
|
||||
'incomplete-object', 'BogusTestClass'
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
), $flattened->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
|
||||
|
||||
$this->assertInstanceof('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception);
|
||||
$this->assertSame($translatedMessage, $exception->getMessage());
|
||||
// class names are case insensitive and PHP/HHVM do not return the same
|
||||
$this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage()));
|
||||
$this->assertSame($error['type'], $exception->getSeverity());
|
||||
$this->assertSame($error['file'], $exception->getFile());
|
||||
$this->assertSame($error['line'], $exception->getLine());
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?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\Tests\FatalErrorHandler;
|
||||
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
||||
|
||||
class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideUndefinedMethodData
|
||||
*/
|
||||
public function testUndefinedMethod($error, $translatedMessage)
|
||||
{
|
||||
$handler = new UndefinedMethodFatalErrorHandler();
|
||||
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
|
||||
|
||||
$this->assertInstanceof('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception);
|
||||
$this->assertSame($translatedMessage, $exception->getMessage());
|
||||
$this->assertSame($error['type'], $exception->getSeverity());
|
||||
$this->assertSame($error['file'], $exception->getFile());
|
||||
$this->assertSame($error['line'], $exception->getLine());
|
||||
}
|
||||
|
||||
public function provideUndefinedMethodData()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
'type' => 1,
|
||||
'line' => 12,
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Call to undefined method SplObjectStorage::what()',
|
||||
),
|
||||
'Attempted to call method "what" on class "SplObjectStorage" in foo.php line 12.',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'type' => 1,
|
||||
'line' => 12,
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Call to undefined method SplObjectStorage::walid()',
|
||||
),
|
||||
'Attempted to call method "walid" on class "SplObjectStorage" in foo.php line 12. Did you mean to call: "valid"?',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'type' => 1,
|
||||
'line' => 12,
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Call to undefined method SplObjectStorage::offsetFet()',
|
||||
),
|
||||
'Attempted to call method "offsetFet" on class "SplObjectStorage" in foo.php line 12. Did you mean to call: "offsetGet", "offsetSet", "offsetUnset"?',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
7
Tests/Fixtures/casemismatch.php
Normal file
7
Tests/Fixtures/casemismatch.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
class CaseMismatch
|
||||
{
|
||||
}
|
||||
7
Tests/Fixtures/notPsr0Bis.php
Normal file
7
Tests/Fixtures/notPsr0Bis.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
class NotPSR0bis
|
||||
{
|
||||
}
|
||||
7
Tests/Fixtures/psr4/Psr4CaseMismatch.php
Normal file
7
Tests/Fixtures/psr4/Psr4CaseMismatch.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
class PSR4CaseMismatch
|
||||
{
|
||||
}
|
||||
7
Tests/Fixtures/reallyNotPsr0.php
Normal file
7
Tests/Fixtures/reallyNotPsr0.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
class NotPSR0
|
||||
{
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
"dev-master": "2.5-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<testsuites>
|
||||
|
||||
Reference in New Issue
Block a user