dunglas / frankenphp

🧟 The modern PHP app server
https://frankenphp.dev
MIT License
6.64k stars 218 forks source link

XDebug documentation #931

Open Radiergummi opened 1 month ago

Radiergummi commented 1 month ago

Describe you feature request

We'd like to use FrankenPHP for our Laravel application (running in Docker), but using it with XDebug isn't as straight-forward as the website claims it is:

In combination, that makes debugging hard to use, which has encouraged some of our developers to go back to dd()—ugh!

Hence, I would kindly like to ask for two thinks:

  1. Improved (that is, any kind of) documentation on debugging, XDebug setup, and debugging caveats that might apply.
  2. Possibly better support for breakpoints set and unset via the XDebug protocol; I'm really not competent in this regard, but my gut says the FrankenPHP worker needs to either reload if breakpoints are modified, or otherwise account for that. When using the built-in PHP debug server, this works without a hitch, so this must be possible in general.

We're using PHP 8.3, Xdebug 3.3.2, Laravel 11 with current Octane, and the latest FrankenPHP Docker image (as a base image for our own).

Radiergummi commented 1 month ago

On browsing the XDebug issue tracker, there's a related issue:

0002161: Breakpoints not updated when process is running
When a PHP is process is already running and you add a new breakpoint, it will never be triggered unless a script is restarted. https://bugs.xdebug.org/view.php?id=2161

Following that, Derick is just now implementing support for control sockets, allowing IDEs to inject new breakpoints into running PHP processes—such as the FrankenPHP worker.
IIUC, that means step debugging with XDebug was never actually possible without restarting FrankenPHP after configuring breakpoints?
Adding this information to the website would have saved us, and probably others too, a lot of time. At least to me it means that I'll revert from FrankenPHP back to the built-in webserver for now; not being able to use breakpoints in local development without restarting the server is a non-starter.

Edit: This is in no way meant to dismiss the amazing work of all FrankenPHP contributors. I'd just like to suggest clarifying the documentation on XDebug to make it clear step debugging doesn't really work for now, as the whole debugging story feels a little under-documented right now.

dunglas commented 1 month ago

There are a workaround for that: not using the worker-mode or setting the max number of requests to handle with the worker mode to 1 (which is very similar to disabling the worker mode entirely). It's what we do in Symfony Docker for instance (the worker mode is disabled in dev, and enabled in production).

Radiergummi commented 1 month ago

By setting max requests to 1, the synchronisation between the IDE and FrankenPHP is off-by-one—after setting a breakpoint, you need to request the old code once, the worker restarts and the breakpoint becomes active; after disabling the breakpoint, you again need to do an additional request, which triggers the (now-gone) breakpoint again, and only after that things are back to normal.

However, disabling the worker mode is the way to go. This isn't as straightforward in Laravel Octane, which doesn't seem to anticipate someone might want to do this. By using a custom Dockerfile adapted from the one included with Octane, it's doable though.
If someone happens to stumble upon this, here's our solution:

(worker) {
    worker "{$APP_PUBLIC_PATH}/frankenphp-worker.php" {$CADDY_SERVER_WORKER_COUNT}
}

{
    {$CADDY_GLOBAL_OPTIONS}

    admin {$CADDY_SERVER_ADMIN_HOST}:{$CADDY_SERVER_ADMIN_PORT}

    frankenphp {
        {$CADDY_SERVER_FRANKENPHP_OPTIONS:import worker}
    }

    {$CADDY_GLOBAL_EXTRA_DIRECTIVES}
}

# (Rest of the default config below)

All it requires you to do locally is set the CADDY_SERVER_FRANKENPHP_OPTIONS environment variable to an empty string locally, using your Dockerfile or compose stack or whatever.

Thank you for pointing me in the right direction, the Symfony repository was very helpful. This is probably a Laravel issue after all, but I'm weary of Taylor silently dismissing PRs. This issue can be closed then, I guess. Again, thank you!

dunglas commented 1 month ago

As the co-author of the FrankenPHP support for Octane, I'm probably the one to blame! IMHO a docs PR on Octane should to the trick. Or maybe an additional (temporarily) debug flag?

Radiergummi commented 1 month ago

The issue is, in my opinion, that the default Caddyfile included in Octane always uses the worker mode. I don't know how others solve this, but I try to keep our development environment as close to production as possible, and thus also want to use Octane locally.

I think the most Laravel-y thing would be to enable worker mode only if APP_ENV is not local, or probably check APP_DEBUG is true?

dunglas commented 1 month ago

Actually the issue occurs only when using Xdebug, maybe could we detect if XDebug is enabled or something like that.

Radiergummi commented 1 month ago

@dunglas just so I can understand this correctly; from the default Octane config, FrankenPHP will start in worker mode, correct? So for changes made to the code to become live in development, users must set the maximum number of requests handled by workers to 1.
If that is true, what benefit does the worker mode offer for development, if Symfony Docker doesn't use it here either?

withinboredom commented 1 month ago

Worker mode is an optimization, for now anyway. As more and more devs get used to working with it (like these changes to xdebug) this will become less truer over time. Generally, you disable optimizations (like a compiled container, etc) during development to make it easier/faster to get feedback loops and iterate.

For now, there are things you have to manually do (kinda like not that long ago, we had to manually disable compiled DI containers in dev, now it's automated, most of the time). Things get better, all the time.

Radiergummi commented 1 month ago

Right; that matches my understanding. So, maybe to rephrase my intent—couldn't we, then, update the Octane integration such that it would only enable worker mode if the application is running in production?
This would improve the development workflow for Laravel users, and work out of the box, without any userland modifications necessary.

withinboredom commented 1 month ago

I assume they accept PRs, so I assume it would be possible.