Skip to content

Commit 3535c7a

Browse files
authored
fix: worker mode events cleanup (#9997)
1 parent 9357570 commit 3535c7a

File tree

8 files changed

+92
-6
lines changed

8 files changed

+92
-6
lines changed

app/Config/WorkerMode.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ class WorkerMode
4040
'cache',
4141
];
4242

43+
/**
44+
* Reset Event Listeners
45+
*
46+
* List of event names whose listeners should be removed between requests.
47+
* Use this if you register event listeners inside other event callbacks
48+
* (rather than at the top level of Config/Events.php), which would cause
49+
* them to accumulate across requests in worker mode.
50+
*
51+
* @var list<string>
52+
*/
53+
public array $resetEventListeners = [];
54+
4355
/**
4456
* Force Garbage Collection
4557
*

system/Commands/Worker/Views/frankenphp-worker.php.tpl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,10 @@ while (frankenphp_handle_request($handler)) {
122122
// Reset services except persistent ones
123123
Services::resetForWorkerMode($workerConfig);
124124

125+
// Reset event listeners
126+
Events::cleanupForWorkerMode($workerConfig->resetEventListeners);
127+
125128
if (CI_DEBUG) {
126-
Events::cleanupForWorkerMode();
127129
Services::toolbar()->reset();
128130
}
129131
}

system/Events/Events.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,18 @@ public static function getPerformanceLogs()
289289
* Cleanup performance log and request-specific listeners for worker mode.
290290
*
291291
* Called at the END of each request to clean up state.
292+
*
293+
* @param list<string> $resetEventListeners Additional event names to reset.
292294
*/
293-
public static function cleanupForWorkerMode(): void
295+
public static function cleanupForWorkerMode(array $resetEventListeners = []): void
294296
{
295-
static::$performanceLog = [];
296-
static::removeAllListeners('DBQuery');
297+
if (CI_DEBUG) {
298+
static::$performanceLog = [];
299+
static::removeAllListeners('DBQuery');
300+
}
301+
302+
foreach ($resetEventListeners as $event) {
303+
static::removeAllListeners($event);
304+
}
297305
}
298306
}

user_guide_src/source/changelogs/v4.7.1.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ Message Changes
2424
Changes
2525
*******
2626

27+
Events
28+
======
29+
30+
- **Worker Mode:** :php:func:`Events::cleanupForWorkerMode()` now accepts an optional
31+
``$resetEventListeners`` array parameter, corresponding to the new
32+
``$resetEventListeners`` property in ``Config\WorkerMode``. This allows users to
33+
declare event names that should be cleaned up between requests when listeners are
34+
registered inside event callbacks. See :ref:`worker-mode-reset-event-listeners`.
35+
2736
Others
2837
======
2938

user_guide_src/source/installation/upgrade_471.rst

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ Please refer to the upgrade instructions corresponding to your installation meth
1616
Mandatory File Changes
1717
**********************
1818

19+
Worker Mode
20+
===========
21+
22+
If you are using Worker Mode, you must update **public/frankenphp-worker.php** after
23+
upgrading. The easiest way is to re-run the install command:
24+
25+
.. code-block:: console
26+
27+
php spark worker:install --force
28+
1929
****************
2030
Breaking Changes
2131
****************
@@ -44,12 +54,14 @@ and it is recommended that you merge the updated versions with your application:
4454
Config
4555
------
4656

47-
- @TODO
57+
- app/Config/WorkerMode.php
58+
- ``Config\WorkerMode::$resetEventListeners`` has been added, with a default
59+
value set to ``[]``. See :ref:`worker-mode-reset-event-listeners` for details.
4860

4961
All Changes
5062
===========
5163

5264
This is a list of all files in the **project space** that received changes;
5365
many will be simple comments or formatting that have no effect on the runtime:
5466

55-
- @TODO
67+
- app/Config/WorkerMode.php

user_guide_src/source/installation/worker_mode.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ Option Type Description
186186
in this list are destroyed after each request to prevent state leakage.
187187
Default: ``['autoloader', 'locator', 'exceptions', 'commands',
188188
'codeigniter', 'superglobals', 'routes', 'cache']``
189+
**$resetEventListeners** array Event names whose listeners are removed between requests. Use this
190+
when you register event listeners inside other event callbacks rather
191+
than at the top level of **Config/Events.php**, which would cause them
192+
to accumulate across requests. Default: ``[]``
189193
**$forceGarbageCollection** bool Whether to force garbage collection after each request.
190194
``true`` (default, recommended): Prevents memory leaks.
191195
``false``: Relies on PHP's automatic garbage collection.
@@ -214,6 +218,29 @@ Service Purpose
214218
state management can cause data leakage between requests. Only persist services
215219
that are truly stateless or manage their own request isolation.
216220

221+
.. _worker-mode-reset-event-listeners:
222+
223+
Reset Event Listeners
224+
=====================
225+
226+
.. versionadded:: 4.7.1
227+
228+
Event listeners registered at the top level of **Config/Events.php** are loaded once
229+
at worker startup and persist correctly across requests. However, if you register a
230+
listener inside another event's callback, it will be re-registered on every request
231+
and accumulate:
232+
233+
.. literalinclude:: worker_mode/001.php
234+
235+
To clean up such listeners between requests, add the event name to
236+
``$resetEventListeners`` in **app/Config/WorkerMode.php**:
237+
238+
.. literalinclude:: worker_mode/002.php
239+
240+
.. note:: The recommended approach is to register listeners at the top level of
241+
**Config/Events.php** instead of inside callbacks. Use ``$resetEventListeners``
242+
only when registering inside a callback is unavoidable.
243+
217244
**********************
218245
Optimize Configuration
219246
**********************
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
use CodeIgniter\Events\Events;
4+
5+
// This runs every request — the 'my_event' listener stacks up indefinitely
6+
Events::on('pre_system', static function (): void {
7+
Events::on('my_event', 'MyClass::myMethod');
8+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Config;
4+
5+
class WorkerMode extends \CodeIgniter\Config\WorkerMode
6+
{
7+
public array $resetEventListeners = ['my_event'];
8+
}

0 commit comments

Comments
 (0)