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

[RFC] Convert exit (and die) from language constructs to functions #13483

Merged
merged 9 commits into from
Aug 14, 2024

Conversation

Girgias
Copy link
Member

@Girgias Girgias commented Feb 23, 2024

The main motivation here is to have the same type juggling semantics as every other function in PHP.
Also, we don't need to have an opcode any more for it.

RFC: https://wiki.php.net/rfc/exit-as-function

Comment on lines 5015 to 5021
if (UNEXPECTED(
zend_string_equals_literal_ci(Z_STR_P(key), "exit")
|| zend_string_equals_literal_ci(Z_STR_P(key), "die")
)) {
zend_throw_unwind_exit();
return FAILURE;
}
Copy link
Member

Choose a reason for hiding this comment

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

Could we achieve similar results with by de-sugaring exit; to a function call at the AST level? (e.g. zend_compile_const() can replace the AST with a function call AST and delegate to zend_compile_call()). This would remove the need for special cases at runtime, and also unify the behavior of exit; and exit(); (wrt to disable_functions).

Copy link
Member

Choose a reason for hiding this comment

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

I agree. Special-casing exit/die constant access doesn't sound like a cleaner solution than what we have now. Keeping them in the lexer/parser also keeps BC for AST-based tools.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, and it has side effects like defined(), constant() calls etc.

We may opt to special-case only statement-level "exit" and "die" constant fetches directly (i.e. handle in zend_compile_stmt, in the branch for constants).

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, that's actually a smarter way of doing it!

I'll see if I can implement this, as I still struggle with the compiler part.

Copy link
Member

@iluuu1994 iluuu1994 Feb 24, 2024

Choose a reason for hiding this comment

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

We may opt to special-case only statement-level "exit" and "die" constant fetches directly (i.e. handle in zend_compile_stmt, in the branch for constants).

I would prefer keeping parser support, as otherwise PHPStan and the likes need to adjust. I don't think there's a reason to break those. Edit: I guess lexer would be sufficient, as almost all of these tools use PHPParser, which defines its own AST. Still, I'd prefer letting the parser handle this.

Copy link
Member

Choose a reason for hiding this comment

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

I was about to suggest something similar, because exit; not actually calling the function but just throwing that unwind exception probably leads to observable behavioral differences in debuggers dependent on whether the user wrote exit; or exit();, no?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think we should constrain ourselves with respect to phpstan etc.; there are worse BC breaks to be had.
I think the lexer, parser and opcodes are aside from any compatibilty guarantees.

Copy link
Member Author

Choose a reason for hiding this comment

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

@ondrejmirtes has told me that this shouldn't be an issue if PHPParser still works, I'm asking @dseguy if this would affect Exakat or not.

Copy link

Choose a reason for hiding this comment

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

@exakat will need an adaptation, at the parser level. That is not an issue.

@Girgias Girgias force-pushed the exit-as-function branch 2 times, most recently from 0549f75 to 89aaa8f Compare May 1, 2024 15:07
Zend/zend_compile.c Outdated Show resolved Hide resolved
@Girgias Girgias marked this pull request as ready for review May 7, 2024 18:27
@dstogov
Copy link
Member

dstogov commented May 8, 2024

Where is the RFC?

Removing ability to use die and exit without brackets, may effect a lot of PHP users.
I wouldn't accept this into PHP-8.*.

Please consider the possibility of adding die/exit functions without removing opcodes.
(This is just a quick idea. May be it doesn't make sense)

@iluuu1994
Copy link
Member

@dstogov See zend_compile.c, the constants are turned into function calls.

That said, I'm also a bit sceptical about the proposed benefits. I think an RFC, or at least a list on the mailing list explaining the rationale would be reasonable.

@dstogov
Copy link
Member

dstogov commented May 8, 2024

I think an RFC, or at least a list on the mailing list explaining the rationale would be reasonable.

The PR name contains [RFC], but I didn't find the text.

@dstogov
Copy link
Member

dstogov commented May 8, 2024

@dstogov See zend_compile.c, the constants are turned into function calls.

Nice hack :), but it needs to be verified (constants may be used in places where calls are not allowed).

@Girgias
Copy link
Member Author

Girgias commented May 8, 2024

The RFC is now live: https://wiki.php.net/rfc/exit-as-function and I have sent an email to internals.

I'm still somewhat ill so I didn't manage to post it yesterday.

@Girgias Girgias force-pushed the exit-as-function branch 2 times, most recently from a9fd352 to 04c54f3 Compare May 11, 2024 14:14
@iluuu1994
Copy link
Member

iluuu1994 commented May 29, 2024

As discussed on the list, here's an approach that converts exit/die into the same AST as exit() in the parser. That avoids most changes in zend_compile.c, and preserves BC (for PHP code, but not extensions).
40751f0
It could be extended to handle T_DIE separately, for improved error messages.

And as discussed privately, the stub parser fails when run with a local PHP binary, because exit is still a keyword and not allowed as a function name. As the Makefile prefers local PHP binaries, this needs to be fixed in some other way (e.g. maybe through a magic prefix that the stub generator removes).

@rodrigoslayertech
Copy link

rodrigoslayertech commented Jul 7, 2024

Good RFC!
Another benefit is that the PHP developers will have a better experience when integrating SAPI CLI development with other non-CLI SAPIs.

For example, if there is a PHP project that integrates HTTP Servers with SAPI non-CLI (which is the PHP standard) with HTTP Server with SAPI CLI, it would be possible to disable exit as a function and prevent exit from completely killing the stack when the code runs in SAPI CLI. In non-CLI SAPIs, exit sends the response to the client, but in CLI, it takes the server offline.

@Girgias
Copy link
Member Author

Girgias commented Jul 7, 2024

Good RFC! Another benefit is that the developer will have a better experience when integrating SAPI CLI development with other non-CLI SAPIs.

For example, if there is a PHP project that integrates HTTP Servers with SAPI non-CLI (which is the PHP standard) with HTTP Server with SAPI CLI, it would be possible to disable exit as a function and prevent exit from completely killing the stack when the code runs in SAPI CLI. In non-CLI SAPIs, exit sends the response to the client, but in CLI, it takes the server offline.

Note that the RFC prevents adding exit() to the disabled_functions INI setting.

@rodrigoslayertech
Copy link

Note that the RFC prevents adding exit() to the disabled_functions INI setting.

Yes, but is that in a future scope. That's the intention, right?

@Girgias
Copy link
Member Author

Girgias commented Jul 7, 2024

Note that the RFC prevents adding exit() to the disabled_functions INI setting.

Yes, but is that in a future scope. That's the intention, right?

It is a possible idea, I don't have any plans on pursuing it as I don't want to argue about the merits of it. But you could do a follow-up RFC if you wanted to!

Copy link
Member

@TimWolla TimWolla left a comment

Choose a reason for hiding this comment

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

LGTM overall.

build/gen_stub.php Outdated Show resolved Hide resolved
ext/standard/tests/array/array_map_variation16.phpt Outdated Show resolved Hide resolved
ext/ftp/tests/server.inc Outdated Show resolved Hide resolved
@@ -83,9 +83,15 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
}
}

/* As exit() and die() are proper token/keywords an need to hack-around */
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if this is a grammatically correct sentence.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll fix it in the commit where I add the NEWS/UPGRADING entry

@Girgias Girgias merged commit a79c70f into php:master Aug 14, 2024
10 checks passed
@Girgias Girgias deleted the exit-as-function branch August 14, 2024 11:44
Comment on lines +10 to +13
function exit(string|int $code = 0): never {}

/** @alias exit */
function die(string|int $code = 0): never {}
Copy link
Contributor

Choose a reason for hiding this comment

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

The parameter names seem to be incorrect.

It should be $status, not $code.

The RFC explicitly uses that parameter name in the proposal: https://wiki.php.net/rfc/exit-as-function#proposal

It is also the name already used in the manual.

Lastly, the parameter name $status better covers what can be passed: either a status message or a status code.
While $code would read pretty weird when passing a message:

exit(code: 'message');

Copy link
Contributor

Choose a reason for hiding this comment

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

@Girgias Note: I'll happily try to provide a PR to fix this, including adding a test with named parameter use, but I wanted to check first if this was an oversight or if there was a particular reason for the parameter name change.

Copy link
Contributor

Choose a reason for hiding this comment

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

#15433 (draft as I've guestimated a patch ;-) )

jrfnl added a commit to jrfnl/php-src that referenced this pull request Aug 15, 2024
Follow up on 13483

As previously reported in php#13483 (comment):
> The parameter names seem to be incorrect.
>
> It should be `$status`, not `$code`.
>
> The RFC explicitly uses that parameter name in the proposal: https://wiki.php.net/rfc/exit-as-function#proposal
>
> It is also the name already used in the [manual](https://www.php.net/exit).
>
> Lastly, the parameter name `$status` better covers what can be passed: either a status _message_ or a status _code_.
> While `$code` would read pretty weird when passing a message:
> ```php
> exit(code: 'message');
> ```

This commit attempts to fix this.

Includes adding a test for exit/die using a named argument.
jrfnl added a commit to jrfnl/php-src that referenced this pull request Aug 15, 2024
Follow up on 13483

As previously reported in php#13483 (comment):

> The parameter names seem to be incorrect.
>
> It should be `$status`, not `$code`.
>
> The RFC explicitly uses that parameter name in the proposal: https://wiki.php.net/rfc/exit-as-function#proposal
>
> It is also the name already used in the [manual](https://www.php.net/exit).
>
> Lastly, the parameter name `$status` better covers what can be passed: either a status _message_ or a status _code_.
> While `$code` would read pretty weird when passing a message:
> ```php
> exit(code: 'message');
> ```

This commit attempts to fix this.

Includes adding a test for exit/die using a named argument.
jrfnl added a commit to jrfnl/php-src that referenced this pull request Aug 15, 2024
Follow up on 13483

As previously reported in php#13483 (comment):

> The parameter names seem to be incorrect.
>
> It should be `$status`, not `$code`.
>
> The RFC explicitly uses that parameter name in the proposal: https://wiki.php.net/rfc/exit-as-function#proposal
>
> It is also the name already used in the [manual](https://www.php.net/exit).
>
> Lastly, the parameter name `$status` better covers what can be passed: either a status _message_ or a status _code_.
> While `$code` would read pretty weird when passing a message:
> ```php
> exit(code: 'message');
> ```

This commit attempts to fix this.

Includes adding a test for exit/die using a named argument.
jrfnl added a commit to jrfnl/php-src that referenced this pull request Aug 15, 2024
Follow up on 13483

As previously reported in php#13483 (comment):

> The parameter names seem to be incorrect.
>
> It should be `$status`, not `$code`.
>
> The RFC explicitly uses that parameter name in the proposal: https://wiki.php.net/rfc/exit-as-function#proposal
>
> It is also the name already used in the [manual](https://www.php.net/exit).
>
> Lastly, the parameter name `$status` better covers what can be passed: either a status _message_ or a status _code_.
> While `$code` would read pretty weird when passing a message:
> ```php
> exit(code: 'message');
> ```

This commit attempts to fix this.

Includes adding a test for exit/die using a named argument.
jrfnl added a commit to jrfnl/php-src that referenced this pull request Aug 16, 2024
Follow up on 13483

As previously reported in php#13483 (comment):

> The parameter names seem to be incorrect.
>
> It should be `$status`, not `$code`.
>
> The RFC explicitly uses that parameter name in the proposal: https://wiki.php.net/rfc/exit-as-function#proposal
>
> It is also the name already used in the [manual](https://www.php.net/exit).
>
> Lastly, the parameter name `$status` better covers what can be passed: either a status _message_ or a status _code_.
> While `$code` would read pretty weird when passing a message:
> ```php
> exit(code: 'message');
> ```

This commit attempts to fix this.

Includes adding a test for exit/die using a named argument.
Girgias pushed a commit that referenced this pull request Aug 16, 2024
Follow up on 13483

As previously reported in #13483 (comment):

> The parameter names seem to be incorrect.
>
> It should be `$status`, not `$code`.
>
> The RFC explicitly uses that parameter name in the proposal: https://wiki.php.net/rfc/exit-as-function#proposal
>
> It is also the name already used in the [manual](https://www.php.net/exit).
>
> Lastly, the parameter name `$status` better covers what can be passed: either a status _message_ or a status _code_.
> While `$code` would read pretty weird when passing a message:
> ```php
> exit(code: 'message');
> ```

This commit attempts to fix this.

Includes adding a test for exit/die using a named argument.

Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
@petk
Copy link
Member

petk commented Aug 18, 2024

Should the UPGRADING.INTERNALS perhaps also note here that ZEND_EXIT doesn't exist anymore?

Because some extensions will now fail with the next PHP 8.4 release. For example, swoole-src

The list of extensions that uses the ZEND_EXIT is not that long:

  • uopz
  • xdebug
  • vld
  • pcov
  • taint
  • swoole

(some extensions here can probably be also archived and aren't that critical since they don't accept any PRs nor they seem to get moving anywhere)

@Girgias
Copy link
Member Author

Girgias commented Aug 18, 2024

Ah yes it should

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants