diff --git a/README.md b/README.md index 4196273..e4cec1f 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [e]: https://www.php.net/manual/en/function.print-r.php [f]: https://github.com/symfony/var-exporter [g]: https://github.com/php-fig/log +[h]: https://code.mensbeam.com/MensBeam/SelfSealingCallable # Catcher # @@ -16,6 +17,7 @@ _Catcher_ uses classes called _handlers_ to handle throwables sent its way. PHP ## Requirements ## * PHP >= 8.1 +* [mensbeam/self-sealing-callable][h] ^1.0 * [psr/log][g] ^3.0 diff --git a/composer.json b/composer.json index 2886f06..18f11d9 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ ], "require": { "php": ">=8.1", - "psr/log": "^3.0" + "psr/log": "^3.0", + "mensbeam/self-sealing-callable": "^1.0" }, "require-dev": { "phpunit/phpunit": "^10", diff --git a/composer.lock b/composer.lock index 85f7678..f6e6bca 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,51 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "016d5eec0db2855c94ebfb0c8a698b7a", + "content-hash": "653095595ffbfaf78354b0eb63a23283", "packages": [ + { + "name": "mensbeam/self-sealing-callable", + "version": "v1.0", + "source": { + "type": "git", + "url": "https://github.com/mensbeam/SelfSealingCallable.git", + "reference": "f3645a5f7745a8673d3a8ab23c0529ccdc793bde" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mensbeam/SelfSealingCallable/zipball/f3645a5f7745a8673d3a8ab23c0529ccdc793bde", + "reference": "f3645a5f7745a8673d3a8ab23c0529ccdc793bde", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "MensBeam\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dustin Wilson", + "email": "dustin@dustinwilson.com" + } + ], + "description": "You won't find a better Self-Sealing Callable in this sector!", + "support": { + "issues": "https://github.com/mensbeam/SelfSealingCallable/issues", + "source": "https://github.com/mensbeam/SelfSealingCallable/tree/v1.0" + }, + "time": "2024-02-12T16:55:01+00:00" + }, { "name": "psr/log", "version": "3.0.0", @@ -1866,5 +1909,5 @@ "php": ">=8.1" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/lib/Catcher.php b/lib/Catcher.php index 6586345..5e37034 100644 --- a/lib/Catcher.php +++ b/lib/Catcher.php @@ -38,12 +38,17 @@ class Catcher { * @var Handler[] */ protected array $handlers = []; + /** + * Created when run for the first time and stores the shutdown handler; + * automatically disables itself when unregistered + */ + protected static ?SelfSealingCallable $shutdownHandler = null; /** Flag set when the shutdown handler is run */ protected bool $isShuttingDown = false; - /** Flag set when the class has registered error, exception, and shutdown handlers */ - protected bool $registered = false; /** The last throwable handled by Catcher */ protected ?\Throwable $lastThrowable = null; + /** Flag set when the class has registered error, exception, and shutdown handlers */ + protected bool $registered = false; @@ -110,7 +115,15 @@ class Catcher { set_error_handler([ $this, 'handleError' ]); set_exception_handler([ $this, 'handleThrowable' ]); - register_shutdown_function([ $this, 'handleShutdown' ]); + + // Shutdown functions are fucky in php and can't be unset, so it is run in a + // SelfSealingCallable. + if (self::$shutdownHandler === null) { + self::$shutdownHandler = new SelfSealingCallable(fn() => $this->handleShutdown()); + register_shutdown_function(self::$shutdownHandler); + } + + self::$shutdownHandler->enable(); $this->registered = true; return true; @@ -136,6 +149,7 @@ class Catcher { restore_error_handler(); restore_exception_handler(); + self::$shutdownHandler->disable(); // If error reporting has been set when registering and the error reporting level // is the same as it was when it was set then add E_ERROR back to the error