Add FrankenPHP worker mode support - #51
Merged
Merged
Conversation
Collaborator
|
@pronskiy thanks for this. Took a look and the current implementation looks good for the issues that it tries to solve. However, I think we need to check a couple more things that may be affected by our implementation:
|
carlos-granados
approved these changes
Apr 22, 2026
- Introduced `frankenphp.c` and `frankenphp.h` to hook into FrankenPHP's per-request lifecycle using `sapi_module.activate` and `sapi_module.deactivate`. - Integrated per-request debugger resets, trigger detection, and breakpoint polling mechanism for FrankenPHP workers. - Updated existing modules (e.g., `handler_dbgp`, `debugger.c`) to support queued command handling and lifecycle management. - Adjusted build files to include new FrankenPHP-specific sources.
Provides a self-contained reproducer for the new sapi_module.activate /
deactivate hooks added in src/debugger/frankenphp.c.
- tests/frankenphp/Dockerfile: multi-stage build that compiles
php_debugger.so against FrankenPHP's bundled ZTS PHP and installs it
as a Zend extension with xdebug.mode=debug and start_with_request=trigger.
- tests/frankenphp/Caddyfile: minimal config registering /app/worker.php
as the FrankenPHP worker, with explicit http:// scheme to avoid the
default auto-https redirect.
- tests/frankenphp/app/{worker.php,index.php}: long-lived
frankenphp_handle_request() loop and a sample request script with an
obvious breakpoint location.
- tests/frankenphp/entrypoint.sh: rewrites xdebug.client_host /
xdebug.client_port from XDEBUG_CLIENT_HOST / XDEBUG_CLIENT_PORT env
vars before exec'ing FrankenPHP (PHP INI has no native fallback
syntax for env-var defaults).
- tests/frankenphp/README.md: build/run instructions, one-time PhpStorm
Server + path-mapping setup, the four verification scenarios
(no-trigger skip, trigger pause, multi-request worker reuse, and the
xdebug_dbgp_poll_pending breakpoint-between-requests test), and a
troubleshooting table.
- .dockerignore: keeps host build artifacts (Makefile, *.dep, *.lo,
.libs, ...) out of the build context — without this, absolute host
paths baked into the local Makefile break the in-container build.
- .gitignore: adds tests/frankenphp/**/*.{php,sh} allowlist exceptions
so the test files aren't swallowed by the blanket *.php / *.sh rules
used to ignore stray test artifacts.
pronskiy
force-pushed
the
feature/frankenphp
branch
from
May 3, 2026 11:44
475822c to
5920eab
Compare
|
Hi @pronskiy sorry I missed your initial comment in dunglas/symfony-docker#868 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes
php-debuggerwork under FrankenPHP worker mode. In worker mode a single long-lived PHP process services many requests viafrankenphp_handle_request(), soMINIT/RINIT/RSHUTDOWN/MSHUTDOWNonly fire once per worker — not per request. This meansthe existing IDE handshake (done at
RINIT), the trigger detection (alsoRINIT), and the per-request socket teardown(
POST_ZEND_DEACTIVATE) all only happen for the very first request the worker handles. Subsequent requests are not debuggable.This PR ports the approach from upstream Xdebug PR into php-debugger:
MINIT(strcmp(sapi_module.name, "frankenphp") == 0) and wrapssapi_module.activate/deactivateto drive a per-request debugger lifecycle inside the worker loop.XG_DBG(detached/no_exec/breakpoints_allowed)andcontext.do_*flags, scans rawSG(request_info).cookie_dataandquery_stringforXDEBUG_SESSION/XDEBUG_TRIGGER/PHP_DEBUGGER_*(superglobals aren'tbuilt yet at this point), then arms
do_connect_to_clientso the existing init path picks up where it would on a normal CGI/CLIrequest.
handler->remote_deinitso the next request gets a fresh one.xdebug_dbgp_poll_pending()— a non-blockingselect()loop on the DBGp socket — and calls it from the activate hook sobreakpoint_set/breakpoint_removecommands the IDE pushed while the worker was busy with another request actually get processedbefore the next request runs. Continuation commands (
run/step_*) are logged and ignored — they're only meaningful at a stop.MSHUTDOWNrestores the original SAPI pointers viaxdebug_frankenphp_mshutdown().For non-FrankenPHP SAPIs the new code is a strict no-op; the existing
RINIT/RSHUTDOWNlifecycle is unchanged.Closes the equivalent of dunglas/symfony-docker#868 for php-debugger.
Test plan
Automated regression (already passing):
phpize && ./configure --enable-php-debugger && make -j— clean build, zero warnings on the new code (PHP 8.5 ZTS).run-xdebug-tests.php tests/debugger/): 240 passed, 0 new failures. The single failure isbug02090.phpt(FFI/__callStaticsegfault), which is pre-existing onmainand unrelated to these hooks.strcmp(sapi_module.name, "frankenphp") == 0.Manual FrankenPHP smoke test (see
tests/frankenphp/README.mdfor full instructions):