Skip to content

Commit e3255bf

Browse files
[Debug] better ouf of memory error handling
1 parent dfa8ff8 commit e3255bf

File tree

4 files changed

+55
-16
lines changed

4 files changed

+55
-16
lines changed

src/Symfony/Component/Debug/ErrorHandler.php

Lines changed: 11 additions & 6 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;
@@ -313,12 +314,16 @@ private function handleFatalError($exceptionHandler, array $error)
313314

314315
$level = isset($this->levels[$error['type']]) ? $this->levels[$error['type']] : $error['type'];
315316
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
316-
$exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line'], 3);
317-
318-
foreach ($this->getFatalErrorHandlers() as $handler) {
319-
if ($e = $handler->handleError($error, $exception)) {
320-
$exception = $e;
321-
break;
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;
326+
}
322327
}
323328
}
324329

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 <[email protected]>
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 <[email protected]>
18+
*/
19+
class OutOfMemoryException extends FatalErrorException
20+
{
21+
}

src/Symfony/Component/Debug/ExceptionHandler.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\HttpFoundation\Response;
1515
use Symfony\Component\Debug\Exception\FlattenException;
16+
use Symfony\Component\Debug\Exception\OutOfMemoryException;
1617

1718
if (!defined('ENT_SUBSTITUTE')) {
1819
define('ENT_SUBSTITUTE', 8);
@@ -62,6 +63,8 @@ public static function register($debug = true)
6263
* Sets a user exception handler.
6364
*
6465
* @param callable $handler An handler that will be called on Exception
66+
*
67+
* @return callable|null The previous exception handler if any
6568
*/
6669
public function setHandler($handler)
6770
{
@@ -88,6 +91,12 @@ public function setHandler($handler)
8891
*/
8992
public function handle(\Exception $exception)
9093
{
94+
if ($exception instanceof OutOfMemoryException) {
95+
$this->sendPhpResponse($exception);
96+
97+
return;
98+
}
99+
91100
// To be as fail-safe as possible, the exception is first handled
92101
// by our simple exception handler, then by the user exception handler.
93102
// The latter takes precedence and any output from the former is cancelled,

0 commit comments

Comments
 (0)