|
3 | 3 | Boot your application once and keep it in memory. |
4 | 4 | FrankenPHP will handle incoming requests in a few milliseconds. |
5 | 5 |
|
6 | | -## Warning about stateful design |
| 6 | +## Designing for Stateful Applications |
7 | 7 |
|
8 | | -Unlike with the traditional PHP-FPM model, the application remains loaded in memory between requests. Consequently, any state stored in your services (object properties, singletons, etc.) will be preserved and shared across successive requests handled by the same worker. This can lead to data leaks and memory issues, or inconsistent states if your application is not designed for it. |
9 | | -The following article summarizes this issue and explains how to fix it, notably for Symfony applications using ResetInterface to ensure your services are "reset" for every new request. |
| 8 | +Unlike the traditional PHP-FPM model, the application remains **loaded in memory** between requests. Consequently, any state stored within your services (object properties, singletons, etc.) will be retained and shared between successive requests handled by the same worker. This can lead to data and memory leaks or inconsistent states if your application is not designed for this. |
10 | 9 |
|
11 | | -**Additional Resources:** |
| 10 | +### Designing a Stateless Application |
12 | 11 |
|
13 | | -- Article: [Getting your Symfony app ready for Swoole, RoadRunner, and FrankenPHP](https://dev.to/sergiid/getting-symfony-app-ready-for-swoole-roadrunner-and-frankenphp-no-ai-involved-2d0g) |
14 | | -- Symfony: [The Messenger](https://symfony.com/doc/current/messenger.html#stateless-worker) documentation also discusses this "stateless worker" concept. |
15 | | -- Tool: [phanalist](https://github.com/denzyldick/phanalist) is a static analyzer that can help you detect "stateful" services in your code. |
| 12 | +The challenge is managing the lifecycle of your objects, especially those that are shared instances by the dependency container. |
| 13 | + |
| 14 | +**Key Principles to Follow:** |
| 15 | + |
| 16 | +- **Avoid Global State:** Global variables and static properties must not be modified. |
| 17 | +- **Caution with Services:** Avoid storing values via setters or modifying public properties of a shared service, as these changes will affect the next request. |
| 18 | +- **Prioritize New Objects:** Anything tied to the request or user parameters must be returned as a new object for each request. |
| 19 | + |
| 20 | +### Issue Detection (Static Analysis) |
| 21 | + |
| 22 | +Static analysis tools can help you identify potentially stateful services. These tools primarily check for: |
| 23 | + |
| 24 | +- The use of mutable public or static properties in shared services. |
| 25 | +- The use of functions like `die()` or `exit()`. |
| 26 | + |
| 27 | +A notable tool is **[denzyldick/phanalist](https://github.com/denzyldick/phanalist)**. After installation, you can run it specifically with rule E0012 (for "Service compatibility with Shared Memory Model") to get a list of problematic areas in your code. |
| 28 | + |
| 29 | +### The Reset Interface (Symfony) |
| 30 | + |
| 31 | +The ideal approach is to design your services to be naturally **`stateless`**. |
| 32 | + |
| 33 | +However, in cases where it is difficult to make a shared service completely stateless (e.g., a service with an internal cache or request-specific configuration), you can use the **`Symfony\Contracts\Service\ResetInterface`**. |
| 34 | + |
| 35 | +When a service implements this interface, its `reset()` method is automatically called by the service container at the end of each request. This allows you to specifically clean up the service's internal state (e.g., emptying an internal cache, resetting properties...). |
16 | 36 |
|
17 | 37 | ## Starting Worker Scripts |
18 | 38 |
|
|
0 commit comments