fix: exit() and dd() support in worker mode (#1946)

* Verifies exit behavior.

* formatting

* Checks for actual exit.

* Fixes test.

* Fixes test.

* Update testdata/dd.php

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>

---------

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
This commit is contained in:
Alexander Stecher
2025-10-28 10:57:50 +01:00
committed by GitHub
parent fb1f46808e
commit bf6e6534f6
3 changed files with 65 additions and 5 deletions

View File

@@ -1423,3 +1423,27 @@ func TestWorkerMatchDirectiveWithoutFileServer(t *testing.T) {
// the request should completely fall through the php_server module
tester.AssertGetResponse("http://localhost:"+testPort+"/static.txt", http.StatusNotFound, "Request falls through")
}
func TestDd(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
}
http://localhost:`+testPort+` {
php {
worker ../testdata/dd.php 1 {
match *
}
}
`, "caddyfile")
// simulate Symfony's dd()
tester.AssertGetResponse(
"http://localhost:"+testPort+"/some-path?output=dump123",
http.StatusInternalServerError,
"dump123",
)
}

View File

@@ -464,12 +464,16 @@ PHP_FUNCTION(frankenphp_handle_request) {
/*
* If an exception occurred, print the message to the client before
* closing the connection and bailout.
* closing the connection.
*/
if (EG(exception) && !zend_is_unwind_exit(EG(exception)) &&
!zend_is_graceful_exit(EG(exception))) {
zend_exception_error(EG(exception), E_ERROR);
zend_bailout();
if (EG(exception)) {
if (!zend_is_unwind_exit(EG(exception)) &&
!zend_is_graceful_exit(EG(exception))) {
zend_exception_error(EG(exception), E_ERROR);
} else {
/* exit() will jump directly to after php_execute_script */
zend_bailout();
}
}
frankenphp_worker_request_shutdown();

32
testdata/dd.php vendored Normal file
View File

@@ -0,0 +1,32 @@
<?php
// simulate Symfony's dd() behavior
// see https://github.com/symfony/http-kernel/blob/7.3/DataCollector/DumpDataCollector.php#L216
class Dumper
{
private string $message;
public function dump(string $message): void
{
http_response_code(500);
$this->message = $message;
}
public function __destruct()
{
if (isset($this->message)) {
echo $this->message;
}
}
}
$dumper = new Dumper();
while (frankenphp_handle_request(function () use ($dumper) {
$dumper->dump($_GET['output'] ?? '');
exit(1);
})) {
// keep handling requests
}
echo "we should never reach here\n";