Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[9.x] Avoid memory leak in HandleExceptions during repeated Application startup #44293

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions src/Illuminate/Foundation/Bootstrap/HandleExceptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class HandleExceptions
*/
protected static $app;

private static $globalShutdownFunctionRegistered = false;

private static $errorHandlerRegistered = false;

private static $exceptionHandlerRegistered = false;

/**
* Bootstrap the given application.
*
Expand All @@ -42,11 +48,18 @@ public function bootstrap(Application $app)

error_reporting(-1);

set_error_handler($this->forwardsTo('handleError'));
if (! self::$globalShutdownFunctionRegistered) {
self::$globalShutdownFunctionRegistered = true;

set_exception_handler($this->forwardsTo('handleException'));
register_shutdown_function($this->forwardsTo('handleShutdown'));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not ideal as it keeps reference to original $this but there is no way to unregister a shutdown function. To solve it properly, yet another layer of indirection with reference to $this must be kept static (which pretty much makes the HandleExceptions class a singleton, which it already pretty much is 🤷‍♂️).

Needs a bit more thought.

}

register_shutdown_function($this->forwardsTo('handleShutdown'));
// error & exception handlers must be overwritten every time since PHPUnit registers its own
// beware, those handlers must be cleared as they are stored on top of each other
set_error_handler($this->forwardsTo('handleError'));
static::$errorHandlerRegistered = true;
set_exception_handler($this->forwardsTo('handleException'));
static::$exceptionHandlerRegistered = true;

if (! $app->environment('testing')) {
ini_set('display_errors', 'Off');
Expand Down Expand Up @@ -300,5 +313,16 @@ protected function getExceptionHandler()
public static function forgetApp()
{
static::$app = null;

// remove registered handlers to prevent memory leak
if (static::$errorHandlerRegistered) {
restore_error_handler();
static::$errorHandlerRegistered = false;
}

if (static::$exceptionHandlerRegistered) {
restore_exception_handler();
static::$exceptionHandlerRegistered = false;
}
}
}