Compare commits

..

21 Commits

Author SHA1 Message Date
Nicolas Grekas
f45a908634 [Debug] Remove false-positive check in DebugClassLoader 2017-08-27 10:27:28 +02:00
Nicolas Grekas
0946243775 bug #23684 [Debug] Missing escape in debug output (c960657)
This PR was merged into the 2.7 branch.

Discussion
----------

[Debug] Missing escape in debug output

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

When pretty-printing an exception, the debug handler does not properly escape array keys.

The problem only occurs when debug output is enabled, so this is not considered a [security issue](http://symfony.com/doc/current/contributing/code/security.html) (according to @fabpot), because the debug tools [should not be used in production](https://symfony.com/doc/current/components/debug.html#usage).

A test for this is included in my patch for #18722.

Commits
-------

636777d [Debug] HTML-escape array key
2017-07-26 22:00:18 +02:00
Christian Schmidt
e1ce6a4ded [Debug] HTML-escape array key 2017-07-26 17:00:11 +02:00
Fabien Potencier
2662c21dea Using FQ name for PHP_VERSION_ID 2017-06-01 13:44:56 -07:00
Nicolas Grekas
28590cbb8f [Debug] Set exit status to 255 on error 2017-04-13 22:03:51 +02:00
Christian Flothmann
102a9b2776 fail when detecting risky tests 2017-04-12 09:39:27 +02:00
Nicolas Grekas
f302a96911 bug #22307 [Debug] Fix php notice (enumag)
This PR was submitted for the 2.8 branch but it was merged into the 2.7 branch instead (closes #22307).

Discussion
----------

[Debug] Fix php notice

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

Of course autoloading of an empty string should not actually happen (fixed that in https://github.com/twigphp/Twig/pull/2438) but if it does happen it should not throw a php notice.

```
Notice: Uninitialized string offset 0
```

Commits
-------

e333a1a [Debug] Fix php notice
2017-04-07 14:20:28 +02:00
Jáchym Toušek
25f994cdf8 [Debug] Fix php notice 2017-04-07 14:16:45 +02:00
Fabien Potencier
ae2d80df72 fixed Composer constraints 2017-02-18 10:28:08 -08:00
Peter Rehm
0d4887956a Update to PHPUnit namespaces 2017-02-18 08:02:39 -08:00
Nicolas Grekas
4f9612ed86 [Debug] Workaround "null" $context 2017-01-25 13:11:45 +01:00
Nicolas Grekas
0ccbc5c4ca [Debug] Remove $context arg from handleError(), preparing for PHP 7.2 2017-01-24 16:28:42 +01:00
Fabien Potencier
bdc57ddfb5 Revert "fixed typo"
This reverts commit 6830d9f4c904d74020d2619fae990670edb3c3de.
2017-01-21 08:37:26 -08:00
Fabien Potencier
a4bf3f9ca9 fixed typo 2017-01-21 08:33:48 -08:00
Fabien Potencier
637babfcdc fixed CS 2017-01-20 08:54:19 -08:00
Fabien Potencier
b3bdc191eb updated LICENSE year 2017-01-02 12:30:00 -08:00
Grégoire Pineau
2f9a68243d [Debug] Wrap call to ->log in a try catch block
If something goes wrong in the logger, the application ends up
with a blank page. Let's display the original exception.
2016-12-27 15:50:31 +01:00
Nicolas Grekas
3717db3bfa bug #21010 [Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes (SpacePossum)
This PR was squashed before being merged into the 2.7 branch (closes #21010).

Discussion
----------

[Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| License       | MIT

When trying to call a method on an anonymous class (with or without methods) the `UndefinedMethodFatalErrorHandler` tries to iterate `null`.

For ref.: https://3v4l.org/l26u1

Commits
-------

ed713ae [Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes
2016-12-27 14:43:30 +01:00
SpacePossum
62a045d4cc [Debug] UndefinedMethodFatalErrorHandler - Handle anonymous classes 2016-12-27 14:43:27 +01:00
Fabien Potencier
83059abb7c fixed @return when returning this or static 2016-12-27 08:23:47 +01:00
Fabien Potencier
9544c5518f fixed obsolete getMock() usage 2016-12-19 16:22:46 +01:00
21 changed files with 127 additions and 339 deletions

View File

@@ -1,37 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Debug;
use Psr\Log\AbstractLogger;
/**
* A buffering logger that stacks logs for later.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class BufferingLogger extends AbstractLogger
{
private $logs = array();
public function log($level, $message, array $context = array())
{
$this->logs[] = array($level, $message, $context);
}
public function cleanLogs()
{
$logs = $this->logs;
$this->logs = array();
return $logs;
}
}

View File

@@ -1,13 +1,6 @@
CHANGELOG
=========
2.8.0
-----
* added BufferingLogger for errors that happen before a proper logger is configured
* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);`
* deprecate ExceptionHandler::createResponse
2.7.0
-----

View File

@@ -52,10 +52,9 @@ class Debug
// CLI - display errors only if they're not already logged to STDERR
ini_set('display_errors', 1);
}
if ($displayErrors) {
ErrorHandler::register(new ErrorHandler(new BufferingLogger()));
} else {
ErrorHandler::register()->throwAt(0, true);
$handler = ErrorHandler::register();
if (!$displayErrors) {
$handler->throwAt(0, true);
}
DebugClassLoader::enable();

View File

@@ -26,6 +26,7 @@ class DebugClassLoader
{
private $classLoader;
private $isFinder;
private $loaded = array();
private $wasFinder;
private static $caseCheck;
private static $deprecated = array();
@@ -164,9 +165,10 @@ class DebugClassLoader
ErrorHandler::stackErrors();
try {
if ($this->isFinder) {
if ($this->isFinder && !isset($this->loaded[$class])) {
$this->loaded[$class] = true;
if ($file = $this->classLoader[0]->findFile($class)) {
require_once $file;
require $file;
}
} else {
call_user_func($this->classLoader, $class);
@@ -186,7 +188,7 @@ class DebugClassLoader
$exists = class_exists($class, false) || interface_exists($class, false) || (function_exists('trait_exists') && trait_exists($class, false));
if ('\\' === $class[0]) {
if ($class && '\\' === $class[0]) {
$class = substr($class, 1);
}
@@ -223,26 +225,9 @@ class DebugClassLoader
@trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED);
}
$parentInterfaces = array();
$deprecatedInterfaces = array();
if ($parent) {
foreach (class_implements($parent) as $interface) {
$parentInterfaces[$interface] = 1;
}
}
foreach ($refl->getInterfaceNames() as $interface) {
if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) {
$deprecatedInterfaces[] = $interface;
}
foreach (class_implements($interface) as $interface) {
$parentInterfaces[$interface] = 1;
}
}
foreach ($deprecatedInterfaces as $interface) {
if (!isset($parentInterfaces[$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);
foreach (class_implements($class) as $interface) {
if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len) && !is_subclass_of($parent, $interface)) {
@trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, interface_exists($class) ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED);
}
}
}

View File

@@ -97,12 +97,11 @@ class ErrorHandler
private $isRecursive = 0;
private $isRoot = false;
private $exceptionHandler;
private $bootstrappingLogger;
private static $reservedMemory;
private static $stackedErrors = array();
private static $stackedErrorLevels = array();
private static $toStringException = null;
private static $exitCode = 0;
/**
* Same init value as thrownErrors.
@@ -159,14 +158,6 @@ class ErrorHandler
return $handler;
}
public function __construct(BufferingLogger $bootstrappingLogger = null)
{
if ($bootstrappingLogger) {
$this->bootstrappingLogger = $bootstrappingLogger;
$this->setDefaultLogger($bootstrappingLogger);
}
}
/**
* Sets a logger to non assigned errors levels.
*
@@ -180,7 +171,7 @@ class ErrorHandler
if (is_array($levels)) {
foreach ($levels as $type => $logLevel) {
if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
if (empty($this->loggers[$type][0]) || $replace) {
$loggers[$type] = array($logger, $logLevel);
}
}
@@ -189,7 +180,7 @@ class ErrorHandler
$levels = E_ALL | E_STRICT;
}
foreach ($this->loggers as $type => $log) {
if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
if (($type & $levels) && (empty($log[0]) || $replace)) {
$log[0] = $logger;
$loggers[$type] = $log;
}
@@ -212,7 +203,6 @@ class ErrorHandler
{
$prevLogged = $this->loggedErrors;
$prev = $this->loggers;
$flush = array();
foreach ($loggers as $type => $log) {
if (!isset($prev[$type])) {
@@ -231,24 +221,9 @@ class ErrorHandler
throw new \InvalidArgumentException('Invalid logger provided');
}
$this->loggers[$type] = $log + $prev[$type];
if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
$flush[$type] = $type;
}
}
$this->reRegister($prevLogged | $this->thrownErrors);
if ($flush) {
foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
$type = $log[2]['type'];
if (!isset($flush[$type])) {
$this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
} elseif ($this->loggers[$type][0]) {
$this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
}
}
}
return $prev;
}
@@ -375,12 +350,10 @@ class ErrorHandler
/**
* Handles errors by filtering then logging them according to the configured bit fields.
*
* @param int $type One of the E_* constants
* @param int $type One of the E_* constants
* @param string $message
* @param string $file
* @param int $line
* @param array $context
* @param array $backtrace
*
* @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
*
@@ -388,7 +361,7 @@ class ErrorHandler
*
* @internal
*/
public function handleError($type, $message, $file, $line, array $context, array $backtrace = null)
public function handleError($type, $message, $file, $line)
{
$level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
$log = $this->loggedErrors & $type;
@@ -398,8 +371,17 @@ class ErrorHandler
if (!$type || (!$log && !$throw)) {
return $type && $log;
}
$scope = $this->scopedErrors & $type;
if (isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
if (4 < $numArgs = func_num_args()) {
$context = $scope ? (func_get_arg(4) ?: array()) : array();
$backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM
} else {
$context = array();
$backtrace = null;
}
if (isset($context['GLOBALS']) && $scope) {
$e = $context; // Whatever the signature of the method,
unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
$context = $e;
@@ -415,17 +397,14 @@ class ErrorHandler
}
if ($throw) {
if (null !== self::$toStringException) {
$throw = self::$toStringException;
self::$toStringException = null;
} elseif (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
if ($scope && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
// Checking for class existence is a work around for https://bugs.php.net/42098
$throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
} else {
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
}
if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
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.
@@ -433,47 +412,6 @@ class ErrorHandler
$throw->errorHandlerCanary = new ErrorHandlerCanary();
}
if (E_USER_ERROR & $type) {
$backtrace = $backtrace ?: $throw->getTrace();
for ($i = 1; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
&& '__toString' === $backtrace[$i]['function']
&& '->' === $backtrace[$i]['type']
&& !isset($backtrace[$i - 1]['class'])
&& ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
) {
// Here, we know trigger_error() has been called from __toString().
// HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead.
// A small convention allows working around the limitation:
// given a caught $e exception in __toString(), quitting the method with
// `return trigger_error($e, E_USER_ERROR);` allows this error handler
// to make $e get through the __toString() barrier.
foreach ($context as $e) {
if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) {
if (1 === $i) {
// On HHVM
$throw = $e;
break;
}
self::$toStringException = $e;
return true;
}
}
if (1 < $i) {
// On PHP (not on HHVM), display the original error message instead of the default one.
$this->handleException($throw);
// Stop the process by giving back the error to the native handler.
return false;
}
}
}
}
throw $throw;
}
@@ -490,7 +428,7 @@ class ErrorHandler
$e = compact('type', 'file', 'line', 'level');
if ($type & $level) {
if ($this->scopedErrors & $type) {
if ($scope) {
$e['scope_vars'] = $context;
if ($trace) {
$e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
@@ -540,6 +478,9 @@ class ErrorHandler
*/
public function handleException($exception, array $error = null)
{
if (null === $error) {
self::$exitCode = 255;
}
if (!$exception instanceof \Exception) {
$exception = new FatalThrowableError($exception);
}
@@ -574,7 +515,11 @@ class ErrorHandler
}
}
if ($this->loggedErrors & $type) {
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
try {
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
} catch (\Exception $handlerException) {
} catch (\Throwable $handlerException) {
}
}
if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
foreach ($this->getFatalErrorHandlers() as $handler) {
@@ -621,7 +566,7 @@ class ErrorHandler
return;
}
if (null === $error) {
if ($exit = null === $error) {
$error = error_get_last();
}
@@ -645,15 +590,21 @@ class ErrorHandler
} 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);
if (isset($exception)) {
self::$exitCode = 255;
$handler->handleException($exception, $error);
}
} catch (FatalErrorException $e) {
// Ignore this re-throw
}
if ($exit && self::$exitCode) {
$exitCode = self::$exitCode;
register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
}
}
/**

View File

@@ -39,8 +39,6 @@ class ExceptionHandler
public function __construct($debug = true, $charset = null, $fileLinkFormat = null)
{
if (false !== strpos($charset, '%')) {
@trigger_error('Providing $fileLinkFormat as second argument to '.__METHOD__.' is deprecated since version 2.8 and will be unsupported in 3.0. Please provide it as third argument, after $charset.', E_USER_DEPRECATED);
// Swap $charset and $fileLinkFormat for BC reasons
$pivot = $fileLinkFormat;
$fileLinkFormat = $charset;
@@ -58,7 +56,7 @@ class ExceptionHandler
* @param string|null $charset The charset used by exception messages
* @param string|null $fileLinkFormat The IDE link template
*
* @return ExceptionHandler The registered exception handler
* @return static
*/
public static function register($debug = true, $charset = null, $fileLinkFormat = null)
{
@@ -166,7 +164,6 @@ class ExceptionHandler
$response = $this->createResponse($exception);
$response->sendHeaders();
$response->sendContent();
@trigger_error(sprintf("The %s::createResponse method is deprecated since 2.8 and won't be called anymore when handling an exception in 3.0.", $reflector->class), E_USER_DEPRECATED);
return;
}
@@ -180,7 +177,7 @@ class ExceptionHandler
* This method uses plain PHP functions like header() and echo to output
* the response.
*
* @param \Exception|FlattenException $exception An \Exception or FlattenException instance
* @param \Exception|FlattenException $exception An \Exception instance
*/
public function sendPhpResponse($exception)
{
@@ -202,37 +199,17 @@ class ExceptionHandler
/**
* Creates the error Response associated with the given Exception.
*
* @param \Exception|FlattenException $exception An \Exception or FlattenException instance
* @param \Exception|FlattenException $exception An \Exception instance
*
* @return Response A Response instance
*
* @deprecated since 2.8, to be removed in 3.0.
*/
public function createResponse($exception)
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
if (!$exception instanceof FlattenException) {
$exception = FlattenException::create($exception);
}
return Response::create($this->getHtml($exception), $exception->getStatusCode(), $exception->getHeaders())->setCharset($this->charset);
}
/**
* Gets the full HTML content associated with the given exception.
*
* @param \Exception|FlattenException $exception An \Exception or FlattenException instance
*
* @return string The HTML content as a string
*/
public function getHtml($exception)
{
if (!$exception instanceof FlattenException) {
$exception = FlattenException::create($exception);
}
return $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
return Response::create($this->decorate($this->getContent($exception), $this->getStylesheet($exception)), $exception->getStatusCode(), $exception->getHeaders())->setCharset($this->charset);
}
/**
@@ -442,7 +419,7 @@ EOF;
$formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true));
}
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escapeHtml($key), $formattedValue);
}
return implode(', ', $result);
@@ -457,7 +434,7 @@ EOF;
{
@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');
return htmlspecialchars($str, ENT_QUOTES | (\PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), 'UTF-8');
}
/**
@@ -465,7 +442,7 @@ EOF;
*/
private function escapeHtml($str)
{
return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), $this->charset);
return htmlspecialchars($str, ENT_QUOTES | (\PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), $this->charset);
}
/**

View File

@@ -179,7 +179,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
);
if ($prefix) {
$candidates = array_filter($candidates, function ($candidate) use ($prefix) {return 0 === strpos($candidate, $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

View File

@@ -36,8 +36,13 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
$message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className);
if (!class_exists($className) || null === $methods = get_class_methods($className)) {
// failed to get the class or its methods on which an unknown method was called (for example on an anonymous class)
return new UndefinedMethodException($message, $exception);
}
$candidates = array();
foreach (get_class_methods($className) as $definedMethodName) {
foreach ($methods as $definedMethodName) {
$lev = levenshtein($methodName, $definedMethodName);
if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) {
$candidates[] = $definedMethodName;
@@ -52,6 +57,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
} else {
$candidates = '"'.$last;
}
$message .= "\nDid you mean to call ".$candidates;
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2016 Fabien Potencier
Copyright (c) 2004-2017 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Debug\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\Debug\ErrorHandler;
class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
class DebugClassLoaderTest extends TestCase
{
/**
* @var int Error reporting level before running tests
@@ -58,9 +59,26 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
$this->fail('DebugClassLoader did not register');
}
/**
* @expectedException \Exception
* @expectedExceptionMessage boo
*/
public function testThrowingClass()
{
try {
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
$this->fail('Exception expected');
} catch (\Exception $e) {
$this->assertSame('boo', $e->getMessage());
}
// the second call also should throw
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
}
public function testUnsilencing()
{
if (PHP_VERSION_ID >= 70000) {
if (\PHP_VERSION_ID >= 70000) {
$this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.');
}
if (defined('HHVM_VERSION')) {
@@ -110,7 +128,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
restore_error_handler();
restore_exception_handler();
$this->assertStringStartsWith(__FILE__, $exception->getFile());
if (PHP_VERSION_ID < 70000) {
if (\PHP_VERSION_ID < 70000) {
$this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage());
$this->assertEquals(E_STRICT, $exception->getSeverity());
} else {
@@ -127,6 +145,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Case mismatch between loaded and declared class names
*/
public function testNameCaseMismatch()
{
@@ -148,6 +167,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Case mismatch between loaded and declared class names
*/
public function testPsr4CaseMismatch()
{
@@ -202,28 +222,6 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
);
}
public function testInterfaceExtendsDeprecatedInterface()
{
set_error_handler(function () { return false; });
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', 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 testDeprecatedSuperInSameNamespace()
{
set_error_handler(function () { return false; });
@@ -248,7 +246,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
public function testReservedForPhp7()
{
if (PHP_VERSION_ID >= 70000) {
if (\PHP_VERSION_ID >= 70000) {
$this->markTestSkipped('PHP7 already prevents using reserved names.');
}
@@ -310,8 +308,6 @@ class ClassLoader
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__.'\NonDeprecatedInterfaceClass' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}');
} elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
}

View File

@@ -11,9 +11,9 @@
namespace Symfony\Component\Debug\Tests;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\BufferingLogger;
use Symfony\Component\Debug\Exception\ContextErrorException;
/**
@@ -22,7 +22,7 @@ use Symfony\Component\Debug\Exception\ContextErrorException;
* @author Robert Schönthal <seroscho@googlemail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
class ErrorHandlerTest extends TestCase
{
public function testRegister()
{
@@ -134,7 +134,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
try {
$handler = ErrorHandler::register();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL));
@@ -213,7 +213,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
restore_error_handler();
restore_exception_handler();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$that = $this;
$warnArgCheck = function ($logLevel, $message, $context) use ($that) {
@@ -238,7 +238,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
restore_error_handler();
restore_exception_handler();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
@@ -269,33 +269,6 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
}
}
public function testHandleUserError()
{
try {
$handler = ErrorHandler::register();
$handler->throwAt(0, true);
$e = null;
$x = new \Exception('Foo');
try {
$f = new Fixtures\ToStringThrower($x);
$f .= ''; // Trigger $f->__toString()
} catch (\Exception $e) {
}
$this->assertSame($x, $e);
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testHandleDeprecation()
{
$that = $this;
@@ -306,7 +279,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$that->assertArrayHasKey('stack', $context);
};
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger
->expects($this->once())
->method('log')
@@ -325,7 +298,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$exception = new \Exception('foo');
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
@@ -372,7 +345,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$handler = ErrorHandler::register();
$handler->screamAt(E_USER_WARNING);
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger
->expects($this->exactly(2))
@@ -400,49 +373,6 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
}
}
public function testBootstrappingLogger()
{
$bootLogger = new BufferingLogger();
$handler = new ErrorHandler($bootLogger);
$loggers = array(
E_DEPRECATED => array($bootLogger, LogLevel::INFO),
E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO),
E_NOTICE => array($bootLogger, LogLevel::WARNING),
E_USER_NOTICE => array($bootLogger, LogLevel::WARNING),
E_STRICT => array($bootLogger, LogLevel::WARNING),
E_WARNING => array($bootLogger, LogLevel::WARNING),
E_USER_WARNING => array($bootLogger, LogLevel::WARNING),
E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING),
E_CORE_WARNING => array($bootLogger, LogLevel::WARNING),
E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_PARSE => array($bootLogger, LogLevel::CRITICAL),
E_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL),
);
$this->assertSame($loggers, $handler->setLoggers(array()));
$handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array());
$expectedLog = array(LogLevel::INFO, 'Foo message', array('type' => E_DEPRECATED, 'file' => __FILE__, 'line' => 123, 'level' => error_reporting()));
$logs = $bootLogger->cleanLogs();
unset($logs[0][2]['stack']);
$this->assertSame(array($expectedLog), $logs);
$bootLogger->log($expectedLog[0], $expectedLog[1], $expectedLog[2]);
$mockLogger = $this->getMock('Psr\Log\LoggerInterface');
$mockLogger->expects($this->once())
->method('log')
->with(LogLevel::WARNING, 'Foo message', $expectedLog[2]);
$handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING)));
}
public function testHandleFatalError()
{
try {
@@ -455,7 +385,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
'line' => 123,
);
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
@@ -507,7 +437,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
try {
$handler = ErrorHandler::register();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger
->expects($this->once())
->method('log')
@@ -560,7 +490,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
restore_error_handler();
restore_exception_handler();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Debug\Tests\Exception;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
@@ -27,7 +28,7 @@ use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
class FlattenExceptionTest extends TestCase
{
public function testStatusCode()
{

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Debug\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\Debug\Exception\OutOfMemoryException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -18,7 +19,7 @@ use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
require_once __DIR__.'/HeaderMock.php';
class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
class ExceptionHandlerTest extends TestCase
{
protected function setUp()
{
@@ -100,7 +101,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
{
$exception = new \Exception('foo');
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(array('sendPhpResponse'))->getMock();
$handler
->expects($this->exactly(2))
->method('sendPhpResponse');
@@ -119,7 +120,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
{
$exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__);
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(array('sendPhpResponse'))->getMock();
$handler
->expects($this->once())
->method('sendPhpResponse');

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
use Symfony\Component\ClassLoader\UniversalClassLoader as SymfonyUniversalClassLoader;
use Symfony\Component\Debug\Exception\FatalErrorException;
@@ -18,7 +19,7 @@ use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
use Symfony\Component\Debug\DebugClassLoader;
use Composer\Autoload\ClassLoader as ComposerClassLoader;
class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
class ClassNotFoundFatalErrorHandlerTest extends TestCase
{
public static function setUpBeforeClass()
{

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
class UndefinedFunctionFatalErrorHandlerTest extends TestCase
{
/**
* @dataProvider provideUndefinedFunctionData

View File

@@ -11,10 +11,11 @@
namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
class UndefinedMethodFatalErrorHandlerTest extends TestCase
{
/**
* @dataProvider provideUndefinedMethodData
@@ -61,6 +62,15 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
),
"Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?",
),
array(
array(
'type' => 1,
'message' => 'Call to undefined method class@anonymous::test()',
'file' => '/home/possum/work/symfony/test.php',
'line' => 11,
),
'Attempted to call an undefined method named "test" of class "class@anonymous".',
),
);
}
}

View File

@@ -1,7 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
interface NonDeprecatedInterface extends DeprecatedInterface
{
}

View File

@@ -0,0 +1,3 @@
<?php
throw new \Exception('boo');

View File

@@ -1,24 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class ToStringThrower
{
private $exception;
public function __construct(\Exception $e)
{
$this->exception = $e;
}
public function __toString()
{
try {
throw $this->exception;
} catch (\Exception $e) {
// Using user_error() here is on purpose so we do not forget
// that this alias also should work alongside with trigger_error().
return user_error($e, E_USER_ERROR);
}
}
}

View File

@@ -23,8 +23,8 @@
"symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
},
"require-dev": {
"symfony/class-loader": "~2.2|~3.0.0",
"symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2|~3.0.0"
"symfony/class-loader": "~2.2",
"symfony/http-kernel": "~2.3.24|~2.5.9|^2.6.2"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Debug\\": "" },
@@ -35,7 +35,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
"dev-master": "2.7-dev"
}
}
}

View File

@@ -5,6 +5,8 @@
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />