On Tue, 28 May 2024, Gina P. Banyard wrote:
On Tuesday, 28 May 2024 at 15:26, Derick Rethans <derick@php.net> wrote:
> On Mon, 27 May 2024, Ilija Tovilo wrote:
>
> > On Sun, May 26, 2024 at 11:47 PM Gina P. Banyard internals@gpb.moe
> > wrote:
> >
> > > On Wednesday, 8 May 2024 at 14:40, Gina P. Banyard
> > > internals@gpb.moe wrote:
> > >
> > > > I would like to formally propose my idea for exit() as a
> > > > function brought up to the list on 2024-02-24 [1] with the
> > > > following RFC: PHP: rfc:exit-as-function
> > >
> > > As there haven't been any comments for nearly two weeks, I'm
> > > planning on opening the vote for the RFC on Tuesday.
>
>
> Right now, I am not going to be in favour of this. Specfically
> because the Backward Incompatible Changes don't list some of the
> backward breaking changes, and no mitigations are provided. See
> below.
>
> I also don't see the benefit from doing this in the first place.
> exit/die has always meant "bail straight out", without any function
> semantics (from PHP: exit - Manual):
>
> exit — Output a message and terminate the current script
>
> exit is a language construct and it can be called without
> parentheses if no status is passed.
>
> It's not that this a particularly new or novel behaviour.
>
> I saw somewhere else in the thread that the exit construct could
> already throw a catchable exception. I consider that a bug.
Considering the whole point of the RFC is to make it a function, I'm
not sure why saying the current exit behaviour is not new or novel
serves any purpose.
I understand that that is the point, but I don't think it's argued *why*
that needs to happen.
Is catching a TypeError a bug? Is catching a ValueError a bug? Is
catching any sort of engine Error a bug?
exit throwing any Exception is the bug I was referring to.
Because this is effectively what you are saying, and if this is the
case, we might as well just reverse the "Exceptions in the engine" RFC
from 2014 [1] to make all those Errors fatal errors again which bail
out immediately. This RFC makes exit() a function such that either it
exits normally, or a TypeError is thrown if you feed it nonsense,
consistently with all our type juggling semantics.
Moreover, when exit() was changed to use an Exception internally there
were various people in favour of being able to catch it, and have it
run finally blocks (like other programming languages do) [2] It should
also be noted exit() does not just bail out and do nothing else, any
destructors will be run before the exiting, so one can still run
userland code after it has been called [3]
> I have just checked this for Xdebug, and you're definitely right
> with that. With the removal of the ZEND_EXIT opcode, it can not now
> detect a scope/function exit now.
>
> It also breaks my "do tasks on ZEND_EXIT" with the profiler. It is
> used to always write the closing profiling footer to the files.
> Without me being able to overload thati opcode, data would not be
> complete. I even have two bugs (with tests) for this:
>
> - 0000068: Summary not written when script ended with "exit()" - MantisBT
> - 0000631: Summary not written when script ended with "exit()" - MantisBT
>
> Xdebug has been overloading ZEND_EXIT for nearly 20 years now.
AFAIK Opcodes are not part of any backwards compatibility guarantees.
AFAIK it isn't documented otherwise either. Just because it's not
documented, doesn't mean we should remove long term existing
functionality.
You could overwrite the function pointer of exit() at MINIT with a
custom implementation.
I can, but overriding functions for this sort of thing is a little bit
of a hack, and I prefer having an actual API. It is also a runtime
feature, not a compile time (see below again).
Alternatively, you could use the zend_is_unwind_exit() engine API to
check if the exception that has been thrown is the one that
corresponds to an exit call, this would work as of PHP 8.0.
Finally, the one that makes more work for me, is to add a new observer
API that indicates an exit() and hook into that one.
That seems a fairly sensible thing to add. But that only addresses my
profiler use case, and not the branch/path detection use case.
>
> > This could be re-added by checking for the never return type instead.
>
>
> I can't quite see a way on how to do that? The EXIT is replaced by an
> INIT_FCALL/DO_ICALL:
>
> 4 0 E > EXT_STMT
> 1 ECHO '55'
> 5 2 EXT_STMT
> - 3 > EXIT
> - 6 4* EXT_STMT
> - 5* ECHO '675'
> - 7 6* EXT_STMT
> - 7* > RETURN null
> + 3 INIT_FCALL 'exit'
> + 4 DO_ICALL
> + 6 5 EXT_STMT
> + 6 ECHO '675'
> + 7 7 EXT_STMT
> + 8 > RETURN null
>
>
> The opcodes don't have direct access to the type mask structure as
> far as I know.
You can access the zend_function pointer from the opcode which has
access to the type-mask info.
Isn't that only during runtime, and not compile time?
The branch/path detection works with the op_array, just after it has
been created through compile_file. I don't see any structure in the
DO_ICALL opcode that has access to this never flag.
Or, if required, the Zend/Optimizer/zend_func_infos.h file can be
amended to include exit() and die() and expose the MAY_BE_NEVER return
type.
I don't think that helps either, as that is also run time?
cheers,
Derick