Hi! exit (and its doppelganger die) is a hard stop to the engine and there is little telemetry provided about the circumstances (file, line, message, and code). In source I control, exit is no big deal: I don't use exit! But in library code, exit can be frustrating.
register_shutdown_function + debug_backtrace doesn't help, because the trace doesn't flow out of the shutdown function. xdebug helps to find the exit frame, but cannot pinpoint the exact line. It seems like the engine could help with a little extra telemetry. I'm wondering if the shutdown functions could access telemetry: <?php register_shutdown_function(function () { $context = shutdown_get_context(); /** array ( 'exit' => array ('file' => '/path/to/Foo.php', 'line' => 242, 'message' => "Calling exit() because...", 'code' => 0)) */ // different SAPI may expand on this context }); require 'vendor/autoload.php'; \Vendor\Package\Class::callsExit(); ?> Or, alternatively, I wonder if a method to convert an exit to an exception would be better: <?php echo ini_get('zend.exit_exception'); // "1" try { require 'vendor/autoload.php'; \Vendor\Package\Class::callsExit(); } catch (\ExitException $ex) { // extends \RuntimeException echo 'Stop that!'; } ?> (In all these examples, "callsExit" is vendor code that performs an undesirable exit();) This latter approach feels more modern, at least from a user perspective, but it has the side effect of making exit recoverable. IMO, that's a good thing, because the conditions under which libraries exit may merely be exceptional for consuming applications. However, I'm uncertain of an "exit exception" implementation. Perhaps when INI enabled, zend_compile_exit could rewrite to emit ZEND_THROW with a synthetic node. Or all the ZEND_EXIT could be updated to throw instead of bailout. Don't know. TL;DR: Engine support for tracing/trapping/debugging exit helps developers find and avoid hard exits in dependent code they don't control. Thoughts on proceeding with an RFC? bishop