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
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
I'd be supportive of this, as you define it.
However, since you're asking for comments, I'd really be supportive of some
means in which telemetry of a call could be gathered for more than just
exit & die. Rarely, but not never, we have a new-ish developer who's just
been given perms to commit/update production and accidentally pushes code
with print-to-screen debugging. It would be amazing if this concept could
be extended out to general userland functions/functions/constructs. To be
able to see what lines a specific call is being made to help track down
where offending code might be happening.
-- Dave
Hi Bishop,
It would be good to preserve contexts to make debugging easier.
I wonder if a method to convert an exit to an exception
would be better:
That doesn't sound like a good plan. There are times when exiting
almost straight away is the correct thing to do. If we changed exit()
to not do that, it would mean there would be a need for a exit_real()
that does exit straight away.
cheers
Dan
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();
?>
I like this idea! I can easily imagine the frustration of having
untrackable calls to exit, and the effort to expose this info for
logging is pretty trivial to implement.
Or, alternatively, I wonder if a method to convert an exit to an exception
would be better:
Eh... That feels less-obvious to me for the reasons you stated in your
OP. I'm not convinced that being able to recover from an
\ExitException is a good thing, but I don't have a compelling reason
why it's bad, so I'll keep thinking about that.
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.
I'd probably leave the compile step alone and have the opcode handler
switch on the INI setting. One lil' integer compare on an instruction
meant to stop execution isn't going to be a problem. (You wouldn't
have exit being called repeatedly in a loop)
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?
+1 from me.
-Sara
Or, alternatively, I wonder if a method to convert an exit to an exception
would be better:Eh... That feels less-obvious to me for the reasons you stated in your
OP. I'm not convinced that being able to recover from an
\ExitException is a good thing, but I don't have a compelling reason
why it's bad, so I'll keep thinking about that.
That could be useful for testing. uopz allows to overload ZEND_EXIT
(PHP 5 only, not sure why that has been removed in the PHP 7 version),
and there's also a respective request for runkit:
https://github.com/zenovich/runkit/issues/49.
--
Christoph M. Becker