mirror of
https://github.com/symfony/debug.git
synced 2026-03-25 09:42:20 +01:00
Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08589346bd | ||
|
|
2d9eb3ddb3 | ||
|
|
5f109a38d2 | ||
|
|
cfc52f64af | ||
|
|
efac4e1daf | ||
|
|
e131370c3c | ||
|
|
489c11d549 | ||
|
|
c0f543d3c4 | ||
|
|
0dbc119596 | ||
|
|
89e1a14dfd | ||
|
|
fb9e6887db | ||
|
|
e1aa457a97 | ||
|
|
64e916c356 | ||
|
|
e9470b1f9e | ||
|
|
fc037263d5 | ||
|
|
c79c361bca | ||
|
|
726bf9651d | ||
|
|
22b295edff | ||
|
|
8e122417c0 | ||
|
|
9cb55ce279 | ||
|
|
2651b63b15 | ||
|
|
aea19edb4a | ||
|
|
77d632fd72 | ||
|
|
9daa1bf9f7 | ||
|
|
fca5696e0c | ||
|
|
20c5dad1af | ||
|
|
8f1257608f | ||
|
|
3cfb19034c | ||
|
|
6136637a77 | ||
|
|
ef29e641f1 | ||
|
|
92818ecdba | ||
|
|
c20499a2c8 | ||
|
|
636d9b2edf | ||
|
|
ce0589b0aa | ||
|
|
192dd832b3 | ||
|
|
355f12cf08 | ||
|
|
e4b7fc3e49 | ||
|
|
66eb82f37f | ||
|
|
075070230c | ||
|
|
ff6a582db5 | ||
|
|
1df2971b27 | ||
|
|
4851a041c4 | ||
|
|
366f3fe9ea | ||
|
|
30d1de52f1 | ||
|
|
d59aecf5f4 | ||
|
|
366a668f66 | ||
|
|
c214e92851 | ||
|
|
dfe71c73cf | ||
|
|
7c6eebd9df | ||
|
|
ad4511a8fd | ||
|
|
8bc9390ba3 | ||
|
|
d7b0e6d455 | ||
|
|
fcc1957a6a | ||
|
|
572006675e | ||
|
|
95a5aa2b72 | ||
|
|
4bed42658c | ||
|
|
289e4e76fd | ||
|
|
32665233b1 | ||
|
|
c1dc68518c | ||
|
|
7318d7473c | ||
|
|
cd19fabc2a | ||
|
|
9d22a48de5 | ||
|
|
5437529190 | ||
|
|
fff6a5af15 | ||
|
|
eb7564c66d | ||
|
|
ca2dbceeec | ||
|
|
9cd1287f00 | ||
|
|
8f9d6bcc9b | ||
|
|
8e6c46832d | ||
|
|
340e9f7086 | ||
|
|
ab5aea541c | ||
|
|
ff5b42fa46 | ||
|
|
bbf75b3d76 | ||
|
|
01c8bc9bac | ||
|
|
c39932e3e4 | ||
|
|
10aa489e84 | ||
|
|
989a4d5289 | ||
|
|
a627f26c13 | ||
|
|
aa08d8e590 | ||
|
|
e350320216 | ||
|
|
2b8d1207a7 | ||
|
|
21e1aa2ed7 | ||
|
|
76df7e93ea | ||
|
|
d6953fe635 | ||
|
|
4bd15a76de | ||
|
|
a893909a50 | ||
|
|
d49a46a20a | ||
|
|
796dd4cd11 | ||
|
|
081206a6a6 | ||
|
|
10b98f6f47 | ||
|
|
5c1570dea1 | ||
|
|
ff4124e238 | ||
|
|
62962eb4a5 | ||
|
|
b8e722662e | ||
|
|
a6dd8ca51d | ||
|
|
3e889e0489 | ||
|
|
692ba4b3dd | ||
|
|
95db75835d | ||
|
|
dfb9bf2e04 | ||
|
|
29a6fd38f3 | ||
|
|
4d5f8b35d0 | ||
|
|
712c58246b | ||
|
|
ee04626994 | ||
|
|
4ba2bda995 | ||
|
|
4d9fd69c90 | ||
|
|
134fad1b92 | ||
|
|
b622a3752d | ||
|
|
49231a9696 | ||
|
|
4ec60561dd | ||
|
|
0a45b2cdbf | ||
|
|
24170a0eb7 | ||
|
|
b7c4104ea0 | ||
|
|
39a94beb5e | ||
|
|
c1dc0e3667 | ||
|
|
9b1895e0fa | ||
|
|
e604b14862 | ||
|
|
9189ee1980 | ||
|
|
e145cd06f8 | ||
|
|
23e1d613b7 | ||
|
|
2d4ca33b6d | ||
|
|
97856ba8bb | ||
|
|
ebc2a51176 | ||
|
|
6b5a58f533 | ||
|
|
5e9b3f4a56 | ||
|
|
b29066e064 | ||
|
|
aa5794d256 | ||
|
|
e18643bb9d | ||
|
|
4b3b65f72c | ||
|
|
4d52567ace | ||
|
|
52babf5396 | ||
|
|
daa33041ff | ||
|
|
8ecd7655f2 | ||
|
|
39f7b9d9e9 | ||
|
|
77d85b05dd | ||
|
|
6ba896b035 | ||
|
|
0de0b800f3 | ||
|
|
a56ff4422f | ||
|
|
4d01c33fd7 | ||
|
|
4d38ec5021 | ||
|
|
35d9057a4b | ||
|
|
7bc27bc03f | ||
|
|
9cc21042eb | ||
|
|
dacba8e89b | ||
|
|
3294ac5d66 |
@@ -1,6 +1,14 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.7.0
|
||||
-----
|
||||
|
||||
* added deprecations checking for parent interfaces/classes to DebugClassLoader
|
||||
* added ZTS support to symfony_debug extension
|
||||
* added symfony_debug_backtrace() to symfony_debug extension
|
||||
to track the backtrace of fatal errors
|
||||
|
||||
2.6.0
|
||||
-----
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace Symfony\Component\Debug;
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christophe Coevoet <stof@notk.org>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class DebugClassLoader
|
||||
{
|
||||
@@ -30,20 +28,21 @@ class DebugClassLoader
|
||||
private $isFinder;
|
||||
private $wasFinder;
|
||||
private static $caseCheck;
|
||||
private static $deprecated = array();
|
||||
private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null');
|
||||
private static $darwinCache = array('/' => array('/', array()));
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param callable|object $classLoader
|
||||
*
|
||||
* @api
|
||||
* @deprecated since 2.5, passing an object is deprecated and support for it will be removed in 3.0
|
||||
* @param callable|object $classLoader Passing an object is @deprecated since version 2.5 and support for it will be removed in 3.0
|
||||
*/
|
||||
public function __construct($classLoader)
|
||||
{
|
||||
$this->wasFinder = is_object($classLoader) && method_exists($classLoader, 'findFile');
|
||||
|
||||
if ($this->wasFinder) {
|
||||
@trigger_error('The '.__METHOD__.' method will no longer support receiving an object into its $classLoader argument in 3.0.', E_USER_DEPRECATED);
|
||||
$this->classLoader = array($classLoader, 'loadClass');
|
||||
$this->isFinder = true;
|
||||
} else {
|
||||
@@ -59,9 +58,7 @@ class DebugClassLoader
|
||||
/**
|
||||
* Gets the wrapped class loader.
|
||||
*
|
||||
* @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
|
||||
* @return callable|object A class loader. Since version 2.5, returning an object is @deprecated and support for it will be removed in 3.0
|
||||
*/
|
||||
public function getClassLoader()
|
||||
{
|
||||
@@ -69,7 +66,7 @@ class DebugClassLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps all autoloaders
|
||||
* Wraps all autoloaders.
|
||||
*/
|
||||
public static function enable()
|
||||
{
|
||||
@@ -117,16 +114,18 @@ class DebugClassLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a file by class name
|
||||
* Finds a file by class name.
|
||||
*
|
||||
* @param string $class A class name to resolve to file
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @deprecated Deprecated since 2.5, to be removed in 3.0.
|
||||
* @deprecated since version 2.5, to be removed in 3.0.
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
|
||||
|
||||
if ($this->wasFinder) {
|
||||
return $this->classLoader[0]->findFile($class);
|
||||
}
|
||||
@@ -148,7 +147,7 @@ class DebugClassLoader
|
||||
try {
|
||||
if ($this->isFinder) {
|
||||
if ($file = $this->classLoader[0]->findFile($class)) {
|
||||
require $file;
|
||||
require_once $file;
|
||||
}
|
||||
} else {
|
||||
call_user_func($this->classLoader, $class);
|
||||
@@ -175,6 +174,39 @@ class DebugClassLoader
|
||||
if ($name !== $class && 0 === strcasecmp($name, $class)) {
|
||||
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name));
|
||||
}
|
||||
|
||||
if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) {
|
||||
@trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
|
||||
} elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) {
|
||||
self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]);
|
||||
} else {
|
||||
if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) {
|
||||
$len = 0;
|
||||
$ns = '';
|
||||
} else {
|
||||
switch ($ns = substr($name, 0, $len)) {
|
||||
case 'Symfony\Bridge\\':
|
||||
case 'Symfony\Bundle\\':
|
||||
case 'Symfony\Component\\':
|
||||
$ns = 'Symfony\\';
|
||||
$len = strlen($ns);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$parent = $refl->getParentClass();
|
||||
|
||||
if (!$parent || strncmp($ns, $parent->name, $len)) {
|
||||
if ($parent && isset(self::$deprecated[$parent->name]) && strncmp($ns, $parent->name, $len)) {
|
||||
@trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent->name, self::$deprecated[$parent->name]), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
foreach ($refl->getInterfaceNames() as $interface) {
|
||||
if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len) && !($parent && $parent->implementsInterface($interface))) {
|
||||
@trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($file) {
|
||||
@@ -185,35 +217,89 @@ class DebugClassLoader
|
||||
|
||||
throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
|
||||
}
|
||||
if (self::$caseCheck && preg_match('#([/\\\\][a-zA-Z_\x7F-\xFF][a-zA-Z0-9_\x7F-\xFF]*)+\.(php|hh)$#D', $file, $tail)) {
|
||||
$tail = $tail[0];
|
||||
$real = $refl->getFilename();
|
||||
if (self::$caseCheck) {
|
||||
$real = explode('\\', $class.strrchr($file, '.'));
|
||||
$tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file));
|
||||
|
||||
$i = count($tail) - 1;
|
||||
$j = count($real) - 1;
|
||||
|
||||
while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
|
||||
--$i;
|
||||
--$j;
|
||||
}
|
||||
|
||||
array_splice($tail, 0, $i + 1);
|
||||
}
|
||||
if (self::$caseCheck && $tail) {
|
||||
$tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail);
|
||||
$tailLen = strlen($tail);
|
||||
$real = $refl->getFileName();
|
||||
|
||||
if (2 === self::$caseCheck) {
|
||||
// realpath() on MacOSX doesn't normalize the case of characters
|
||||
$cwd = getcwd();
|
||||
$basename = strrpos($real, '/');
|
||||
chdir(substr($real, 0, $basename));
|
||||
$basename = substr($real, $basename + 1);
|
||||
// glob() patterns are case-sensitive even if the underlying fs is not
|
||||
if (!in_array($basename, glob($basename.'*', GLOB_NOSORT), true)) {
|
||||
$real = getcwd().'/';
|
||||
$h = opendir('.');
|
||||
while (false !== $f = readdir($h)) {
|
||||
if (0 === strcasecmp($f, $basename)) {
|
||||
$real .= $f;
|
||||
break;
|
||||
|
||||
$i = 1 + strrpos($real, '/');
|
||||
$file = substr($real, $i);
|
||||
$real = substr($real, 0, $i);
|
||||
|
||||
if (isset(self::$darwinCache[$real])) {
|
||||
$kDir = $real;
|
||||
} else {
|
||||
$kDir = strtolower($real);
|
||||
|
||||
if (isset(self::$darwinCache[$kDir])) {
|
||||
$real = self::$darwinCache[$kDir][0];
|
||||
} else {
|
||||
$dir = getcwd();
|
||||
chdir($real);
|
||||
$real = getcwd().'/';
|
||||
chdir($dir);
|
||||
|
||||
$dir = $real;
|
||||
$k = $kDir;
|
||||
$i = strlen($dir) - 1;
|
||||
while (!isset(self::$darwinCache[$k])) {
|
||||
self::$darwinCache[$k] = array($dir, array());
|
||||
self::$darwinCache[$dir] = &self::$darwinCache[$k];
|
||||
|
||||
while ('/' !== $dir[--$i]) {
|
||||
}
|
||||
$k = substr($k, 0, ++$i);
|
||||
$dir = substr($dir, 0, $i--);
|
||||
}
|
||||
}
|
||||
closedir($h);
|
||||
}
|
||||
chdir($cwd);
|
||||
|
||||
$dirFiles = self::$darwinCache[$kDir][1];
|
||||
|
||||
if (isset($dirFiles[$file])) {
|
||||
$kFile = $file;
|
||||
} else {
|
||||
$kFile = strtolower($file);
|
||||
|
||||
if (!isset($dirFiles[$kFile])) {
|
||||
foreach (scandir($real, 2) as $f) {
|
||||
if ('.' !== $f[0]) {
|
||||
$dirFiles[$f] = $f;
|
||||
if ($f === $file) {
|
||||
$kFile = $k = $file;
|
||||
} elseif ($f !== $k = strtolower($f)) {
|
||||
$dirFiles[$k] = $f;
|
||||
}
|
||||
}
|
||||
}
|
||||
self::$darwinCache[$kDir][1] = $dirFiles;
|
||||
}
|
||||
}
|
||||
|
||||
$real .= $dirFiles[$kFile];
|
||||
}
|
||||
|
||||
if (0 === substr_compare($real, $tail, -strlen($tail), strlen($tail), true)
|
||||
&& 0 !== substr_compare($real, $tail, -strlen($tail), strlen($tail), false)
|
||||
if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
|
||||
&& 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
|
||||
) {
|
||||
throw new \RuntimeException(sprintf('Case mismatch between class and source file names: %s vs %s', $class, $real));
|
||||
throw new \RuntimeException(sprintf('Case mismatch between class and real file names: %s vs %s in %s', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
290
ErrorHandler.php
290
ErrorHandler.php
@@ -15,6 +15,7 @@ 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\FatalThrowableError;
|
||||
use Symfony\Component\Debug\Exception\OutOfMemoryException;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
||||
@@ -46,7 +47,7 @@ use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
|
||||
class ErrorHandler
|
||||
{
|
||||
/**
|
||||
* @deprecated since 2.6, to be removed in 3.0.
|
||||
* @deprecated since version 2.6, to be removed in 3.0.
|
||||
*/
|
||||
const TYPE_DEPRECATION = -100;
|
||||
|
||||
@@ -71,19 +72,19 @@ class ErrorHandler
|
||||
private $loggers = array(
|
||||
E_DEPRECATED => array(null, LogLevel::INFO),
|
||||
E_USER_DEPRECATED => array(null, LogLevel::INFO),
|
||||
E_NOTICE => array(null, LogLevel::NOTICE),
|
||||
E_USER_NOTICE => array(null, LogLevel::NOTICE),
|
||||
E_STRICT => array(null, LogLevel::NOTICE),
|
||||
E_NOTICE => array(null, LogLevel::WARNING),
|
||||
E_USER_NOTICE => array(null, LogLevel::WARNING),
|
||||
E_STRICT => array(null, LogLevel::WARNING),
|
||||
E_WARNING => array(null, LogLevel::WARNING),
|
||||
E_USER_WARNING => array(null, LogLevel::WARNING),
|
||||
E_COMPILE_WARNING => array(null, LogLevel::WARNING),
|
||||
E_CORE_WARNING => array(null, LogLevel::WARNING),
|
||||
E_USER_ERROR => array(null, LogLevel::ERROR),
|
||||
E_RECOVERABLE_ERROR => array(null, LogLevel::ERROR),
|
||||
E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY),
|
||||
E_PARSE => array(null, LogLevel::EMERGENCY),
|
||||
E_ERROR => array(null, LogLevel::EMERGENCY),
|
||||
E_CORE_ERROR => array(null, LogLevel::EMERGENCY),
|
||||
E_USER_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_PARSE => array(null, LogLevel::CRITICAL),
|
||||
E_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_CORE_ERROR => array(null, LogLevel::CRITICAL),
|
||||
);
|
||||
|
||||
private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
|
||||
@@ -101,16 +102,16 @@ 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.
|
||||
* @deprecated since version 2.6, to be removed in 3.0.
|
||||
*/
|
||||
private $displayErrors = 0x1FFF;
|
||||
|
||||
/**
|
||||
* Registers the error handler.
|
||||
*
|
||||
* @param self|null|int $handler The handler to register, or @deprecated (since 2.6, to be removed in 3.0) bit field of thrown levels
|
||||
* @param self|null|int $handler The handler to register, or @deprecated (since version 2.6, to be removed in 3.0) bit field of thrown levels
|
||||
* @param bool $replace Whether to replace or not any existing handler
|
||||
*
|
||||
* @return self The registered error handler
|
||||
@@ -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
|
||||
@@ -250,20 +251,20 @@ class ErrorHandler
|
||||
public function throwAt($levels, $replace = false)
|
||||
{
|
||||
$prev = $this->thrownErrors;
|
||||
$this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
|
||||
$this->thrownErrors = (E_ALL | E_STRICT) & ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
|
||||
if (!$replace) {
|
||||
$this->thrownErrors |= $prev;
|
||||
}
|
||||
$this->reRegister($prev | $this->loggedErrors);
|
||||
|
||||
// $this->displayErrors is @deprecated since 2.6
|
||||
// $this->displayErrors is @deprecated since version 2.6
|
||||
$this->displayErrors = $this->thrownErrors;
|
||||
|
||||
return $prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -349,76 +350,94 @@ class ErrorHandler
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function handleError($type, $message, $file, $line, array $context)
|
||||
public function handleError($type, $message, $file, $line, array $context, array $backtrace = null)
|
||||
{
|
||||
$level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR;
|
||||
$level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
|
||||
$log = $this->loggedErrors & $type;
|
||||
$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 (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 (null !== $backtrace && $type & E_ERROR) {
|
||||
// E_ERROR fatal errors are triggered on HHVM when
|
||||
// hhvm.error_handling.call_user_handler_on_fatals=1
|
||||
// which is the way to get their backtrace.
|
||||
$this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 ($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);
|
||||
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;
|
||||
} 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'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
}
|
||||
} elseif ($trace) {
|
||||
if (null === $backtrace) {
|
||||
$e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
} else {
|
||||
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
$this->loggedTraces[$e] = 1;
|
||||
}
|
||||
|
||||
$e = compact('type', 'file', 'line', 'level');
|
||||
|
||||
if ($type & $level) {
|
||||
if ($this->scopedErrors & $type) {
|
||||
$e['context'] = $context;
|
||||
if ($trace) {
|
||||
$e['stack'] = debug_backtrace(true); // Provide object
|
||||
foreach ($backtrace as &$frame) {
|
||||
unset($frame['args'], $frame);
|
||||
}
|
||||
} elseif ($trace) {
|
||||
$e['stack'] = debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false);
|
||||
$e['stack'] = $backtrace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if ($this->isRecursive) {
|
||||
$log = 0;
|
||||
} elseif (self::$stackedErrorLevels) {
|
||||
self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $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;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,26 +445,39 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an exception by logging then forwarding it to an other handler.
|
||||
* Handles an exception by logging then forwarding it to another handler.
|
||||
*
|
||||
* @param \Exception $exception An exception to handle
|
||||
* @param array $error An array as returned by error_get_last()
|
||||
* @param \Exception|\Throwable $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 FatalThrowableError($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) {
|
||||
$message = 'Fatal '.$exception->getMessage();
|
||||
if ($exception instanceof FatalThrowableError) {
|
||||
$error = array(
|
||||
'type' => $type,
|
||||
'message' => $message = $exception->getMessage(),
|
||||
'file' => $e['file'],
|
||||
'line' => $e['line'],
|
||||
);
|
||||
} else {
|
||||
$message = 'Fatal '.$exception->getMessage();
|
||||
}
|
||||
} elseif ($exception instanceof \ErrorException) {
|
||||
$message = 'Uncaught '.$exception->getMessage();
|
||||
if ($exception instanceof ContextErrorException) {
|
||||
@@ -472,6 +504,9 @@ class ErrorHandler
|
||||
try {
|
||||
call_user_func($this->exceptionHandler, $exception);
|
||||
} catch (\Exception $handlerException) {
|
||||
} catch (\Throwable $handlerException) {
|
||||
}
|
||||
if (isset($handlerException)) {
|
||||
$this->exceptionHandler = null;
|
||||
$this->handleException($handlerException);
|
||||
}
|
||||
@@ -486,41 +521,50 @@ class ErrorHandler
|
||||
*/
|
||||
public static function handleFatalError(array $error = null)
|
||||
{
|
||||
self::$reservedMemory = '';
|
||||
if (null === self::$reservedMemory) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$reservedMemory = null;
|
||||
|
||||
$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);
|
||||
$trace = isset($error['backtrace']) ? $error['backtrace'] : null;
|
||||
|
||||
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, $trace);
|
||||
} else {
|
||||
$exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
|
||||
}
|
||||
} elseif (!isset($exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$handler->handleException($exception, $error);
|
||||
} catch (FatalErrorException $e) {
|
||||
// Ignore this re-throw
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +585,7 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Unstacks stacked errors and forwards to the logger
|
||||
* Unstacks stacked errors and forwards to the logger.
|
||||
*/
|
||||
public static function unstackErrors()
|
||||
{
|
||||
@@ -560,7 +604,7 @@ class ErrorHandler
|
||||
self::$stackedErrors = array();
|
||||
|
||||
foreach ($errors as $e) {
|
||||
$e[0][0]->log($e[0][1], $e[1], $e[2]);
|
||||
$e[0]->log($e[1], $e[2], $e[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -586,10 +630,12 @@ class ErrorHandler
|
||||
*
|
||||
* @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.
|
||||
* @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
|
||||
*/
|
||||
public function setLevel($level)
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
|
||||
|
||||
$level = null === $level ? error_reporting() : $level;
|
||||
$this->throwAt($level, true);
|
||||
}
|
||||
@@ -599,10 +645,12 @@ class ErrorHandler
|
||||
*
|
||||
* @param int $displayErrors The display_errors flag value
|
||||
*
|
||||
* @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead.
|
||||
* @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
|
||||
*/
|
||||
public function setDisplayErrors($displayErrors)
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
|
||||
|
||||
if ($displayErrors) {
|
||||
$this->throwAt($this->displayErrors, true);
|
||||
} else {
|
||||
@@ -618,10 +666,12 @@ class ErrorHandler
|
||||
* @param LoggerInterface $logger A logger interface
|
||||
* @param string $channel The channel associated with the logger (deprecation, emergency or scream)
|
||||
*
|
||||
* @deprecated since 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead.
|
||||
* @deprecated since version 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead.
|
||||
*/
|
||||
public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' static method is deprecated since version 2.6 and will be removed in 3.0. Use the setLoggers() or setDefaultLogger() methods instead.', E_USER_DEPRECATED);
|
||||
|
||||
$handler = set_error_handler('var_dump', 0);
|
||||
$handler = is_array($handler) ? $handler[0] : null;
|
||||
restore_error_handler();
|
||||
@@ -641,26 +691,30 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 2.6, to be removed in 3.0. Use handleError() instead.
|
||||
* @deprecated since version 2.6, to be removed in 3.0. Use handleError() instead.
|
||||
*/
|
||||
public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
|
||||
{
|
||||
$this->handleError(E_USER_DEPRECATED, 'The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the handleError() method instead.', __FILE__, __LINE__, array());
|
||||
|
||||
return $this->handleError($level, $message, $file, $line, (array) $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles PHP fatal errors.
|
||||
*
|
||||
* @deprecated since 2.6, to be removed in 3.0. Use handleFatalError() instead.
|
||||
* @deprecated since version 2.6, to be removed in 3.0. Use handleFatalError() instead.
|
||||
*/
|
||||
public function handleFatal()
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the handleFatalError() method instead.', E_USER_DEPRECATED);
|
||||
|
||||
static::handleFatalError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\Debug\Exception;
|
||||
|
||||
@trigger_error('The '.__NAMESPACE__.'\DummyException class is deprecated since version 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
|
||||
@@ -35,11 +35,19 @@ use Symfony\Component\HttpKernel\Exception\FatalErrorException as LegacyFatalErr
|
||||
*/
|
||||
class FatalErrorException extends LegacyFatalErrorException
|
||||
{
|
||||
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true)
|
||||
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null)
|
||||
{
|
||||
parent::__construct($message, $code, $severity, $filename, $lineno);
|
||||
|
||||
if (null !== $traceOffset) {
|
||||
if (null !== $trace) {
|
||||
if (!$traceArgs) {
|
||||
foreach ($trace as &$frame) {
|
||||
unset($frame['args'], $frame['this'], $frame);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setTrace($trace);
|
||||
} elseif (null !== $traceOffset) {
|
||||
if (function_exists('xdebug_get_function_stack')) {
|
||||
$trace = xdebug_get_function_stack();
|
||||
if (0 < $traceOffset) {
|
||||
@@ -48,7 +56,7 @@ class FatalErrorException extends LegacyFatalErrorException
|
||||
|
||||
foreach ($trace as &$frame) {
|
||||
if (!isset($frame['type'])) {
|
||||
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
|
||||
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
|
||||
if (isset($frame['class'])) {
|
||||
$frame['type'] = '::';
|
||||
}
|
||||
@@ -69,6 +77,11 @@ class FatalErrorException extends LegacyFatalErrorException
|
||||
|
||||
unset($frame);
|
||||
$trace = array_reverse($trace);
|
||||
} elseif (function_exists('symfony_debug_backtrace')) {
|
||||
$trace = symfony_debug_backtrace();
|
||||
if (0 < $traceOffset) {
|
||||
array_splice($trace, 0, $traceOffset);
|
||||
}
|
||||
} else {
|
||||
$trace = array();
|
||||
}
|
||||
|
||||
44
Exception/FatalThrowableError.php
Normal file
44
Exception/FatalThrowableError.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;
|
||||
|
||||
/**
|
||||
* Fatal Throwable Error.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class FatalThrowableError extends FatalErrorException
|
||||
{
|
||||
public function __construct(\Throwable $e)
|
||||
{
|
||||
if ($e instanceof \ParseError) {
|
||||
$message = 'Parse error: '.$e->getMessage();
|
||||
$severity = E_PARSE;
|
||||
} elseif ($e instanceof \TypeError) {
|
||||
$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());
|
||||
}
|
||||
}
|
||||
@@ -94,8 +94,13 @@ class FlattenException extends LegacyFlattenException
|
||||
$e->setClass(get_class($exception));
|
||||
$e->setFile($exception->getFile());
|
||||
$e->setLine($exception->getLine());
|
||||
if ($exception->getPrevious()) {
|
||||
$e->setPrevious(static::create($exception->getPrevious()));
|
||||
|
||||
$previous = $exception->getPrevious();
|
||||
|
||||
if ($previous instanceof \Exception) {
|
||||
$e->setPrevious(static::create($previous));
|
||||
} elseif ($previous instanceof \Throwable) {
|
||||
$e->setPrevious(static::create(new FatalThrowableError($previous)));
|
||||
}
|
||||
|
||||
return $e;
|
||||
|
||||
@@ -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, '%')) {
|
||||
// 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);
|
||||
$handler = new static($debug, $charset, $fileLinkFormat);
|
||||
|
||||
$prev = set_exception_handler(array($handler, 'handle'));
|
||||
if (is_array($prev) && $prev[0] instanceof ErrorHandler) {
|
||||
@@ -143,19 +153,22 @@ class ExceptionHandler
|
||||
* it will fallback to plain PHP functions.
|
||||
*
|
||||
* @param \Exception $exception An \Exception instance
|
||||
*
|
||||
* @see sendPhpResponse()
|
||||
* @see createResponse()
|
||||
*/
|
||||
private function failSafeHandle(\Exception $exception)
|
||||
{
|
||||
if (class_exists('Symfony\Component\HttpFoundation\Response', false)) {
|
||||
if (class_exists('Symfony\Component\HttpFoundation\Response', false)
|
||||
&& __CLASS__ !== get_class($this)
|
||||
&& ($reflector = new \ReflectionMethod($this, 'createResponse'))
|
||||
&& __CLASS__ !== $reflector->class
|
||||
) {
|
||||
$response = $this->createResponse($exception);
|
||||
$response->sendHeaders();
|
||||
$response->sendContent();
|
||||
} else {
|
||||
$this->sendPhpResponse($exception);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sendPhpResponse($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,6 +190,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));
|
||||
@@ -195,7 +209,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,7 +237,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>
|
||||
@@ -251,7 +265,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.';
|
||||
}
|
||||
@@ -337,7 +351,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 */
|
||||
@@ -360,16 +374,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);
|
||||
}
|
||||
@@ -389,11 +403,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]) {
|
||||
@@ -401,7 +415,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);
|
||||
@@ -411,24 +425,25 @@ EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an UTF-8 and HTML encoded string
|
||||
* Returns an UTF-8 and HTML encoded string.
|
||||
*
|
||||
* @deprecated since version 2.7, to be removed in 3.0.
|
||||
*/
|
||||
protected static function utf8Htmlize($str)
|
||||
{
|
||||
if (!preg_match('//u', $str) && function_exists('iconv')) {
|
||||
set_error_handler('var_dump', 0);
|
||||
$charset = ini_get('default_charset');
|
||||
if ('UTF-8' === $charset || $str !== @iconv($charset, $charset, $str)) {
|
||||
$charset = 'CP1252';
|
||||
}
|
||||
restore_error_handler();
|
||||
|
||||
$str = iconv($charset, 'UTF-8', $str);
|
||||
}
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -77,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)
|
||||
@@ -101,7 +101,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
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);
|
||||
}
|
||||
@@ -118,6 +118,13 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
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 array_unique($classes);
|
||||
@@ -132,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;
|
||||
}
|
||||
@@ -160,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.
|
||||
|
||||
@@ -34,7 +34,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
$className = $matches[1];
|
||||
$methodName = $matches[2];
|
||||
|
||||
$message = sprintf('Attempted to call method "%s" on class "%s".', $methodName, $className);
|
||||
$message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className);
|
||||
|
||||
$candidates = array();
|
||||
foreach (get_class_methods($className) as $definedMethodName) {
|
||||
|
||||
@@ -15,6 +15,7 @@ Debug::enable();
|
||||
You can also use the tools individually:
|
||||
|
||||
```php
|
||||
use Symfony\Component\Debug\DebugClassLoader;
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
use Symfony\Component\Debug\ExceptionHandler;
|
||||
|
||||
@@ -25,11 +26,9 @@ if ('cli' !== php_sapi_name()) {
|
||||
ini_set('display_errors', 1);
|
||||
}
|
||||
ErrorHandler::register();
|
||||
DebugClassLoader::enable();
|
||||
```
|
||||
|
||||
Note that the `Debug::enable()` call also registers the debug class loader
|
||||
from the Symfony ClassLoader component when available.
|
||||
|
||||
This component can optionally take advantage of the features of the HttpKernel
|
||||
and HttpFoundation components.
|
||||
|
||||
@@ -39,5 +38,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
|
||||
|
||||
133
Resources/ext/README.md
Normal file
133
Resources/ext/README.md
Normal file
@@ -0,0 +1,133 @@
|
||||
Symfony Debug Extension
|
||||
=======================
|
||||
|
||||
This extension publishes several functions to help building powerful debugging tools.
|
||||
|
||||
symfony_zval_info()
|
||||
-------------------
|
||||
|
||||
- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP,
|
||||
- does work with references, preventing memory copying.
|
||||
|
||||
Its behavior is about the same as:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function symfony_zval_info($key, $array, $options = 0)
|
||||
{
|
||||
|
||||
// $options is currently not used, but could be in future version.
|
||||
|
||||
if (!array_key_exists($key, $array)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$info = array(
|
||||
'type' => gettype($array[$key]),
|
||||
'zval_hash' => /* hashed memory address of $array[$key] */,
|
||||
'zval_refcount' => /* internal zval refcount of $array[$key] */,
|
||||
'zval_isref' => /* is_ref status of $array[$key] */,
|
||||
);
|
||||
|
||||
switch ($info['type']) {
|
||||
case 'object':
|
||||
$info += array(
|
||||
'object_class' => get_class($array[$key]),
|
||||
'object_refcount' => /* internal object refcount of $array[$key] */,
|
||||
'object_hash' => spl_object_hash($array[$key]),
|
||||
'object_handle' => /* internal object handle $array[$key] */,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'resource':
|
||||
$info += array(
|
||||
'resource_handle' => (int) $array[$key],
|
||||
'resource_type' => get_resource_type($array[$key]),
|
||||
'resource_refcount' => /* internal resource refcount of $array[$key] */,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'array':
|
||||
$info += array(
|
||||
'array_count' => count($array[$key]),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
$info += array(
|
||||
'strlen' => strlen($array[$key]),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
```
|
||||
|
||||
symfony_debug_backtrace()
|
||||
-------------------------
|
||||
|
||||
This function works like debug_backtrace(), except that it can fetch the full backtrace in case of fatal errors:
|
||||
|
||||
```php
|
||||
function foo() { fatal(); }
|
||||
function bar() { foo(); }
|
||||
|
||||
function sd() { var_dump(symfony_debug_backtrace()); }
|
||||
|
||||
register_shutdown_function('sd');
|
||||
|
||||
bar();
|
||||
|
||||
/* Will output
|
||||
Fatal error: Call to undefined function fatal() in foo.php on line 42
|
||||
array(3) {
|
||||
[0]=>
|
||||
array(2) {
|
||||
["function"]=>
|
||||
string(2) "sd"
|
||||
["args"]=>
|
||||
array(0) {
|
||||
}
|
||||
}
|
||||
[1]=>
|
||||
array(4) {
|
||||
["file"]=>
|
||||
string(7) "foo.php"
|
||||
["line"]=>
|
||||
int(1)
|
||||
["function"]=>
|
||||
string(3) "foo"
|
||||
["args"]=>
|
||||
array(0) {
|
||||
}
|
||||
}
|
||||
[2]=>
|
||||
array(4) {
|
||||
["file"]=>
|
||||
string(102) "foo.php"
|
||||
["line"]=>
|
||||
int(2)
|
||||
["function"]=>
|
||||
string(3) "bar"
|
||||
["args"]=>
|
||||
array(0) {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The extension is compatible with ZTS mode, and should be supported by PHP5.3, 5.4, 5.5 and 5.6.
|
||||
To enable the extension from source, run:
|
||||
|
||||
```
|
||||
phpize
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
@@ -1,72 +0,0 @@
|
||||
Symfony Debug Extension
|
||||
=======================
|
||||
|
||||
This extension adds a ``symfony_zval_info($key, $array, $options = 0)`` function that:
|
||||
|
||||
- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP,
|
||||
- does work with references, preventing memory copying.
|
||||
|
||||
Its behavior is about the same as:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
function symfony_zval_info($key, $array, $options = 0)
|
||||
{
|
||||
// $options is currently not used, but could be in future version.
|
||||
|
||||
if (!array_key_exists($key, $array)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$info = array(
|
||||
'type' => gettype($array[$key]),
|
||||
'zval_hash' => /* hashed memory address of $array[$key] */,
|
||||
'zval_refcount' => /* internal zval refcount of $array[$key] */,
|
||||
'zval_isref' => /* is_ref status of $array[$key] */,
|
||||
);
|
||||
|
||||
switch ($info['type']) {
|
||||
case 'object':
|
||||
$info += array(
|
||||
'object_class' => get_class($array[$key]),
|
||||
'object_refcount' => /* internal object refcount of $array[$key] */,
|
||||
'object_hash' => spl_object_hash($array[$key]),
|
||||
'object_handle' => /* internal object handle $array[$key] */,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'resource':
|
||||
$info += array(
|
||||
'resource_handle' => (int) $array[$key],
|
||||
'resource_type' => get_resource_type($array[$key]),
|
||||
'resource_refcount' => /* internal resource refcount of $array[$key] */,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'array':
|
||||
$info += array(
|
||||
'array_count' => count($array[$key]),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
$info += array(
|
||||
'strlen' => strlen($array[$key]),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
To enable the extension from source, run:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
phpize
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
extern zend_module_entry symfony_debug_module_entry;
|
||||
#define phpext_symfony_debug_ptr &symfony_debug_module_entry
|
||||
|
||||
#define PHP_SYMFONY_DEBUG_VERSION "1.0"
|
||||
#define PHP_SYMFONY_DEBUG_VERSION "2.7"
|
||||
|
||||
#ifdef PHP_WIN32
|
||||
# define PHP_SYMFONY_DEBUG_API __declspec(dllexport)
|
||||
@@ -29,6 +29,8 @@ extern zend_module_entry symfony_debug_module_entry;
|
||||
|
||||
ZEND_BEGIN_MODULE_GLOBALS(symfony_debug)
|
||||
intptr_t req_rand_init;
|
||||
void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args);
|
||||
zval *debug_bt;
|
||||
ZEND_END_MODULE_GLOBALS(symfony_debug)
|
||||
|
||||
PHP_MINIT_FUNCTION(symfony_debug);
|
||||
@@ -40,11 +42,14 @@ PHP_GINIT_FUNCTION(symfony_debug);
|
||||
PHP_GSHUTDOWN_FUNCTION(symfony_debug);
|
||||
|
||||
PHP_FUNCTION(symfony_zval_info);
|
||||
PHP_FUNCTION(symfony_debug_backtrace);
|
||||
|
||||
static char *_symfony_debug_memory_address_hash(void *);
|
||||
static char *_symfony_debug_memory_address_hash(void * TSRMLS_DC);
|
||||
static const char *_symfony_debug_zval_type(zval *);
|
||||
static const char* _symfony_debug_get_resource_type(long);
|
||||
static int _symfony_debug_get_resource_refcount(long);
|
||||
static const char* _symfony_debug_get_resource_type(long TSRMLS_DC);
|
||||
static int _symfony_debug_get_resource_refcount(long TSRMLS_DC);
|
||||
|
||||
void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args);
|
||||
|
||||
#ifdef ZTS
|
||||
#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v)
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#endif
|
||||
|
||||
#include "php.h"
|
||||
#ifdef ZTS
|
||||
#include "TSRM.h"
|
||||
#endif
|
||||
#include "php_ini.h"
|
||||
#include "ext/standard/info.h"
|
||||
#include "php_symfony_debug.h"
|
||||
@@ -19,6 +22,13 @@
|
||||
#include "ext/standard/php_lcg.h"
|
||||
#include "ext/spl/php_spl.h"
|
||||
#include "Zend/zend_gc.h"
|
||||
#include "Zend/zend_builtin_functions.h"
|
||||
#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */
|
||||
#include "ext/standard/php_array.h"
|
||||
#include "Zend/zend_interfaces.h"
|
||||
#include "SAPI.h"
|
||||
|
||||
#define IS_PHP_53 ZEND_EXTENSION_API_NO == 220090626
|
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(symfony_debug)
|
||||
|
||||
@@ -30,9 +40,28 @@ ZEND_END_ARG_INFO()
|
||||
|
||||
const zend_function_entry symfony_debug_functions[] = {
|
||||
PHP_FE(symfony_zval_info, symfony_zval_arginfo)
|
||||
PHP_FE(symfony_debug_backtrace, NULL)
|
||||
PHP_FE_END
|
||||
};
|
||||
|
||||
PHP_FUNCTION(symfony_debug_backtrace)
|
||||
{
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
return;
|
||||
}
|
||||
#if IS_PHP_53
|
||||
zend_fetch_debug_backtrace(return_value, 1, 0 TSRMLS_CC);
|
||||
#else
|
||||
zend_fetch_debug_backtrace(return_value, 1, 0, 0 TSRMLS_CC);
|
||||
#endif
|
||||
|
||||
if (!SYMFONY_DEBUG_G(debug_bt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(SYMFONY_DEBUG_G(debug_bt)), 0 TSRMLS_CC);
|
||||
}
|
||||
|
||||
PHP_FUNCTION(symfony_zval_info)
|
||||
{
|
||||
zval *key = NULL, *arg = NULL;
|
||||
@@ -40,7 +69,7 @@ PHP_FUNCTION(symfony_zval_info)
|
||||
HashTable *array = NULL;
|
||||
long options = 0;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zh|l", &key, &array, &options) == FAILURE) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zh|l", &key, &array, &options) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,13 +91,14 @@ PHP_FUNCTION(symfony_zval_info)
|
||||
array_init(return_value);
|
||||
|
||||
add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1);
|
||||
add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg), 16, 1);
|
||||
add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg TSRMLS_CC), 16, 0);
|
||||
add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg));
|
||||
add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg));
|
||||
|
||||
if (Z_TYPE_P(arg) == IS_OBJECT) {
|
||||
static char hash[33] = {0};
|
||||
php_spl_object_hash(arg, (char *)hash);
|
||||
char hash[33] = {0};
|
||||
|
||||
php_spl_object_hash(arg, (char *)hash TSRMLS_CC);
|
||||
add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1);
|
||||
add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount);
|
||||
add_assoc_string(return_value, "object_hash", hash, 1);
|
||||
@@ -77,17 +107,41 @@ PHP_FUNCTION(symfony_zval_info)
|
||||
add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg)));
|
||||
} else if(Z_TYPE_P(arg) == IS_RESOURCE) {
|
||||
add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg));
|
||||
add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg)), 1);
|
||||
add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg)));
|
||||
add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg) TSRMLS_CC), 1);
|
||||
add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg) TSRMLS_CC));
|
||||
} else if (Z_TYPE_P(arg) == IS_STRING) {
|
||||
add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* _symfony_debug_get_resource_type(long rsid)
|
||||
void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args)
|
||||
{
|
||||
TSRMLS_FETCH();
|
||||
zval *retval;
|
||||
|
||||
switch (type) {
|
||||
case E_ERROR:
|
||||
case E_PARSE:
|
||||
case E_CORE_ERROR:
|
||||
case E_CORE_WARNING:
|
||||
case E_COMPILE_ERROR:
|
||||
case E_COMPILE_WARNING:
|
||||
ALLOC_INIT_ZVAL(retval);
|
||||
#if IS_PHP_53
|
||||
zend_fetch_debug_backtrace(retval, 1, 0 TSRMLS_CC);
|
||||
#else
|
||||
zend_fetch_debug_backtrace(retval, 1, 0, 0 TSRMLS_CC);
|
||||
#endif
|
||||
SYMFONY_DEBUG_G(debug_bt) = retval;
|
||||
}
|
||||
|
||||
SYMFONY_DEBUG_G(old_error_cb)(type, error_filename, error_lineno, format, args);
|
||||
}
|
||||
|
||||
static const char* _symfony_debug_get_resource_type(long rsid TSRMLS_DC)
|
||||
{
|
||||
const char *res_type;
|
||||
res_type = zend_rsrc_list_get_rsrc_type(rsid);
|
||||
res_type = zend_rsrc_list_get_rsrc_type(rsid TSRMLS_CC);
|
||||
|
||||
if (!res_type) {
|
||||
return "Unknown";
|
||||
@@ -96,7 +150,7 @@ static const char* _symfony_debug_get_resource_type(long rsid)
|
||||
return res_type;
|
||||
}
|
||||
|
||||
static int _symfony_debug_get_resource_refcount(long rsid)
|
||||
static int _symfony_debug_get_resource_refcount(long rsid TSRMLS_DC)
|
||||
{
|
||||
zend_rsrc_list_entry *le;
|
||||
|
||||
@@ -107,21 +161,21 @@ static int _symfony_debug_get_resource_refcount(long rsid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *_symfony_debug_memory_address_hash(void *address)
|
||||
static char *_symfony_debug_memory_address_hash(void *address TSRMLS_DC)
|
||||
{
|
||||
static char result[17] = {0};
|
||||
char *result = NULL;
|
||||
intptr_t address_rand;
|
||||
|
||||
if (!SYMFONY_DEBUG_G(req_rand_init)) {
|
||||
if (!BG(mt_rand_is_seeded)) {
|
||||
php_mt_srand(GENERATE_SEED() TSRMLS_CC);
|
||||
}
|
||||
SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand();
|
||||
SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand(TSRMLS_C);
|
||||
}
|
||||
|
||||
address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init);
|
||||
|
||||
snprintf(result, 17, "%016zx", address_rand);
|
||||
spprintf(&result, 17, "%016zx", address_rand);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -187,7 +241,7 @@ ZEND_GET_MODULE(symfony_debug)
|
||||
|
||||
PHP_GINIT_FUNCTION(symfony_debug)
|
||||
{
|
||||
symfony_debug_globals->req_rand_init = 0;
|
||||
memset(symfony_debug_globals, 0 , sizeof(*symfony_debug_globals));
|
||||
}
|
||||
|
||||
PHP_GSHUTDOWN_FUNCTION(symfony_debug)
|
||||
@@ -197,11 +251,16 @@ PHP_GSHUTDOWN_FUNCTION(symfony_debug)
|
||||
|
||||
PHP_MINIT_FUNCTION(symfony_debug)
|
||||
{
|
||||
SYMFONY_DEBUG_G(old_error_cb) = zend_error_cb;
|
||||
zend_error_cb = symfony_debug_error_cb;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
PHP_MSHUTDOWN_FUNCTION(symfony_debug)
|
||||
{
|
||||
zend_error_cb = SYMFONY_DEBUG_G(old_error_cb);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Test symfony_zval_info API
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
<?php
|
||||
|
||||
$int = 42;
|
||||
$float = 42.42;
|
||||
@@ -88,7 +88,7 @@ array(8) {
|
||||
["object_hash"]=>
|
||||
string(32) "%s"
|
||||
["object_handle"]=>
|
||||
int(1)
|
||||
int(%d)
|
||||
}
|
||||
array(5) {
|
||||
["type"]=>
|
||||
@@ -112,7 +112,7 @@ array(7) {
|
||||
["zval_isref"]=>
|
||||
bool(false)
|
||||
["resource_handle"]=>
|
||||
int(4)
|
||||
int(%d)
|
||||
["resource_type"]=>
|
||||
string(6) "stream"
|
||||
["resource_refcount"]=>
|
||||
|
||||
64
Resources/ext/tests/002.phpt
Normal file
64
Resources/ext/tests/002.phpt
Normal file
@@ -0,0 +1,64 @@
|
||||
--TEST--
|
||||
Test symfony_debug_backtrace in case of fatal error
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function bar()
|
||||
{
|
||||
foo();
|
||||
}
|
||||
|
||||
function foo()
|
||||
{
|
||||
notexist();
|
||||
}
|
||||
|
||||
function bt()
|
||||
{
|
||||
print_r(symfony_debug_backtrace());
|
||||
|
||||
}
|
||||
|
||||
register_shutdown_function('bt');
|
||||
|
||||
bar();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Call to undefined function notexist() in %s on line %d
|
||||
Array
|
||||
(
|
||||
[0] => Array
|
||||
(
|
||||
[function] => bt
|
||||
[args] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
[1] => Array
|
||||
(
|
||||
[file] => %s
|
||||
[line] => %d
|
||||
[function] => foo
|
||||
[args] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
[2] => Array
|
||||
(
|
||||
[file] => %s
|
||||
[line] => %d
|
||||
[function] => bar
|
||||
[args] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
)
|
||||
47
Resources/ext/tests/002_1.phpt
Normal file
47
Resources/ext/tests/002_1.phpt
Normal file
@@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
Test symfony_debug_backtrace in case of non fatal error
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function bar()
|
||||
{
|
||||
bt();
|
||||
}
|
||||
|
||||
function bt()
|
||||
{
|
||||
print_r(symfony_debug_backtrace());
|
||||
|
||||
}
|
||||
|
||||
bar();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Array
|
||||
(
|
||||
[0] => Array
|
||||
(
|
||||
[file] => %s
|
||||
[line] => %d
|
||||
[function] => bt
|
||||
[args] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
[1] => Array
|
||||
(
|
||||
[file] => %s
|
||||
[line] => %d
|
||||
[function] => bar
|
||||
[args] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
)
|
||||
85
Resources/ext/tests/003.phpt
Normal file
85
Resources/ext/tests/003.phpt
Normal file
@@ -0,0 +1,85 @@
|
||||
--TEST--
|
||||
Test ErrorHandler in case of fatal error
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
class LogLevel
|
||||
{
|
||||
const EMERGENCY = 'emergency';
|
||||
const ALERT = 'alert';
|
||||
const CRITICAL = 'critical';
|
||||
const ERROR = 'error';
|
||||
const WARNING = 'warning';
|
||||
const NOTICE = 'notice';
|
||||
const INFO = 'info';
|
||||
const DEBUG = 'debug';
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Debug;
|
||||
|
||||
$dir = __DIR__.'/../../../';
|
||||
require $dir.'ErrorHandler.php';
|
||||
require $dir.'Exception/FatalErrorException.php';
|
||||
require $dir.'Exception/UndefinedFunctionException.php';
|
||||
require $dir.'FatalErrorHandler/FatalErrorHandlerInterface.php';
|
||||
require $dir.'FatalErrorHandler/ClassNotFoundFatalErrorHandler.php';
|
||||
require $dir.'FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php';
|
||||
require $dir.'FatalErrorHandler/UndefinedMethodFatalErrorHandler.php';
|
||||
|
||||
function bar()
|
||||
{
|
||||
foo();
|
||||
}
|
||||
|
||||
function foo()
|
||||
{
|
||||
notexist();
|
||||
}
|
||||
|
||||
$handler = ErrorHandler::register();
|
||||
$handler->setExceptionHandler('print_r');
|
||||
|
||||
if (function_exists('xdebug_disable')) {
|
||||
xdebug_disable();
|
||||
}
|
||||
|
||||
bar();
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Call to undefined function Symfony\Component\Debug\notexist() in %s on line %d
|
||||
Symfony\Component\Debug\Exception\UndefinedFunctionException Object
|
||||
(
|
||||
[message:protected] => Attempted to call function "notexist" from namespace "Symfony\Component\Debug".
|
||||
[string:Exception:private] =>
|
||||
[code:protected] => 0
|
||||
[file:protected] => %s
|
||||
[line:protected] => %d
|
||||
[trace:Exception:private] => Array
|
||||
(
|
||||
[0] => Array
|
||||
(
|
||||
%A [function] => Symfony\Component\Debug\foo
|
||||
%A [args] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
[1] => Array
|
||||
(
|
||||
%A [function] => Symfony\Component\Debug\bar
|
||||
%A [args] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
%A
|
||||
)
|
||||
|
||||
[previous:Exception:private] =>
|
||||
[severity:protected] => 1
|
||||
)
|
||||
@@ -61,6 +61,13 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testUnsilencing()
|
||||
{
|
||||
if (PHP_VERSION_ID >= 70000) {
|
||||
$this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.');
|
||||
}
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('HHVM is not handled in this test case.');
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
$this->iniSet('log_errors', 0);
|
||||
@@ -82,6 +89,9 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) {
|
||||
$this->markTestSkipped('The ContextErrorException class is already loaded.');
|
||||
}
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('HHVM is not handled in this test case.');
|
||||
}
|
||||
|
||||
ErrorHandler::register();
|
||||
|
||||
@@ -100,9 +110,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();
|
||||
@@ -121,6 +136,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage Case mismatch between class and real file names
|
||||
*/
|
||||
public function testFileCaseMismatch()
|
||||
{
|
||||
@@ -153,6 +169,87 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDeprecatedSuper
|
||||
*/
|
||||
public function testDeprecatedSuper($class, $super, $type)
|
||||
{
|
||||
set_error_handler('var_dump', 0);
|
||||
$e = error_reporting(0);
|
||||
trigger_error('', E_USER_DEPRECATED);
|
||||
|
||||
class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
|
||||
$lastError = error_get_last();
|
||||
unset($lastError['file'], $lastError['line']);
|
||||
|
||||
$xError = array(
|
||||
'type' => E_USER_DEPRECATED,
|
||||
'message' => 'The Test\Symfony\Component\Debug\Tests\\'.$class.' class '.$type.' Symfony\Component\Debug\Tests\Fixtures\\'.$super.' that is deprecated but this is a test deprecation notice.',
|
||||
);
|
||||
|
||||
$this->assertSame($xError, $lastError);
|
||||
}
|
||||
|
||||
public function provideDeprecatedSuper()
|
||||
{
|
||||
return array(
|
||||
array('DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'),
|
||||
array('DeprecatedParentClass', 'DeprecatedClass', 'extends'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testDeprecatedSuperInSameNamespace()
|
||||
{
|
||||
set_error_handler('var_dump', 0);
|
||||
$e = error_reporting(0);
|
||||
trigger_error('', E_USER_NOTICE);
|
||||
|
||||
class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
|
||||
$lastError = error_get_last();
|
||||
unset($lastError['file'], $lastError['line']);
|
||||
|
||||
$xError = array(
|
||||
'type' => E_USER_NOTICE,
|
||||
'message' => '',
|
||||
);
|
||||
|
||||
$this->assertSame($xError, $lastError);
|
||||
}
|
||||
|
||||
public function testReservedForPhp7()
|
||||
{
|
||||
if (PHP_VERSION_ID >= 70000) {
|
||||
$this->markTestSkipped('PHP7 already prevents using reserved names.');
|
||||
}
|
||||
|
||||
set_error_handler('var_dump', 0);
|
||||
$e = error_reporting(0);
|
||||
trigger_error('', E_USER_NOTICE);
|
||||
|
||||
class_exists('Test\\'.__NAMESPACE__.'\\Float', true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
|
||||
$lastError = error_get_last();
|
||||
unset($lastError['file'], $lastError['line']);
|
||||
|
||||
$xError = array(
|
||||
'type' => E_USER_DEPRECATED,
|
||||
'message' => 'Test\Symfony\Component\Debug\Tests\Float uses a reserved class name (Float) that will break on PHP 7 and higher',
|
||||
);
|
||||
|
||||
$this->assertSame($xError, $lastError);
|
||||
}
|
||||
}
|
||||
|
||||
class ClassLoader
|
||||
@@ -168,6 +265,8 @@ class ClassLoader
|
||||
|
||||
public function findFile($class)
|
||||
{
|
||||
$fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR;
|
||||
|
||||
if (__NAMESPACE__.'\TestingUnsilencing' === $class) {
|
||||
eval('-- parse error --');
|
||||
} elseif (__NAMESPACE__.'\TestingStacking' === $class) {
|
||||
@@ -175,13 +274,23 @@ class ClassLoader
|
||||
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
|
||||
eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
|
||||
} elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) {
|
||||
return __DIR__.'/Fixtures/CaseMismatch.php';
|
||||
return $fixtureDir.'CaseMismatch.php';
|
||||
} elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
|
||||
return __DIR__.'/Fixtures/psr4/Psr4CaseMismatch.php';
|
||||
return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
|
||||
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
|
||||
return __DIR__.'/Fixtures/reallyNotPsr0.php';
|
||||
return $fixtureDir.'reallyNotPsr0.php';
|
||||
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
|
||||
return __DIR__.'/Fixtures/notPsr0Bis.php';
|
||||
return $fixtureDir.'notPsr0Bis.php';
|
||||
} elseif (__NAMESPACE__.'\Fixtures\DeprecatedInterface' === $class) {
|
||||
return $fixtureDir.'DeprecatedInterface.php';
|
||||
} elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) {
|
||||
eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertEquals(E_NOTICE, $exception->getSeverity());
|
||||
$this->assertEquals(__FILE__, $exception->getFile());
|
||||
$this->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
|
||||
$this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
|
||||
$this->assertArrayHasKey('foobar', $exception->getContext());
|
||||
|
||||
$trace = $exception->getTrace();
|
||||
@@ -141,19 +141,19 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$loggers = array(
|
||||
E_DEPRECATED => array(null, LogLevel::INFO),
|
||||
E_USER_DEPRECATED => array(null, LogLevel::INFO),
|
||||
E_NOTICE => array($logger, LogLevel::NOTICE),
|
||||
E_NOTICE => array($logger, LogLevel::WARNING),
|
||||
E_USER_NOTICE => array($logger, LogLevel::CRITICAL),
|
||||
E_STRICT => array(null, LogLevel::NOTICE),
|
||||
E_STRICT => array(null, LogLevel::WARNING),
|
||||
E_WARNING => array(null, LogLevel::WARNING),
|
||||
E_USER_WARNING => array(null, LogLevel::WARNING),
|
||||
E_COMPILE_WARNING => array(null, LogLevel::WARNING),
|
||||
E_CORE_WARNING => array(null, LogLevel::WARNING),
|
||||
E_USER_ERROR => array(null, LogLevel::ERROR),
|
||||
E_RECOVERABLE_ERROR => array(null, LogLevel::ERROR),
|
||||
E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY),
|
||||
E_PARSE => array(null, LogLevel::EMERGENCY),
|
||||
E_ERROR => array(null, LogLevel::EMERGENCY),
|
||||
E_CORE_ERROR => array(null, LogLevel::EMERGENCY),
|
||||
E_USER_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_PARSE => array(null, LogLevel::CRITICAL),
|
||||
E_ERROR => array(null, LogLevel::CRITICAL),
|
||||
E_CORE_ERROR => array(null, LogLevel::CRITICAL),
|
||||
);
|
||||
$this->assertSame($loggers, $handler->setLoggers(array()));
|
||||
|
||||
@@ -169,8 +169,6 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testHandleError()
|
||||
{
|
||||
$this->iniSet('error_reporting', -1);
|
||||
|
||||
try {
|
||||
$handler = ErrorHandler::register();
|
||||
$handler->throwAt(0, true);
|
||||
@@ -270,6 +268,28 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testHandleDeprecation()
|
||||
{
|
||||
$that = $this;
|
||||
$logArgCheck = function ($level, $message, $context) use ($that) {
|
||||
$that->assertEquals(LogLevel::INFO, $level);
|
||||
$that->assertArrayHasKey('level', $context);
|
||||
$that->assertEquals(E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED, $context['level']);
|
||||
$that->assertArrayHasKey('stack', $context);
|
||||
};
|
||||
|
||||
$logger = $this->getMock('Psr\Log\LoggerInterface');
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('log')
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
;
|
||||
|
||||
$handler = new ErrorHandler();
|
||||
$handler->setDefaultLogger($logger);
|
||||
@$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array());
|
||||
}
|
||||
|
||||
public function testHandleException()
|
||||
{
|
||||
try {
|
||||
@@ -318,6 +338,40 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testErrorStacking()
|
||||
{
|
||||
try {
|
||||
$handler = ErrorHandler::register();
|
||||
$handler->screamAt(E_USER_WARNING);
|
||||
|
||||
$logger = $this->getMock('Psr\Log\LoggerInterface');
|
||||
|
||||
$logger
|
||||
->expects($this->exactly(2))
|
||||
->method('log')
|
||||
->withConsecutive(
|
||||
array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')),
|
||||
array($this->equalTo(LogLevel::DEBUG), $this->equalTo('Silenced warning'))
|
||||
)
|
||||
;
|
||||
|
||||
$handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING));
|
||||
|
||||
ErrorHandler::stackErrors();
|
||||
@trigger_error('Silenced warning', E_USER_WARNING);
|
||||
$logger->log(LogLevel::WARNING, 'Dummy log');
|
||||
ErrorHandler::unstackErrors();
|
||||
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
} catch (\Exception $e) {
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function testHandleFatalError()
|
||||
{
|
||||
try {
|
||||
@@ -336,7 +390,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
|
||||
@@ -345,7 +399,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
;
|
||||
|
||||
$handler->setDefaultLogger($logger, E_ERROR);
|
||||
$handler->setDefaultLogger($logger, E_PARSE);
|
||||
|
||||
$handler->handleFatalError($error);
|
||||
|
||||
@@ -359,10 +413,57 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testHandleFatalErrorOnHHVM()
|
||||
{
|
||||
try {
|
||||
$handler = ErrorHandler::register();
|
||||
|
||||
$logger = $this->getMock('Psr\Log\LoggerInterface');
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('log')
|
||||
->with(
|
||||
$this->equalTo(LogLevel::CRITICAL),
|
||||
$this->equalTo('Fatal Error: foo'),
|
||||
$this->equalTo(array(
|
||||
'type' => 1,
|
||||
'file' => 'bar',
|
||||
'line' => 123,
|
||||
'level' => -1,
|
||||
'stack' => array(456),
|
||||
))
|
||||
)
|
||||
;
|
||||
|
||||
$handler->setDefaultLogger($logger, E_ERROR);
|
||||
|
||||
$error = array(
|
||||
'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors
|
||||
'message' => 'foo',
|
||||
'file' => 'bar',
|
||||
'line' => 123,
|
||||
'context' => array(123),
|
||||
'backtrace' => array(456),
|
||||
);
|
||||
|
||||
call_user_func_array(array($handler, 'handleError'), $error);
|
||||
$handler->handleFatalError($error);
|
||||
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
} catch (\Exception $e) {
|
||||
restore_error_handler();
|
||||
restore_exception_handler();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()));
|
||||
@@ -386,7 +487,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
;
|
||||
|
||||
$handler = ErrorHandler::register(E_NOTICE);
|
||||
$handler->setLogger($logger, 'scream');
|
||||
@$handler->setLogger($logger, 'scream');
|
||||
unset($undefVar);
|
||||
@$undefVar++;
|
||||
|
||||
|
||||
@@ -131,6 +131,20 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame(array($flattened2), $flattened->getAllPrevious());
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 7.0
|
||||
*/
|
||||
public function testPreviousError()
|
||||
{
|
||||
$exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42));
|
||||
|
||||
$flattened = FlattenException::create($exception)->getPrevious();
|
||||
|
||||
$this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.');
|
||||
$this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.');
|
||||
$this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider flattenDataProvider
|
||||
*/
|
||||
@@ -189,9 +203,9 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
|
||||
public function testTooBigArray()
|
||||
{
|
||||
$a = array();
|
||||
for ($i = 0; $i < 20; $i++) {
|
||||
for ($j = 0; $j < 50; $j++) {
|
||||
for ($k = 0; $k < 10; $k++) {
|
||||
for ($i = 0; $i < 20; ++$i) {
|
||||
for ($j = 0; $j < 50; ++$j) {
|
||||
for ($k = 0; $k < 10; ++$k) {
|
||||
$a[$i][$j][$k] = 'value';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,71 +13,97 @@ namespace Symfony\Component\Debug\Tests;
|
||||
|
||||
use Symfony\Component\Debug\ExceptionHandler;
|
||||
use Symfony\Component\Debug\Exception\OutOfMemoryException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
|
||||
require_once __DIR__.'/HeaderMock.php';
|
||||
|
||||
class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
testHeader();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
testHeader();
|
||||
}
|
||||
|
||||
public function testDebug()
|
||||
{
|
||||
$handler = new ExceptionHandler(false);
|
||||
$response = $handler->createResponse(new \RuntimeException('Foo'));
|
||||
|
||||
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent());
|
||||
$this->assertNotContains('<h2 class="block_exception clear_fix">', $response->getContent());
|
||||
ob_start();
|
||||
$handler->sendPhpResponse(new \RuntimeException('Foo'));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response);
|
||||
$this->assertNotContains('<h2 class="block_exception clear_fix">', $response);
|
||||
|
||||
$handler = new ExceptionHandler(true);
|
||||
$response = $handler->createResponse(new \RuntimeException('Foo'));
|
||||
|
||||
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent());
|
||||
$this->assertContains('<h2 class="block_exception clear_fix">', $response->getContent());
|
||||
ob_start();
|
||||
$handler->sendPhpResponse(new \RuntimeException('Foo'));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response);
|
||||
$this->assertContains('<h2 class="block_exception clear_fix">', $response);
|
||||
}
|
||||
|
||||
public function testStatusCode()
|
||||
{
|
||||
$handler = new ExceptionHandler(false);
|
||||
$handler = new ExceptionHandler(false, 'iso8859-1');
|
||||
|
||||
$response = $handler->createResponse(new \RuntimeException('Foo'));
|
||||
$this->assertEquals('500', $response->getStatusCode());
|
||||
$this->assertContains('Whoops, looks like something went wrong.', $response->getContent());
|
||||
ob_start();
|
||||
$handler->sendPhpResponse(new NotFoundHttpException('Foo'));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$response = $handler->createResponse(new NotFoundHttpException('Foo'));
|
||||
$this->assertEquals('404', $response->getStatusCode());
|
||||
$this->assertContains('Sorry, the page you are looking for could not be found.', $response->getContent());
|
||||
$this->assertContains('Sorry, the page you are looking for could not be found.', $response);
|
||||
|
||||
$expectedHeaders = array(
|
||||
array('HTTP/1.0 404', true, null),
|
||||
array('Content-Type: text/html; charset=iso8859-1', true, null),
|
||||
);
|
||||
|
||||
$this->assertSame($expectedHeaders, testHeader());
|
||||
}
|
||||
|
||||
public function testHeaders()
|
||||
{
|
||||
$handler = new ExceptionHandler(false);
|
||||
$handler = new ExceptionHandler(false, 'iso8859-1');
|
||||
|
||||
$response = $handler->createResponse(new MethodNotAllowedHttpException(array('POST')));
|
||||
$this->assertEquals('405', $response->getStatusCode());
|
||||
$this->assertEquals('POST', $response->headers->get('Allow'));
|
||||
ob_start();
|
||||
$handler->sendPhpResponse(new MethodNotAllowedHttpException(array('POST')));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$expectedHeaders = array(
|
||||
array('HTTP/1.0 405', true, null),
|
||||
array('Allow: POST', false, null),
|
||||
array('Content-Type: text/html; charset=iso8859-1', true, null),
|
||||
);
|
||||
|
||||
$this->assertSame($expectedHeaders, testHeader());
|
||||
}
|
||||
|
||||
public function testNestedExceptions()
|
||||
{
|
||||
$handler = new ExceptionHandler(true);
|
||||
$response = $handler->createResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar')));
|
||||
ob_start();
|
||||
$handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar')));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$this->assertStringMatchesFormat('%A<span class="exception_message">Foo</span>%A<span class="exception_message">Bar</span>%A', $response);
|
||||
}
|
||||
|
||||
public function testHandle()
|
||||
{
|
||||
$exception = new \Exception('foo');
|
||||
|
||||
if (class_exists('Symfony\Component\HttpFoundation\Response')) {
|
||||
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('createResponse'));
|
||||
$handler
|
||||
->expects($this->exactly(2))
|
||||
->method('createResponse')
|
||||
->will($this->returnValue(new Response()));
|
||||
} else {
|
||||
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
|
||||
$handler
|
||||
->expects($this->exactly(2))
|
||||
->method('sendPhpResponse');
|
||||
}
|
||||
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
|
||||
$handler
|
||||
->expects($this->exactly(2))
|
||||
->method('sendPhpResponse');
|
||||
|
||||
$handler->handle($exception);
|
||||
|
||||
@@ -93,18 +119,10 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__);
|
||||
|
||||
if (class_exists('Symfony\Component\HttpFoundation\Response')) {
|
||||
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('createResponse'));
|
||||
$handler
|
||||
->expects($this->once())
|
||||
->method('createResponse')
|
||||
->will($this->returnValue(new Response()));
|
||||
} else {
|
||||
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
|
||||
$handler
|
||||
->expects($this->once())
|
||||
->method('sendPhpResponse');
|
||||
}
|
||||
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
|
||||
$handler
|
||||
->expects($this->once())
|
||||
->method('sendPhpResponse');
|
||||
|
||||
$that = $this;
|
||||
$handler->setHandler(function ($e) use ($that) {
|
||||
|
||||
@@ -15,19 +15,53 @@ 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 testHandleClassNotFound($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']));
|
||||
|
||||
$this->assertInstanceof('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
|
||||
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());
|
||||
$this->assertSame($error['file'], $exception->getFile());
|
||||
@@ -35,34 +69,35 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideLegacyClassNotFoundData
|
||||
* @group legacy
|
||||
*/
|
||||
public function testLegacyHandleClassNotFound($error, $translatedMessage, $autoloader)
|
||||
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);
|
||||
|
||||
// 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']));
|
||||
|
||||
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());
|
||||
$this->assertSame($error['file'], $exception->getFile());
|
||||
$this->assertSame($error['line'], $exception->getLine());
|
||||
$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(
|
||||
@@ -109,20 +144,6 @@ 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\"?",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function provideLegacyClassNotFoundData()
|
||||
{
|
||||
$prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception'));
|
||||
|
||||
$symfonyAutoloader = new SymfonyClassLoader();
|
||||
$symfonyAutoloader->addPrefixes($prefixes);
|
||||
|
||||
$symfonyUniversalClassLoader = new SymfonyUniversalClassLoader();
|
||||
$symfonyUniversalClassLoader->registerPrefixes($prefixes);
|
||||
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
'type' => 1,
|
||||
@@ -141,7 +162,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
'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'),
|
||||
array($debugClassLoader, 'loadClass'),
|
||||
),
|
||||
array(
|
||||
array(
|
||||
@@ -158,11 +179,11 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
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,
|
||||
@@ -174,6 +195,6 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$handler = new ClassNotFoundFatalErrorHandler();
|
||||
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
|
||||
|
||||
$this->assertInstanceof('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
|
||||
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$handler = new UndefinedFunctionFatalErrorHandler();
|
||||
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
|
||||
|
||||
$this->assertInstanceof('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception);
|
||||
$this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception);
|
||||
// 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());
|
||||
|
||||
@@ -24,7 +24,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$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->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception);
|
||||
$this->assertSame($translatedMessage, $exception->getMessage());
|
||||
$this->assertSame($error['type'], $exception->getSeverity());
|
||||
$this->assertSame($error['file'], $exception->getFile());
|
||||
@@ -41,7 +41,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Call to undefined method SplObjectStorage::what()',
|
||||
),
|
||||
'Attempted to call method "what" on class "SplObjectStorage".',
|
||||
'Attempted to call an undefined method named "what" of class "SplObjectStorage".',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
@@ -50,7 +50,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Call to undefined method SplObjectStorage::walid()',
|
||||
),
|
||||
"Attempted to call method \"walid\" on class \"SplObjectStorage\".\nDid you mean to call \"valid\"?",
|
||||
"Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?",
|
||||
),
|
||||
array(
|
||||
array(
|
||||
@@ -59,7 +59,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Call to undefined method SplObjectStorage::offsetFet()',
|
||||
),
|
||||
"Attempted to call method \"offsetFet\" on class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?",
|
||||
"Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
12
Tests/Fixtures/DeprecatedClass.php
Normal file
12
Tests/Fixtures/DeprecatedClass.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @deprecated but this is a test
|
||||
* deprecation notice.
|
||||
* @foobar
|
||||
*/
|
||||
class DeprecatedClass
|
||||
{
|
||||
}
|
||||
12
Tests/Fixtures/DeprecatedInterface.php
Normal file
12
Tests/Fixtures/DeprecatedInterface.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @deprecated but this is a test
|
||||
* deprecation notice.
|
||||
* @foobar
|
||||
*/
|
||||
interface DeprecatedInterface
|
||||
{
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
38
Tests/HeaderMock.php
Normal file
38
Tests/HeaderMock.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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;
|
||||
|
||||
function headers_sent()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function header($str, $replace = true, $status = null)
|
||||
{
|
||||
Tests\testHeader($str, $replace, $status);
|
||||
}
|
||||
|
||||
namespace Symfony\Component\Debug\Tests;
|
||||
|
||||
function testHeader()
|
||||
{
|
||||
static $headers = array();
|
||||
|
||||
if (!$h = func_get_args()) {
|
||||
$h = $headers;
|
||||
$headers = array();
|
||||
|
||||
return $h;
|
||||
}
|
||||
|
||||
$headers[] = func_get_args();
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"type": "library",
|
||||
"description": "Symfony Debug Component",
|
||||
"keywords": [],
|
||||
"homepage": "http://symfony.com",
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
@@ -12,11 +12,11 @@
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"php": ">=5.3.9",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
@@ -24,21 +24,18 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"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": {
|
||||
"symfony/http-foundation": "",
|
||||
"symfony/http-kernel": ""
|
||||
"symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Symfony\\Component\\Debug\\": "" }
|
||||
"psr-4": { "Symfony\\Component\\Debug\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"target-dir": "Symfony/Component/Debug",
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
"dev-master": "2.7-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<php>
|
||||
<!-- Silence E_USER_DEPRECATED (-16385 == -1 & ~E_USER_DEPRECATED) -->
|
||||
<ini name="error_reporting" value="-16385"/>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony Debug Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Symfony Debug Extension Test Suite">
|
||||
<directory suffix=".phpt">./Resources/ext/tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
|
||||
Reference in New Issue
Block a user