Compare commits

..

10 Commits

Author SHA1 Message Date
Fabien Potencier
e90099a295 fixed Composer constraints 2017-02-18 11:13:35 -08:00
Nicolas Grekas
3b77268009 Merge branch '2.7' into 2.8
* 2.7:
  Update to PHPUnit namespaces
  remove translation data collector when not usable
2017-02-18 18:06:33 +01:00
Peter Rehm
0d4887956a Update to PHPUnit namespaces 2017-02-18 08:02:39 -08:00
Fabien Potencier
4a99c3a6ca Merge branch '2.7' into 2.8
* 2.7:
  always check for all fields to be mapped
  clarify exception when no args are configured
  [PropertyAccess] Handle interfaces in the invalid argument exception
  [Debug] Workaround "null" $context
  [Debug] Remove $context arg from handleError(), preparing for PHP 7.2
  [Routing] Fix BC break in AnnotationClassLoader defaults attributes handling
  Fix tests with ICU 57.1
  Fix the condition checking the minimum ICU version
2017-01-27 15:54:58 -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
b58c5193eb Merge branch '2.7' into 2.8
* 2.7:
  fixed typo
  Revert "fixed typo"
  fixed typo
  fixed CS
  Avoid setting request attributes from signature arguments in AnnotationClassLoader
  [DependencyInjection] Add some missing typehints in YamlFileLoader
  [DependencyInjection] minor: Fix a DocBlock
  [HttpKernel] Give higher priority to adding request formats
  [FrameworkBundle] Fix third level headers for MarkdownDescriptor
  [TwigBundle] do not lose already set method calls
  #20411 fix Yaml parsing for very long quoted strings
  CS: apply is_null
  DX: remove invalid inheritdoc
  bumped Symfony version to 2.7.24
  updated VERSION for 2.7.23
  update CONTRIBUTORS for 2.7.23
  updated CHANGELOG for 2.7.23
  [FrameworkBundle] Skip test if xdebug.file_link_format is defined.
2017-01-21 08:40:50 -08: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
17 changed files with 592 additions and 110 deletions

View File

@@ -1,11 +1,6 @@
CHANGELOG
=========
3.0.0
-----
* removed classes, methods and interfaces deprecated in 2.x
2.8.0
-----

View File

@@ -31,7 +31,7 @@ class Debug
* @param int $errorReportingLevel The level of error reporting you want
* @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
*/
public static function enable($errorReportingLevel = E_ALL, $displayErrors = true)
public static function enable($errorReportingLevel = null, $displayErrors = true)
{
if (static::$enabled) {
return;
@@ -42,7 +42,7 @@ class Debug
if (null !== $errorReportingLevel) {
error_reporting($errorReportingLevel);
} else {
error_reporting(E_ALL);
error_reporting(-1);
}
if ('cli' !== PHP_SAPI) {

View File

@@ -26,6 +26,7 @@ class DebugClassLoader
{
private $classLoader;
private $isFinder;
private $wasFinder;
private static $caseCheck;
private static $deprecated = array();
private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null');
@@ -34,12 +35,20 @@ class DebugClassLoader
/**
* Constructor.
*
* @param callable $classLoader A class loader
* @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(callable $classLoader)
public function __construct($classLoader)
{
$this->classLoader = $classLoader;
$this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile');
$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 {
$this->classLoader = $classLoader;
$this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile');
}
if (!isset(self::$caseCheck)) {
$file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR);
@@ -68,11 +77,11 @@ class DebugClassLoader
/**
* Gets the wrapped class loader.
*
* @return callable The wrapped class loader
* @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()
{
return $this->classLoader;
return $this->wasFinder ? $this->classLoader[0] : $this->classLoader;
}
/**
@@ -123,6 +132,24 @@ class DebugClassLoader
}
}
/**
* Finds a file by class name.
*
* @param string $class A class name to resolve to file
*
* @return string|null
*
* @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);
}
}
/**
* Loads the given class or interface.
*
@@ -145,11 +172,19 @@ class DebugClassLoader
call_user_func($this->classLoader, $class);
$file = false;
}
} finally {
} catch (\Exception $e) {
ErrorHandler::unstackErrors();
throw $e;
} catch (\Throwable $e) {
ErrorHandler::unstackErrors();
throw $e;
}
$exists = class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
ErrorHandler::unstackErrors();
$exists = class_exists($class, false) || interface_exists($class, false) || (function_exists('trait_exists') && trait_exists($class, false));
if ('\\' === $class[0]) {
$class = substr($class, 1);

View File

@@ -46,6 +46,11 @@ use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
*/
class ErrorHandler
{
/**
* @deprecated since version 2.6, to be removed in 3.0.
*/
const TYPE_DEPRECATION = -100;
private $levels = array(
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
@@ -99,22 +104,36 @@ class ErrorHandler
private static $stackedErrorLevels = array();
private static $toStringException = null;
/**
* Same init value as thrownErrors.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
private $displayErrors = 0x1FFF;
/**
* Registers the error handler.
*
* @param self|null $handler The handler to register
* @param bool $replace Whether to replace or not any existing handler
* @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
*/
public static function register(self $handler = null, $replace = true)
public static function register($handler = null, $replace = true)
{
if (null === self::$reservedMemory) {
self::$reservedMemory = str_repeat('x', 10240);
register_shutdown_function(__CLASS__.'::handleFatalError');
}
if ($handlerIsNew = null === $handler) {
$levels = -1;
if ($handlerIsNew = !$handler instanceof self) {
// @deprecated polymorphism, to be removed in 3.0
if (null !== $handler) {
$levels = $replace ? $handler : 0;
$replace = true;
}
$handler = new static();
}
@@ -135,7 +154,7 @@ class ErrorHandler
restore_error_handler();
}
$handler->throwAt(E_ALL & $handler->thrownErrors, true);
$handler->throwAt($levels & $handler->thrownErrors, true);
return $handler;
}
@@ -155,7 +174,7 @@ class ErrorHandler
* @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
* @param bool $replace Whether to replace or not any existing logger
*/
public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false)
{
$loggers = array();
@@ -167,7 +186,7 @@ class ErrorHandler
}
} else {
if (null === $levels) {
$levels = E_ALL;
$levels = E_ALL | E_STRICT;
}
foreach ($this->loggers as $type => $log) {
if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
@@ -239,9 +258,14 @@ class ErrorHandler
* @param callable $handler A handler that will be called on Exception
*
* @return callable|null The previous exception handler
*
* @throws \InvalidArgumentException
*/
public function setExceptionHandler(callable $handler = null)
public function setExceptionHandler($handler)
{
if (null !== $handler && !is_callable($handler)) {
throw new \LogicException('The exception handler must be a valid PHP callable.');
}
$prev = $this->exceptionHandler;
$this->exceptionHandler = $handler;
@@ -265,6 +289,9 @@ class ErrorHandler
}
$this->reRegister($prev | $this->loggedErrors);
// $this->displayErrors is @deprecated since version 2.6
$this->displayErrors = $this->thrownErrors;
return $prev;
}
@@ -348,12 +375,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
*
@@ -361,7 +386,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;
@@ -371,9 +396,20 @@ class ErrorHandler
if (!$type || (!$log && !$throw)) {
return $type && $log;
}
$scope = $this->scopedErrors & $type;
if (isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
unset($context['GLOBALS']);
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;
}
if (null !== $backtrace && $type & E_ERROR) {
@@ -389,12 +425,21 @@ class ErrorHandler
if (null !== self::$toStringException) {
$throw = self::$toStringException;
self::$toStringException = null;
} elseif (($this->scopedErrors & $type) && class_exists(ContextErrorException::class)) {
} elseif ($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)) {
// 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();
}
if (E_USER_ERROR & $type) {
$backtrace = $backtrace ?: $throw->getTrace();
@@ -452,7 +497,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);
@@ -477,8 +522,15 @@ class ErrorHandler
try {
$this->isRecursive = true;
$this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
} finally {
$this->isRecursive = false;
} catch (\Exception $e) {
$this->isRecursive = false;
throw $e;
} catch (\Throwable $e) {
$this->isRecursive = false;
throw $e;
}
}
@@ -671,4 +723,118 @@ class ErrorHandler
new ClassNotFoundFatalErrorHandler(),
);
}
/**
* Sets the level at which the conversion to Exception is done.
*
* @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
*
* @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);
}
/**
* Sets the display_errors flag value.
*
* @param int $displayErrors The display_errors flag value
*
* @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 {
$displayErrors = $this->displayErrors;
$this->throwAt(0, true);
$this->displayErrors = $displayErrors;
}
}
/**
* Sets a logger for the given channel.
*
* @param LoggerInterface $logger A logger interface
* @param string $channel The channel associated with the logger (deprecation, emergency or scream)
*
* @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');
$handler = is_array($handler) ? $handler[0] : null;
restore_error_handler();
if (!$handler instanceof self) {
return;
}
if ('deprecation' === $channel) {
$handler->setDefaultLogger($logger, E_DEPRECATED | E_USER_DEPRECATED, true);
$handler->screamAt(E_DEPRECATED | E_USER_DEPRECATED);
} elseif ('scream' === $channel) {
$handler->setDefaultLogger($logger, E_ALL | E_STRICT, false);
$handler->screamAt(E_ALL | E_STRICT);
} elseif ('emergency' === $channel) {
$handler->setDefaultLogger($logger, E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR, true);
$handler->screamAt(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
}
}
/**
* @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 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.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
class ErrorHandlerCanary
{
private static $displayErrors = null;
public function __construct()
{
if (null === self::$displayErrors) {
self::$displayErrors = ini_set('display_errors', 1);
}
}
public function __destruct()
{
if (null !== self::$displayErrors) {
ini_set('display_errors', self::$displayErrors);
self::$displayErrors = null;
}
}
}

View File

@@ -0,0 +1,23 @@
<?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;
@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>
*
* @deprecated since version 2.5, to be removed in 3.0.
*/
class DummyException extends \ErrorException
{
}

View File

@@ -9,14 +9,31 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Exception;
/**
* Fatal Error Exception.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Konstanton Myakshin <koc-dp@yandex.ru>
* @author Nicolas Grekas <p@tchwork.com>
*
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
*/
class FatalErrorException extends \ErrorException
{
}
namespace Symfony\Component\Debug\Exception;
use Symfony\Component\HttpKernel\Exception\FatalErrorException as LegacyFatalErrorException;
/**
* Fatal Error Exception.
*
* @author Konstanton Myakshin <koc-dp@yandex.ru>
*/
class FatalErrorException extends \ErrorException
class FatalErrorException extends LegacyFatalErrorException
{
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null)
{

View File

@@ -9,8 +9,49 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Exception;
use Symfony\Component\Debug\Exception\FlattenException as DebugFlattenException;
/**
* FlattenException wraps a PHP Exception to be able to serialize it.
*
* Basically, this class removes all objects from the trace.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
*/
class FlattenException
{
private $handler;
public static function __callStatic($method, $args)
{
if (!method_exists('Symfony\Component\Debug\Exception\FlattenException', $method)) {
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_called_class(), $method));
}
return call_user_func_array(array('Symfony\Component\Debug\Exception\FlattenException', $method), $args);
}
public function __call($method, $args)
{
if (!isset($this->handler)) {
$this->handler = new DebugFlattenException();
}
if (!method_exists($this->handler, $method)) {
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_class($this), $method));
}
return call_user_func_array(array($this->handler, $method), $args);
}
}
namespace Symfony\Component\Debug\Exception;
use Symfony\Component\HttpKernel\Exception\FlattenException as LegacyFlattenException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
/**
@@ -20,7 +61,7 @@ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FlattenException
class FlattenException extends LegacyFlattenException
{
private $message;
private $code;

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Debug;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\Debug\Exception\OutOfMemoryException;
@@ -37,6 +38,14 @@ 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;
$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');
@@ -71,8 +80,11 @@ class ExceptionHandler
*
* @return callable|null The previous exception handler if any
*/
public function setHandler(callable $handler = null)
public function setHandler($handler)
{
if (null !== $handler && !is_callable($handler)) {
throw new \LogicException('The exception handler must be a valid PHP callable.');
}
$old = $this->handler;
$this->handler = $handler;
@@ -105,36 +117,20 @@ class ExceptionHandler
public function handle(\Exception $exception)
{
if (null === $this->handler || $exception instanceof OutOfMemoryException) {
$this->sendPhpResponse($exception);
$this->failSafeHandle($exception);
return;
}
$caughtLength = $this->caughtLength = 0;
ob_start(function ($buffer) {
$this->caughtBuffer = $buffer;
return '';
});
$this->sendPhpResponse($exception);
ob_start(array($this, 'catchOutput'));
$this->failSafeHandle($exception);
while (null === $this->caughtBuffer && ob_end_flush()) {
// Empty loop, everything is in the condition
}
if (isset($this->caughtBuffer[0])) {
ob_start(function ($buffer) {
if ($this->caughtLength) {
// use substr_replace() instead of substr() for mbstring overloading resistance
$cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength);
if (isset($cleanBuffer[0])) {
$buffer = $cleanBuffer;
}
}
return $buffer;
});
ob_start(array($this, 'cleanOutput'));
echo $this->caughtBuffer;
$caughtLength = ob_get_length();
}
@@ -151,6 +147,33 @@ class ExceptionHandler
}
}
/**
* Sends a response for the given Exception.
*
* If you have the Symfony HttpFoundation component installed,
* this method will use it to create and send the response. If not,
* it will fallback to plain PHP functions.
*
* @param \Exception $exception An \Exception instance
*/
private function failSafeHandle(\Exception $exception)
{
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();
@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;
}
$this->sendPhpResponse($exception);
}
/**
* Sends the error associated with the given Exception as a plain PHP response.
*
@@ -176,6 +199,26 @@ class ExceptionHandler
echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
}
/**
* Creates the error Response associated with the given Exception.
*
* @param \Exception|FlattenException $exception An \Exception or FlattenException 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.
*
@@ -405,11 +448,49 @@ EOF;
return implode(', ', $result);
}
/**
* Returns an UTF-8 and HTML encoded string.
*
* @deprecated since version 2.7, to be removed in 3.0.
*/
protected static function utf8Htmlize($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 | ENT_SUBSTITUTE, $this->charset);
return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), $this->charset);
}
/**
* @internal
*/
public function catchOutput($buffer)
{
$this->caughtBuffer = $buffer;
return '';
}
/**
* @internal
*/
public function cleanOutput($buffer)
{
if ($this->caughtLength) {
// use substr_replace() instead of substr() for mbstring overloading resistance
$cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength);
if (isset($cleanBuffer[0])) {
$buffer = $cleanBuffer;
}
}
return $buffer;
}
}

View File

@@ -16,6 +16,7 @@ use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\DebugClassLoader;
use Composer\Autoload\ClassLoader as ComposerClassLoader;
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
use Symfony\Component\ClassLoader\UniversalClassLoader as SymfonyUniversalClassLoader;
/**
* ErrorHandler for classes that do not exist.
@@ -100,12 +101,17 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
if ($function[0] instanceof DebugClassLoader) {
$function = $function[0]->getClassLoader();
// @deprecated since version 2.5. Returning an object from DebugClassLoader::getClassLoader() is deprecated.
if (is_object($function)) {
$function = array($function);
}
if (!is_array($function)) {
continue;
}
}
if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader || $function[0] instanceof SymfonyUniversalClassLoader) {
foreach ($function[0]->getPrefixes() as $prefix => $paths) {
foreach ($paths as $path) {
$classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
@@ -173,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
@@ -201,6 +207,6 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
*/
private function classExists($class)
{
return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
return class_exists($class, false) || interface_exists($class, false) || (function_exists('trait_exists') && trait_exists($class, false));
}
}

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
@@ -25,7 +26,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
$this->errorReporting = error_reporting(E_ALL);
$this->errorReporting = error_reporting(E_ALL | E_STRICT);
$this->loader = new ClassLoader();
spl_autoload_register(array($this->loader, 'loadClass'), true, true);
DebugClassLoader::enable();
@@ -107,6 +108,8 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
$this->fail('ContextErrorException expected');
} catch (\ErrorException $exception) {
// if an exception is thrown, the test passed
restore_error_handler();
restore_exception_handler();
$this->assertStringStartsWith(__FILE__, $exception->getFile());
if (PHP_VERSION_ID < 70000) {
$this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage());
@@ -115,9 +118,11 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertRegExp('/^Warning: Declaration/', $exception->getMessage());
$this->assertEquals(E_WARNING, $exception->getSeverity());
}
} finally {
} catch (\Exception $exception) {
restore_error_handler();
restore_exception_handler();
throw $exception;
}
}

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\Debug\Tests;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\BufferingLogger;
@@ -22,7 +23,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()
{
@@ -73,6 +74,9 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$this->fail('ContextErrorException expected');
} catch (ContextErrorException $exception) {
// if an exception is thrown, the test passed
restore_error_handler();
restore_exception_handler();
$this->assertEquals(E_NOTICE, $exception->getSeverity());
$this->assertEquals(__FILE__, $exception->getFile());
$this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
@@ -93,9 +97,11 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(__CLASS__, $trace[2]['class']);
$this->assertEquals(__FUNCTION__, $trace[2]['function']);
$this->assertEquals('->', $trace[2]['type']);
} finally {
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -113,9 +119,14 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
$this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -147,9 +158,14 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
E_CORE_ERROR => array(null, LogLevel::CRITICAL),
);
$this->assertSame($loggers, $handler->setLoggers(array()));
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -200,13 +216,14 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$warnArgCheck = function ($logLevel, $message, $context) {
$this->assertEquals('info', $logLevel);
$this->assertEquals('foo', $message);
$this->assertArrayHasKey('type', $context);
$this->assertEquals($context['type'], E_USER_DEPRECATED);
$this->assertArrayHasKey('stack', $context);
$this->assertInternalType('array', $context['stack']);
$that = $this;
$warnArgCheck = function ($logLevel, $message, $context) use ($that) {
$that->assertEquals('info', $logLevel);
$that->assertEquals('foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_USER_DEPRECATED);
$that->assertArrayHasKey('stack', $context);
$that->assertInternalType('array', $context['stack']);
};
$logger
@@ -224,10 +241,11 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logArgCheck = function ($level, $message, $context) {
$this->assertEquals('Undefined variable: undefVar', $message);
$this->assertArrayHasKey('type', $context);
$this->assertEquals($context['type'], E_NOTICE);
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals('Undefined variable: undefVar', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_NOTICE);
};
$logger
@@ -268,19 +286,25 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
}
$this->assertSame($x, $e);
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testHandleDeprecation()
{
$logArgCheck = function ($level, $message, $context) {
$this->assertEquals(LogLevel::INFO, $level);
$this->assertArrayHasKey('level', $context);
$this->assertEquals(E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED, $context['level']);
$this->assertArrayHasKey('stack', $context);
$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->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
@@ -304,10 +328,11 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logArgCheck = function ($level, $message, $context) {
$this->assertEquals('Uncaught Exception: foo', $message);
$this->assertArrayHasKey('type', $context);
$this->assertEquals($context['type'], E_ERROR);
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals('Uncaught Exception: foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_ERROR);
};
$logger
@@ -325,14 +350,20 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertSame($exception, $e);
}
$handler->setExceptionHandler(function ($e) use ($exception) {
$this->assertSame($exception, $e);
$that = $this;
$handler->setExceptionHandler(function ($e) use ($exception, $that) {
$that->assertSame($exception, $e);
});
$handler->handleException($exception);
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -359,9 +390,14 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
@trigger_error('Silenced warning', E_USER_WARNING);
$logger->log(LogLevel::WARNING, 'Dummy log');
ErrorHandler::unstackErrors();
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -422,10 +458,11 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logArgCheck = function ($level, $message, $context) {
$this->assertEquals('Fatal Parse Error: foo', $message);
$this->assertArrayHasKey('type', $context);
$this->assertEquals($context['type'], E_PARSE);
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals('Fatal Parse Error: foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_PARSE);
};
$logger
@@ -501,9 +538,56 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
call_user_func_array(array($handler, 'handleError'), $error);
$handler->handleFatalError($error);
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
/**
* @group legacy
*/
public function testLegacyInterface()
{
try {
$handler = ErrorHandler::register(0);
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals('Undefined variable: undefVar', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_NOTICE);
};
$logger
->expects($this->once())
->method('log')
->will($this->returnCallback($logArgCheck))
;
$handler = ErrorHandler::register(E_NOTICE);
@$handler->setLogger($logger, 'scream');
unset($undefVar);
@$undefVar++;
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
}

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()
{
@@ -107,8 +108,9 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
$handler->handle($exception);
$handler->setHandler(function ($e) use ($exception) {
$this->assertSame($exception, $e);
$that = $this;
$handler->setHandler(function ($e) use ($exception, $that) {
$that->assertSame($exception, $e);
});
$handler->handle($exception);
@@ -123,8 +125,9 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
->expects($this->once())
->method('sendPhpResponse');
$handler->setHandler(function ($e) {
$this->fail('OutOfMemoryException should bypass the handler');
$that = $this;
$handler->setHandler(function ($e) use ($that) {
$that->fail('OutOfMemoryException should bypass the handler');
});
$handler->handle($exception);

View File

@@ -11,13 +11,15 @@
namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
use Symfony\Component\Debug\Exception\FatalErrorException;
use PHPUnit\Framework\TestCase;
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
class ClassNotFoundFatalErrorHandlerTest extends TestCase
{
public static function setUpBeforeClass()
{
@@ -67,6 +69,27 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertSame($error['line'], $exception->getLine());
}
/**
* @group legacy
*/
public function testLegacyHandleClassNotFound()
{
$prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception'));
$symfonyUniversalClassLoader = new SymfonyUniversalClassLoader();
$symfonyUniversalClassLoader->registerPrefixes($prefixes);
$this->testHandleClassNotFound(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
),
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
array($symfonyUniversalClassLoader, 'loadClass')
);
}
public function provideClassNotFoundData()
{
$prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception'));

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

View File

@@ -16,15 +16,15 @@
}
],
"require": {
"php": ">=5.5.9",
"php": ">=5.3.9",
"psr/log": "~1.0"
},
"conflict": {
"symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
},
"require-dev": {
"symfony/class-loader": "~2.8|~3.0",
"symfony/http-kernel": "~2.8|~3.0"
"symfony/class-loader": "~2.2|~3.0.0",
"symfony/http-kernel": "~2.3.24|~2.5.9|^2.6.2|~3.0.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Debug\\": "" },
@@ -35,7 +35,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
"dev-master": "2.8-dev"
}
}
}