Skip to content

Commit 06adb24

Browse files
committed
feature #10941 [Debug] cleanup interfaces before 2.5-final (nicolas-grekas)
This PR was merged into the 2.4-dev branch. Discussion ---------- [Debug] cleanup interfaces before 2.5-final | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | none | License | MIT | Doc PR | none This PR is targeted at cleaning up interfaces before 2.5 final: - ExceptionHandlerInterface has never been released in a stable Symfony, lets drop it, not deprecate it, - generalize a little bit how fatal errors are handled and make them take the same path as uncaught exceptions, - enhance handling of out of memory situations. Commits ------- e3255bf [Debug] better ouf of memory error handling dfa8ff8 [Debug] cleanup interfaces before 2.5-final
2 parents b2855a5 + e3255bf commit 06adb24

File tree

10 files changed

+209
-187
lines changed

10 files changed

+209
-187
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<parameter key="debug.stopwatch.class">Symfony\Component\Stopwatch\Stopwatch</parameter>
1010
<parameter key="debug.container.dump">%kernel.cache_dir%/%kernel.container_class%.xml</parameter>
1111
<parameter key="debug.controller_resolver.class">Symfony\Component\HttpKernel\Controller\TraceableControllerResolver</parameter>
12-
<parameter key="debug.fatal_error_exceptions_listener.class">Symfony\Component\HttpKernel\EventListener\FatalErrorExceptionsListener</parameter>
12+
<parameter key="debug.debug_handlers_listener.class">Symfony\Component\HttpKernel\EventListener\DebugHandlersListener</parameter>
1313
</parameters>
1414

1515
<services>
@@ -41,11 +41,11 @@
4141
<argument type="service" id="logger" on-invalid="null" />
4242
</service>
4343

44-
<service id="debug.fatal_error_exceptions_listener" class="%debug.fatal_error_exceptions_listener.class%">
44+
<service id="debug.debug_handlers_listener" class="%debug.debug_handlers_listener.class%">
4545
<tag name="kernel.event_subscriber" />
4646
<argument type="collection">
4747
<argument type="service" id="http_kernel" on-invalid="null" />
48-
<argument>handleFatalErrorException</argument>
48+
<argument>terminateWithException</argument>
4949
</argument>
5050
</service>
5151
</services>

src/Symfony/Component/Debug/CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ CHANGELOG
44
2.5.0
55
-----
66

7-
* added ErrorHandler::setFatalErrorExceptionHandler()
7+
* added ExceptionHandler::setHandler()
88
* added UndefinedMethodFatalErrorHandler
9-
* deprecated ExceptionHandlerInterface
109
* deprecated DummyException
1110

1211
2.4.0

src/Symfony/Component/Debug/ErrorHandler.php

Lines changed: 17 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Psr\Log\LoggerInterface;
1616
use Symfony\Component\Debug\Exception\ContextErrorException;
1717
use Symfony\Component\Debug\Exception\FatalErrorException;
18+
use Symfony\Component\Debug\Exception\OutOfMemoryException;
1819
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
1920
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
2021
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
@@ -53,8 +54,6 @@ class ErrorHandler
5354

5455
private $displayErrors;
5556

56-
private $caughtOutput = 0;
57-
5857
/**
5958
* @var LoggerInterface[] Loggers for channels
6059
*/
@@ -64,8 +63,6 @@ class ErrorHandler
6463

6564
private static $stackedErrorLevels = array();
6665

67-
private static $fatalHandler = false;
68-
6966
/**
7067
* Registers the error handler.
7168
*
@@ -119,16 +116,6 @@ public static function setLogger(LoggerInterface $logger, $channel = 'deprecatio
119116
self::$loggers[$channel] = $logger;
120117
}
121118

122-
/**
123-
* Sets a fatal error exception handler.
124-
*
125-
* @param callable $handler An handler that will be called on FatalErrorException
126-
*/
127-
public static function setFatalErrorExceptionHandler($handler)
128-
{
129-
self::$fatalHandler = $handler;
130-
}
131-
132119
/**
133120
* @throws ContextErrorException When error_reporting returns error
134121
*/
@@ -284,7 +271,7 @@ public function handleFatal()
284271
throw $exception;
285272
}
286273

287-
if (!$error || !$this->level || !in_array($error['type'], array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
274+
if (!$error || !$this->level || !($error['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_PARSE))) {
288275
return;
289276
}
290277

@@ -298,7 +285,7 @@ public function handleFatal()
298285
self::$loggers['emergency']->emergency($error['message'], $fatal);
299286
}
300287

301-
if ($this->displayErrors && ($exceptionHandler || self::$fatalHandler)) {
288+
if ($this->displayErrors && $exceptionHandler) {
302289
$this->handleFatalError($exceptionHandler, $error);
303290
}
304291
}
@@ -327,82 +314,25 @@ private function handleFatalError($exceptionHandler, array $error)
327314

328315
$level = isset($this->levels[$error['type']]) ? $this->levels[$error['type']] : $error['type'];
329316
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
330-
$exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line'], 3);
331-
332-
foreach ($this->getFatalErrorHandlers() as $handler) {
333-
if ($e = $handler->handleError($error, $exception)) {
334-
$exception = $e;
335-
break;
336-
}
337-
}
338-
339-
// To be as fail-safe as possible, the FatalErrorException is first handled
340-
// by the exception handler, then by the fatal error handler. The latter takes
341-
// precedence and any output from the former is cancelled, if and only if
342-
// nothing bad happens in this handling path.
343-
344-
$caughtOutput = 0;
345-
346-
if ($exceptionHandler) {
347-
$this->caughtOutput = false;
348-
ob_start(array($this, 'catchOutput'));
349-
try {
350-
call_user_func($exceptionHandler, $exception);
351-
} catch (\Exception $e) {
352-
// Ignore this exception, we have to deal with the fatal error
353-
}
354-
if (false === $this->caughtOutput) {
355-
ob_end_clean();
356-
}
357-
if (isset($this->caughtOutput[0])) {
358-
ob_start(array($this, 'cleanOutput'));
359-
echo $this->caughtOutput;
360-
$caughtOutput = ob_get_length();
361-
}
362-
$this->caughtOutput = 0;
363-
}
364-
365-
if (self::$fatalHandler) {
366-
try {
367-
call_user_func(self::$fatalHandler, $exception);
368-
369-
if ($caughtOutput) {
370-
$this->caughtOutput = $caughtOutput;
371-
}
372-
} catch (\Exception $e) {
373-
if (!$caughtOutput) {
374-
// Neither the exception nor the fatal handler succeeded.
375-
// Let PHP handle that now.
376-
throw $exception;
317+
if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
318+
$exception = new OutOfMemoryException($message, 0, $error['type'], $error['file'], $error['line'], 3, false);
319+
} else {
320+
$exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line'], 3, true);
321+
322+
foreach ($this->getFatalErrorHandlers() as $handler) {
323+
if ($e = $handler->handleError($error, $exception)) {
324+
$exception = $e;
325+
break;
377326
}
378327
}
379328
}
380-
}
381-
382-
/**
383-
* @internal
384-
*/
385-
public function catchOutput($buffer)
386-
{
387-
$this->caughtOutput = $buffer;
388329

389-
return '';
390-
}
391-
392-
/**
393-
* @internal
394-
*/
395-
public function cleanOutput($buffer)
396-
{
397-
if ($this->caughtOutput) {
398-
// use substr_replace() instead of substr() for mbstring overloading resistance
399-
$cleanBuffer = substr_replace($buffer, '', 0, $this->caughtOutput);
400-
if (isset($cleanBuffer[0])) {
401-
$buffer = $cleanBuffer;
402-
}
330+
try {
331+
call_user_func($exceptionHandler, $exception);
332+
} catch (\Exception $e) {
333+
// The handler failed. Let PHP handle that now.
334+
throw $exception;
403335
}
404-
405-
return $buffer;
406336
}
407337
}
408338

src/Symfony/Component/Debug/Exception/FatalErrorException.php

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,40 @@
2020
*/
2121
class FatalErrorException extends \ErrorException
2222
{
23-
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null)
23+
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true)
2424
{
2525
parent::__construct($message, $code, $severity, $filename, $lineno);
2626

2727
if (null !== $traceOffset) {
2828
if (function_exists('xdebug_get_function_stack')) {
2929
$trace = xdebug_get_function_stack();
3030
if (0 < $traceOffset) {
31-
$trace = array_slice($trace, 0, -$traceOffset);
31+
array_splice($trace, -$traceOffset);
3232
}
33-
$trace = array_reverse($trace);
3433

35-
foreach ($trace as $i => $frame) {
34+
foreach ($trace as &$frame) {
3635
if (!isset($frame['type'])) {
3736
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
3837
if (isset($frame['class'])) {
39-
$trace[$i]['type'] = '::';
38+
$frame['type'] = '::';
4039
}
4140
} elseif ('dynamic' === $frame['type']) {
42-
$trace[$i]['type'] = '->';
41+
$frame['type'] = '->';
4342
} elseif ('static' === $frame['type']) {
44-
$trace[$i]['type'] = '::';
43+
$frame['type'] = '::';
4544
}
4645

4746
// XDebug also has a different name for the parameters array
48-
if (isset($frame['params']) && !isset($frame['args'])) {
49-
$trace[$i]['args'] = $frame['params'];
50-
unset($trace[$i]['params']);
47+
if (!$traceArgs) {
48+
unset($frame['params'], $frame['args']);
49+
} elseif (isset($frame['params']) && !isset($frame['args'])) {
50+
$frame['args'] = $frame['params'];
51+
unset($frame['params']);
5152
}
5253
}
54+
55+
unset($frame);
56+
$trace = array_reverse($trace);
5357
} else {
5458
$trace = array();
5559
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Debug\Exception;
13+
14+
/**
15+
* Out of memory exception.
16+
*
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*/
19+
class OutOfMemoryException extends FatalErrorException
20+
{
21+
}

0 commit comments

Comments
 (0)