thePHP.cc Logo Deutsch Contact
Understanding FrankenPHP

Understanding FrankenPHP

This article is based on an explorative presentation on FrankenPHP I gave recently, where I acknowledged the uncertainties in my current understanding. And rightly so: Because as it turned out, some of my initial assumptions on its technical operation were incorrect, which led to valuable corrections from the audience. This piece reflects my updated understanding of how FrankenPHP actually works.

The Traditional PHP Execution Landscape

To understand why FrankenPHP is generating interest in the PHP community, it helps to look at how we've traditionally executed PHP applications. Over the years, we've relied on several execution models:

These commonly used approaches share one fundamental characteristic from PHP's runtime perspective: process isolation. Meaning, each request is handled in isolation, with the PHP environment being reset between requests.

Enter FrankenPHP

FrankenPHP embeds PHP directly into the Caddy web server — an approach that almost feels like going back to the roots, like Apache's mod_php integration.

In its default "classic" mode, nothing fundamentally changes for your application and they will run much as they always have, maintaining the familiar process isolation model we PHP developers have grown accustomed to.

The real difference emerges when you enable FrankenPHP's worker mode.

Worker Mode: Where Things Get Interesting

This is where my initial understanding went a bit astray. Based on my reading of the documentation and the wording of the relevant passages, I believed that a single worker would spawn multiple threads, and that you'd need to start multiple workers to ensure reliability. This, luckily, turned out to be incorrect.

What actually happens is quite more stable and a bit less dangerous: FrankenPHP does indeed start multiple workers as threads, but each worker handles only one request at a time.

The key difference from traditional PHP runtimes is that workers don't exit after serving a request — they persist and wait for the next one. This seemingly small architectural change can have significant performance implications.

It's worth noting that using worker threads isn't technically a requirement for persisting state across multiple requests. While PHP-FPM doesn't currently support this, it could theoretically be enhanced with similar persistent worker capabilities.

The Performance Benefits

The performance gains from worker mode become noticeable when your application requires significant bootstrapping. Think about frameworks that need to:

In traditional PHP execution models, this bootstrapping happens for every single request. With FrankenPHP's persistent workers, this initialization work happens once per worker, not once per request.

It is thus important to understand that FrankenPHP will keep the overall runtime performance of PHP itself unchanged. If your application is CPU-intensive or I/O-heavy during request processing, you won't see dramatic improvements in those areas - if it all. The gains are specifically in eliminating repeated bootstrapping overhead.

While technically using threads internally, FrankenPHP does not offer or add async capabilities to PHP itself like we get, for instance, with Node.js. I/O wait remains I/O wait and when your application waits for database queries or external API calls, the worker is blocked just as it would be in traditional PHP setups. This also means that features like WebSockets or Server-Sent Events (SSE) still tie up one worker for the entire connection duration, which can quickly become a scalability bottleneck.

However, the persistent nature of workers does open up possibilities for work to be done outside the request lifecycle — before or after request processing — which wasn't considered practical with traditional models.

Giving Up Process Isolation

The loss of process isolation comes with important considerations and quite some side effects. While FrankenPHP makes significant efforts to clean the environment between requests, the reality is that global state leaks from one request to the next. After all, that is at least in parts the very idea of reusing a worker. But of course unexpected error states, invalid cached data, or global configuration changes like set_locale persist along and may negatively affect the next request.

Additionally, FrankenPHP's worker mode requires thread-safe PHP (ZTS - Zend Thread Safety), which means not all PHP extensions are available. Some extensions that work perfectly with traditional PHP setups may not be compatible with the threaded environment that worker mode requires.

This places new demands on application architecture and framework design. Code that previously relied on the "clean slate" nature of per-request process isolation now needs to be more careful about:

TL;DR - just tell me: Shall We Switch?

FrankenPHP represents an interesting evolution in PHP execution models, offering performance benefits for applications with significant bootstrapping overhead while requiring more thoughtful approaches to state management and application architecture.

As with any architectural change, the benefits need to be weighed against the additional complexity and the need for more disciplined coding practices. For applications where bootstrapping represents a significant portion of request overhead, FrankenPHP's worker mode offers a compelling alternative to traditional execution models. If you're using only a lightweight bootstrap or no framework at all, chances are, a switch to FrankenPHP is not going to give you much in terms of performance.

FrankenPHP isn't the first to implement this approach by the way — solutions like RoadRunner have explored similar persistent worker concepts for PHP applications. However, FrankenPHP's integration with Caddy brings additional advantages, including built-in automatic HTTPS, HTTP/3 support, and Caddy's powerful configuration system and extensions.

So, switch? The answer to this question isn't easy as it highly depends on your environment as well as the (performance) requirements. Feel free to reach out if you're considering these technologies for your projects. As a consultant, I can advise and help with FrankenPHP implementations or explore alternatives that might be better suited to your specific use case.