Compare commits

..

18 Commits

Author SHA1 Message Date
Fabien Potencier
bdfb718a6c made phpdoc types consistent with those defined in Hack 2014-04-15 07:41:45 +02:00
Jerome TAMARELLE
3c1b5f218d Check headers sent before sending PHP response
If the response contents has been sent before an error occurs, PHP
triggers the warning "Cannot modify header information - headers already sent"

This change ensure that the error message is echoed, while it's impossible
to change the HTTP status code and headers.
2014-03-26 19:06:50 +01:00
Fabien Potencier
78dc94dce6 changed some PHPUnit assertions to more specific ones 2014-03-01 18:25:29 +01:00
Fabien Potencier
4885c636af fixed various inconsistencies 2014-02-11 11:29:24 +01:00
sun
6a55635898 Fixed recursion level incrementing in FlattenException::flattenArgs(). 2014-02-04 08:06:39 +01:00
Luis Cordova
7f875d2aae update year on licenses 2014-01-07 08:19:25 -05:00
Pascal Borreli
13d5fe432f Fixed typo 2013-12-28 12:31:17 +01:00
Fabien Potencier
89151cc3fe fixed CS for lambdas 2013-12-28 08:46:05 +01:00
Fabien Potencier
93f26330c7 fixed CS 2013-12-12 17:08:59 +01:00
Fabien Potencier
b4ea949b41 [Debug] ensured that a fatal PHP error is actually fatal after being handled by our error handler 2013-11-28 17:41:31 +01:00
Fabien Potencier
264eb40f8d [Debug] fixed unit tests 2013-11-28 11:16:43 +01:00
Christian Schmidt
ff1f1fc483 Avoid notice from being *eaten* by fatal error. 2013-11-28 11:16:19 +01:00
Fabien Potencier
5da7e8c937 fixed typos 2013-11-25 16:00:46 +01:00
Fabien Potencier
085d4fd990 fixed CS 2013-10-30 09:30:20 +01:00
Fabien Potencier
7f671456b9 fixed phpdoc 2013-09-19 11:47:13 +02:00
Fabien Potencier
234f31cd20 Merge branch '2.2' into 2.3
* 2.2:
  [FrameworkBundle][Security] Replaced void return type with null for consistency
  fixed CS
  NativeSessionStorage regenerate
  removed unneeded comment
  Use setTimeZone if this method exists.
  Fix FileResource test
  fixed wrong usage of unset()
  [HttpFoundation] Fixed the way path to directory is trimmed.
  [Console] Fixed argument parsing when a single dash is passed.

Conflicts:
	src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php
2013-09-13 14:20:37 +02:00
ShiraNai7
729f6d19cf Make sure ContextErrorException is loaded during compile time errors 2013-08-08 16:16:10 +02:00
Fabien Potencier
947e8d434b fixed CS 2013-07-01 14:24:43 +02:00
10 changed files with 207 additions and 21 deletions

View File

@@ -30,8 +30,8 @@ class Debug
* If the Symfony ClassLoader component is available, a special
* class loader is also registered.
*
* @param integer $errorReportingLevel The level of error reporting you want
* @param Boolean $displayErrors Whether to display errors (for development) or just log them (for production)
* @param int $errorReportingLevel The level of error reporting you want
* @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
*/
public static function enable($errorReportingLevel = null, $displayErrors = true)
{

View File

@@ -13,6 +13,7 @@ namespace Symfony\Component\Debug;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\Exception\ContextErrorException;
use Symfony\Component\Debug\Exception\DummyException;
use Psr\Log\LoggerInterface;
/**
@@ -55,10 +56,10 @@ class ErrorHandler
/**
* Registers the error handler.
*
* @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
* @param Boolean $displayErrors Display errors (for dev environment) or just log they (production usage)
* @param int $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
* @param bool $displayErrors Display errors (for dev environment) or just log they (production usage)
*
* @return The registered error handler
* @return ErrorHandler The registered error handler
*/
public static function register($level = null, $displayErrors = true)
{
@@ -104,6 +105,7 @@ class ErrorHandler
$stack = array_map(
function ($row) {
unset($row['args']);
return $row;
},
array_slice(debug_backtrace(false), 0, 10)
@@ -119,7 +121,39 @@ class ErrorHandler
}
if ($this->displayErrors && error_reporting() & $level && $this->level & $level) {
throw new ContextErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line, $context);
// make sure the ContextErrorException class is loaded (https://bugs.php.net/bug.php?id=65322)
if (!class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
require __DIR__.'/Exception/ContextErrorException.php';
}
$exception = new ContextErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line, $context);
// Exceptions thrown from error handlers are sometimes not caught by the exception
// handler, so we invoke it directly (https://bugs.php.net/bug.php?id=54275)
$exceptionHandler = set_exception_handler(function () {});
restore_exception_handler();
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {
$exceptionHandler[0]->handle($exception);
if (!class_exists('Symfony\Component\Debug\Exception\DummyException')) {
require __DIR__.'/Exception/DummyException.php';
}
// we must stop the PHP script execution, as the exception has
// already been dealt with, so, let's throw an exception that
// will be caught by a dummy exception handler
set_exception_handler(function (\Exception $e) use ($exceptionHandler) {
if (!$e instanceof DummyException) {
// happens if our dummy exception is caught by a
// catch-all from user code, in which case, let's the
// current handler handle this "new" exception
call_user_func($exceptionHandler, $e);
}
});
throw new DummyException();
}
}
return false;
@@ -131,7 +165,7 @@ class ErrorHandler
return;
}
unset($this->reservedMemory);
$this->reservedMemory = '';
$type = $error['type'];
if (0 === $this->level || !in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
return;
@@ -152,7 +186,7 @@ class ErrorHandler
}
// get current exception handler
$exceptionHandler = set_exception_handler(function() {});
$exceptionHandler = set_exception_handler(function () {});
restore_exception_handler();
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {

View File

@@ -19,18 +19,18 @@ namespace Symfony\Component\Debug\Exception;
class ContextErrorException extends \ErrorException
{
private $context = array();
public function __construct($message, $code, $severity, $filename, $lineno, $context = array())
{
parent::__construct($message, $code, $severity, $filename, $lineno);
$this->context = $context;
}
/**
* @return array Array of variables that existed when the exception occured
* @return array Array of variables that existed when the exception occurred
*/
public function getContext()
{
return $this->context;
}
}
}

View File

@@ -0,0 +1,21 @@
<?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;
/**
* Used to stop execution of a PHP script after handling a fatal error.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DummyException extends \ErrorException
{
}

View File

@@ -249,7 +249,7 @@ class FlattenException
if ($level > 10) {
$result[$key] = array('array', '*DEEP NESTED ARRAY*');
} else {
$result[$key] = array('array', $this->flattenArgs($value, ++$level));
$result[$key] = array('array', $this->flattenArgs($value, $level + 1));
}
} elseif (null === $value) {
$result[$key] = array('null', null);

View File

@@ -43,7 +43,7 @@ class ExceptionHandler
/**
* Registers the exception handler.
*
* @param Boolean $debug
* @param bool $debug
*
* @return ExceptionHandler The registered exception handler
*/
@@ -91,9 +91,11 @@ class ExceptionHandler
$exception = FlattenException::create($exception);
}
header(sprintf('HTTP/1.0 %s', $exception->getStatusCode()));
foreach ($exception->getHeaders() as $name => $value) {
header($name.': '.$value, false);
if (!headers_sent()) {
header(sprintf('HTTP/1.0 %s', $exception->getStatusCode()));
foreach ($exception->getHeaders() as $name => $value) {
header($name.': '.$value, false);
}
}
echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception));

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2013 Fabien Potencier
Copyright (c) 2004-2014 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

@@ -12,6 +12,7 @@
namespace Symfony\Component\Debug\Tests;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\DummyException;
/**
* ErrorHandlerTest
@@ -20,6 +21,134 @@ use Symfony\Component\Debug\ErrorHandler;
*/
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var int Error reporting level before running tests.
*/
protected $errorReporting;
/**
* @var string Display errors setting before running tests.
*/
protected $displayErrors;
public function setUp()
{
$this->errorReporting = error_reporting(E_ALL | E_STRICT);
$this->displayErrors = ini_get('display_errors');
ini_set('display_errors', '1');
}
public function tearDown()
{
ini_set('display_errors', $this->displayErrors);
error_reporting($this->errorReporting);
}
public function testCompileTimeError()
{
// the ContextErrorException must not be loaded to test the workaround
// for https://bugs.php.net/bug.php?id=65322.
if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) {
$this->markTestSkipped('The ContextErrorException class is already loaded.');
}
$exceptionHandler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('handle'));
// the following code forces some PHPUnit classes to be loaded
// so that they will be available in the exception handler
// as they won't be autoloaded by PHP
class_exists('PHPUnit_Framework_MockObject_Invocation_Object');
$this->assertInstanceOf('stdClass', new \stdClass());
$this->assertEquals(1, 1);
$this->assertStringStartsWith('foo', 'foobar');
$this->assertArrayHasKey('bar', array('bar' => 'foo'));
$that = $this;
$exceptionCheck = function ($exception) use ($that) {
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
$that->assertEquals(E_STRICT, $exception->getSeverity());
$that->assertEquals(2, $exception->getLine());
$that->assertStringStartsWith('Runtime Notice: Declaration of _CompileTimeError::foo() should be compatible with', $exception->getMessage());
$that->assertArrayHasKey('bar', $exception->getContext());
};
$exceptionHandler->expects($this->once())
->method('handle')
->will($this->returnCallback($exceptionCheck))
;
ErrorHandler::register();
set_exception_handler(array($exceptionHandler, 'handle'));
// dummy variable to check for in error handler.
$bar = 123;
// trigger compile time error
try {
eval(<<<'PHP'
class _BaseCompileTimeError { function foo() {} }
class _CompileTimeError extends _BaseCompileTimeError { function foo($invalid) {} }
PHP
);
} catch (DummyException $e) {
// if an exception is thrown, the test passed
}
restore_error_handler();
restore_exception_handler();
}
public function testNotice()
{
$exceptionHandler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('handle'));
set_exception_handler(array($exceptionHandler, 'handle'));
$that = $this;
$exceptionCheck = function ($exception) use ($that) {
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
$that->assertEquals(E_NOTICE, $exception->getSeverity());
$that->assertEquals(__LINE__ + 40, $exception->getLine());
$that->assertEquals(__FILE__, $exception->getFile());
$that->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
$that->assertArrayHasKey('foobar', $exception->getContext());
$trace = $exception->getTrace();
$that->assertEquals(__FILE__, $trace[0]['file']);
$that->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']);
$that->assertEquals('handle', $trace[0]['function']);
$that->assertEquals('->', $trace[0]['type']);
$that->assertEquals(__FILE__, $trace[1]['file']);
$that->assertEquals(__CLASS__, $trace[1]['class']);
$that->assertEquals('triggerNotice', $trace[1]['function']);
$that->assertEquals('::', $trace[1]['type']);
$that->assertEquals(__CLASS__, $trace[2]['class']);
$that->assertEquals('testNotice', $trace[2]['function']);
$that->assertEquals('->', $trace[2]['type']);
};
$exceptionHandler->expects($this->once())
->method('handle')
->will($this->returnCallback($exceptionCheck));
ErrorHandler::register();
try {
self::triggerNotice($this);
} catch (DummyException $e) {
// if an exception is thrown, the test passed
}
restore_error_handler();
}
// dummy function to test trace in error handler.
private static function triggerNotice($that)
{
// dummy variable to check for in error handler.
$foobar = 123;
$that->assertSame('', $foo.$foo.$bar);
}
public function testConstruct()
{
@@ -70,7 +199,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$logger = $this->getMock('Psr\Log\LoggerInterface');
$that = $this;
$warnArgCheck = function($message, $context) use ($that) {
$warnArgCheck = function ($message, $context) use ($that) {
$that->assertEquals('foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], ErrorHandler::TYPE_DEPRECATION);

View File

@@ -113,7 +113,7 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.');
$this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.');
$this->assertEquals(get_class($exception), $flattened->getClass(), 'The class is set to the class of the original exception');
$this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception');
}

View File

@@ -64,6 +64,6 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
public function testNestedExceptions()
{
$handler = new ExceptionHandler(true);
$response = $handler->createResponse(new \RuntimeException('Foo', null, new \RuntimeException('Bar')));
$response = $handler->createResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar')));
}
}