Skip to content

Commit 1c0e62d

Browse files
refactor: rename PHP API
1 parent 83b6757 commit 1c0e62d

37 files changed

+122
-130
lines changed

background_worker.go

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,11 @@ func (l *backgroundWorkerLookup) Resolve(name string) *backgroundWorkerRegistry
5858
}
5959

6060
type backgroundWorkerRegistry struct {
61-
entrypoint string
62-
num int // threads per background worker (0 = lazy-start with 1 thread)
63-
maxWorkers int // max lazy-started instances (0 = unlimited)
64-
autoStartNames []string // names to start at boot when num >= 1
65-
mu sync.Mutex
66-
workers map[string]*backgroundWorkerState
61+
entrypoint string
62+
num int // threads per background worker (0 = lazy-start with 1 thread)
63+
maxWorkers int // max lazy-started instances (0 = unlimited)
64+
mu sync.Mutex
65+
workers map[string]*backgroundWorkerState
6766
}
6867

6968
func newBackgroundWorkerRegistry(entrypoint string) *backgroundWorkerRegistry {
@@ -84,10 +83,6 @@ func (registry *backgroundWorkerRegistry) SetNum(num int) {
8483
registry.num = num
8584
}
8685

87-
func (registry *backgroundWorkerRegistry) AddAutoStartNames(names ...string) {
88-
registry.autoStartNames = append(registry.autoStartNames, names...)
89-
}
90-
9186
func (registry *backgroundWorkerRegistry) SetMaxWorkers(max int) {
9287
registry.maxWorkers = max
9388
}
@@ -125,7 +120,6 @@ func buildBackgroundWorkerLookups(workers []*worker, opts []workerOpt) map[strin
125120
if phpName != "" && phpName != w.fileName {
126121
// Named background worker
127122
if o.num > 0 {
128-
registry.AddAutoStartNames(phpName)
129123
registry.SetNum(o.num)
130124
}
131125
lookup.AddNamed(phpName, registry)
@@ -271,15 +265,15 @@ func getLookup(thread *phpThread) *backgroundWorkerLookup {
271265
return nil
272266
}
273267

274-
// go_frankenphp_worker_get_vars starts background workers if needed, waits for them
268+
// go_frankenphp_get_vars starts background workers if needed, waits for them
275269
// to be ready, takes read locks, copies vars via C helper, and releases locks.
276270
// All locking/unlocking happens within this single Go call.
277271
//
278272
// callerVersions/outVersions: if callerVersions is non-nil and all versions match,
279273
// the copy is skipped entirely (returns 1). outVersions receives current versions.
280274
//
281-
//export go_frankenphp_worker_get_vars
282-
func go_frankenphp_worker_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C.size_t, nameCount C.int, timeoutMs C.int, returnValue *C.zval, callerVersions *C.uint64_t, outVersions *C.uint64_t) *C.char {
275+
//export go_frankenphp_get_vars
276+
func go_frankenphp_get_vars(threadIndex C.uintptr_t, names **C.char, nameLens *C.size_t, nameCount C.int, timeoutMs C.int, returnValue *C.zval, callerVersions *C.uint64_t, outVersions *C.uint64_t) *C.char {
283277
thread := phpThreads[threadIndex]
284278
lookup := getLookup(thread)
285279
if lookup == nil {
@@ -365,13 +359,13 @@ func go_frankenphp_worker_get_vars(threadIndex C.uintptr_t, names **C.char, name
365359
return nil
366360
}
367361

368-
//export go_frankenphp_worker_set_vars
369-
func go_frankenphp_worker_set_vars(threadIndex C.uintptr_t, varsPtr unsafe.Pointer, oldPtr *unsafe.Pointer) *C.char {
362+
//export go_frankenphp_set_vars
363+
func go_frankenphp_set_vars(threadIndex C.uintptr_t, varsPtr unsafe.Pointer, oldPtr *unsafe.Pointer) *C.char {
370364
thread := phpThreads[threadIndex]
371365

372366
bgHandler, ok := thread.handler.(*backgroundWorkerThread)
373367
if !ok || bgHandler.worker.backgroundWorker == nil {
374-
return C.CString("frankenphp_worker_set_vars() can only be called from a background worker")
368+
return C.CString("frankenphp_set_vars() can only be called from a background worker")
375369
}
376370

377371
sk := bgHandler.worker.backgroundWorker

docs/background-workers.md

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Background Workers
22

33
Background workers are long-running PHP scripts that run outside the HTTP request cycle.
4-
They observe their environment and publish configuration that HTTP [workers](worker.md) can read in real time.
4+
They observe their environment and publish variables that HTTP [workers](worker.md) can read in real time.
55

66
## How It Works
77

88
1. A background worker runs its own event loop (subscribe to Redis, watch files, poll an API...)
9-
2. It calls `frankenphp_worker_set_vars()` to publish a snapshot of key-value pairs
10-
3. HTTP workers call `frankenphp_worker_get_vars()` to read the latest snapshot
9+
2. It calls `frankenphp_set_vars()` to publish a snapshot of key-value pairs
10+
3. HTTP workers call `frankenphp_get_vars()` to read the latest snapshot
1111
4. The first `get_vars()` call blocks until the background worker has published - no startup race condition
1212

1313
## Configuration
@@ -42,15 +42,15 @@ example.com {
4242

4343
## PHP API
4444

45-
### `frankenphp_worker_get_vars(string|array $name, float $timeout = 30.0): array`
45+
### `frankenphp_get_vars(string|array $name, float $timeout = 30.0): array`
4646

47-
Starts a background worker (at-most-once) and returns its published variables.
47+
Starts a background worker (at-most-once) and returns its published vars.
4848

4949
```php
50-
$redis = frankenphp_worker_get_vars('redis-watcher');
50+
$redis = frankenphp_get_vars('redis-watcher');
5151
// ['MASTER_HOST' => '10.0.0.1', 'MASTER_PORT' => 6379]
5252

53-
$all = frankenphp_worker_get_vars(['redis-watcher', 'feature-flags']);
53+
$all = frankenphp_get_vars(['redis-watcher', 'feature-flags']);
5454
// ['redis-watcher' => [...], 'feature-flags' => [...]]
5555
```
5656

@@ -60,18 +60,19 @@ $all = frankenphp_worker_get_vars(['redis-watcher', 'feature-flags']);
6060
- Throws `RuntimeException` on timeout, missing entrypoint, or background worker crash
6161
- Works in both worker and non-worker mode
6262

63-
### `frankenphp_worker_set_vars(array $vars): void`
63+
### `frankenphp_set_vars(array $vars): void`
6464

65-
Publishes a snapshot of key-value pairs from inside a background worker.
66-
Each call **replaces** the entire snapshot atomically.
65+
Publishes vars from inside a background worker.
66+
Each call **replaces** the entire vars array atomically.
6767
If the new value is identical (`===`) to the previous one, the call is a no-op.
6868

69-
Supported types: `null`, `bool`, `int`, `float`, `string`, `array` (nested), and **enums**.
69+
Values can be `null`, scalars (`bool`, `int`, `float`, `string`), nested `array`s, or **enum**s.
70+
Objects, resources, and references are rejected.
7071

7172
- Throws `RuntimeException` if not called from a background worker context
7273
- Throws `ValueError` if values contain objects, resources, or references
7374

74-
### `frankenphp_worker_get_signaling_stream(): resource`
75+
### `frankenphp_get_worker_handle(): resource`
7576

7677
Returns a readable stream for receiving signals from FrankenPHP.
7778
On shutdown or restart, the stream is closed - `fgets()` returns `false` (EOF).
@@ -81,7 +82,7 @@ Use `stream_select()` to wait between iterations instead of `sleep()`:
8182
function background_worker_should_stop(float $timeout = 0): bool
8283
{
8384
static $stream;
84-
$stream ??= frankenphp_worker_get_signaling_stream();
85+
$stream ??= frankenphp_get_worker_handle();
8586
$s = (int) $timeout;
8687

8788
return match (@stream_select(...[[$stream], [], [], $s, (int) (($timeout - $s) * 1e6)])) {
@@ -118,7 +119,7 @@ function run_config_watcher(): void
118119
$redis->pconnect('127.0.0.1');
119120

120121
do {
121-
frankenphp_worker_set_vars([
122+
frankenphp_set_vars([
122123
'maintenance' => (bool) $redis->get('maintenance_mode'),
123124
'feature_flags' => json_decode($redis->get('features'), true),
124125
]);
@@ -134,23 +135,23 @@ to integrate the signaling stream into the event loop:
134135
```php
135136
function run_redis_watcher(): void
136137
{
137-
$signalingStream = frankenphp_worker_get_signaling_stream();
138+
$signalingStream = frankenphp_get_worker_handle();
138139
$sentinel = Amp\Redis\createRedisClient('tcp://sentinel-host:26379');
139140

140141
$subscription = $sentinel->subscribe('+switch-master');
141142

142143
Amp\async(function () use ($subscription) {
143144
foreach ($subscription as $message) {
144145
[$name, $oldIp, $oldPort, $newIp, $newPort] = explode(' ', $message);
145-
frankenphp_worker_set_vars([
146+
frankenphp_set_vars([
146147
'MASTER_HOST' => $newIp,
147148
'MASTER_PORT' => (int) $newPort,
148149
]);
149150
}
150151
});
151152

152153
$master = $sentinel->rawCommand('SENTINEL', 'get-master-addr-by-name', 'mymaster');
153-
frankenphp_worker_set_vars([
154+
frankenphp_set_vars([
154155
'MASTER_HOST' => $master[0],
155156
'MASTER_PORT' => (int) $master[1],
156157
]);
@@ -174,7 +175,7 @@ $app = new App();
174175
$app->boot();
175176

176177
while (frankenphp_handle_request(function () use ($app) {
177-
$config = frankenphp_worker_get_vars('config-watcher');
178+
$config = frankenphp_get_vars('config-watcher');
178179

179180
$_SERVER += ['APP_REDIS_HOST' => $config['MASTER_HOST'], 'APP_REDIS_PORT' => $config['MASTER_PORT']];
180181
$app->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
@@ -186,8 +187,8 @@ while (frankenphp_handle_request(function () use ($app) {
186187
### Graceful Degradation
187188

188189
```php
189-
if (function_exists('frankenphp_worker_get_vars')) {
190-
$config = frankenphp_worker_get_vars('config-watcher');
190+
if (function_exists('frankenphp_get_vars')) {
191+
$config = frankenphp_get_vars('config-watcher');
191192
} else {
192193
$config = ['MASTER_HOST' => getenv('REDIS_HOST') ?: '127.0.0.1'];
193194
}

frankenphp.c

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -838,54 +838,53 @@ PHP_FUNCTION(frankenphp_handle_request) {
838838
/* Persistent zval helpers: validation, deep-copy, immutable detection, enums */
839839
#include "bg_worker_vars.h"
840840

841-
PHP_FUNCTION(frankenphp_worker_set_vars) {
842-
zval *vars_array = NULL;
841+
PHP_FUNCTION(frankenphp_set_vars) {
842+
zval *vars = NULL;
843843

844844
ZEND_PARSE_PARAMETERS_START(1, 1);
845-
Z_PARAM_ARRAY(vars_array);
845+
Z_PARAM_ARRAY(vars);
846846
ZEND_PARSE_PARAMETERS_END();
847847

848848
/* Skip if the new value is identical to the last one.
849849
* Always update the cache so the next comparison can use pointer equality. */
850850
if (Z_TYPE(last_set_vars_zval) != IS_UNDEF &&
851-
zend_is_identical(vars_array, &last_set_vars_zval)) {
851+
zend_is_identical(vars, &last_set_vars_zval)) {
852852
zval_ptr_dtor(&last_set_vars_zval);
853-
ZVAL_COPY(&last_set_vars_zval, vars_array);
853+
ZVAL_COPY(&last_set_vars_zval, vars);
854854
return;
855855
}
856856

857-
HashTable *ht = Z_ARRVAL_P(vars_array);
857+
HashTable *ht = Z_ARRVAL_P(vars);
858858

859859
if (bg_worker_is_immutable(ht)) {
860-
/* Fast path: immutable arrays are already in shared memory.
861-
* No validation needed (immutable arrays contain only safe types).
862-
* No deep-copy needed. */
860+
/* Fast path: immutable arrays are already in shared memory. */
863861
void *old = NULL;
864-
char *error = go_frankenphp_worker_set_vars(thread_index, ht, &old);
862+
char *error = go_frankenphp_set_vars(thread_index, ht, &old);
865863
if (error) {
866864
zend_throw_exception(spl_ce_RuntimeException, error, 0);
867865
free(error);
868866
RETURN_THROWS();
869867
}
870868
bg_worker_free_stored_vars(old);
871869
} else {
872-
/* Slow path: validate, deep-copy to persistent memory */
870+
/* Validate nested values */
873871
zval *val;
874872
ZEND_HASH_FOREACH_VAL(ht, val) {
875873
if (!bg_worker_validate_zval(val)) {
876-
zend_value_error("Values must be null, scalars, arrays, or enums; "
877-
"objects and resources are not allowed");
874+
zend_value_error(
875+
"Vars values must be null, scalars, arrays, or enums; "
876+
"objects and resources are not allowed");
878877
RETURN_THROWS();
879878
}
880879
}
881880
ZEND_HASH_FOREACH_END();
882881

883882
zval persistent;
884-
bg_worker_persist_zval(&persistent, vars_array);
883+
bg_worker_persist_zval(&persistent, vars);
885884

886885
void *old = NULL;
887886
char *error =
888-
go_frankenphp_worker_set_vars(thread_index, Z_ARRVAL(persistent), &old);
887+
go_frankenphp_set_vars(thread_index, Z_ARRVAL(persistent), &old);
889888
if (error) {
890889
bg_worker_free_persistent_zval(&persistent);
891890
zend_throw_exception(spl_ce_RuntimeException, error, 0);
@@ -899,7 +898,7 @@ PHP_FUNCTION(frankenphp_worker_set_vars) {
899898
if (Z_TYPE(last_set_vars_zval) != IS_UNDEF) {
900899
zval_ptr_dtor(&last_set_vars_zval);
901900
}
902-
ZVAL_COPY(&last_set_vars_zval, vars_array);
901+
ZVAL_COPY(&last_set_vars_zval, vars);
903902
}
904903

905904
/* Copy vars from persistent storage into a PHP zval.
@@ -919,22 +918,22 @@ bool frankenphp_worker_copy_vars(zval *dst, int count, char **names,
919918

920919
array_init(dst);
921920
for (int i = 0; i < count; i++) {
922-
zval worker_vars;
921+
zval entry;
923922
if (ptrs[i]) {
924-
bg_worker_read_stored_vars(&worker_vars, ptrs[i]);
923+
bg_worker_read_stored_vars(&entry, ptrs[i]);
925924
if (EG(exception)) {
926-
zval_ptr_dtor(&worker_vars);
925+
zval_ptr_dtor(&entry);
927926
return false;
928927
}
929928
} else {
930-
array_init(&worker_vars);
929+
array_init(&entry);
931930
}
932-
add_assoc_zval_ex(dst, names[i], name_lens[i], &worker_vars);
931+
add_assoc_zval_ex(dst, names[i], name_lens[i], &entry);
933932
}
934933
return true;
935934
}
936935

937-
PHP_FUNCTION(frankenphp_worker_get_vars) {
936+
PHP_FUNCTION(frankenphp_get_vars) {
938937
zval *names = NULL;
939938
double timeout = 30.0;
940939

@@ -972,7 +971,7 @@ PHP_FUNCTION(frankenphp_worker_get_vars) {
972971
}
973972
}
974973

975-
char *error = go_frankenphp_worker_get_vars(
974+
char *error = go_frankenphp_get_vars(
976975
thread_index, &name_ptr, &name_len_val, 1, timeout_ms, return_value,
977976
cached ? &caller_version : NULL, &out_version);
978977
if (error) {
@@ -1034,7 +1033,7 @@ PHP_FUNCTION(frankenphp_worker_get_vars) {
10341033
}
10351034
ZEND_HASH_FOREACH_END();
10361035

1037-
char *error = go_frankenphp_worker_get_vars(
1036+
char *error = go_frankenphp_get_vars(
10381037
thread_index, name_ptrs, name_lens_arr, name_count, timeout_ms,
10391038
return_value, NULL, NULL);
10401039
free(name_ptrs);
@@ -1046,12 +1045,12 @@ PHP_FUNCTION(frankenphp_worker_get_vars) {
10461045
}
10471046
}
10481047

1049-
PHP_FUNCTION(frankenphp_worker_get_signaling_stream) {
1048+
PHP_FUNCTION(frankenphp_get_worker_handle) {
10501049
ZEND_PARSE_PARAMETERS_NONE();
10511050

10521051
if (!is_background_worker) {
10531052
zend_throw_exception(spl_ce_RuntimeException,
1054-
"frankenphp_worker_get_signaling_stream() can only "
1053+
"frankenphp_get_worker_handle() can only "
10551054
"be called from a background worker",
10561055
0);
10571056
RETURN_THROWS();

frankenphp.stub.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@
1717
function frankenphp_handle_request(callable $callback): bool {}
1818

1919
/**
20-
* @param array<null|scalar|\UnitEnum|array<mixed>> $vars Nested arrays must recursively follow the same type constraints
20+
* @param array<string, null|scalar|\UnitEnum|array<mixed>> $vars
2121
*/
22-
function frankenphp_worker_set_vars(array $vars): void {}
22+
function frankenphp_set_vars(array $vars): void {}
2323

2424
/**
25-
* @return array<null|scalar|array<mixed>|\UnitEnum> Nested arrays recursively follow the same type constraints
25+
* @return array<string, null|scalar|array<mixed>|\UnitEnum>
2626
*/
27-
function frankenphp_worker_get_vars(string|array $name, float $timeout = 30.0): array {}
27+
function frankenphp_get_vars(string|array $name, float $timeout = 30.0): array {}
2828

2929
/** @return resource */
30-
function frankenphp_worker_get_signaling_stream() {}
30+
function frankenphp_get_worker_handle() {}
3131

3232
function headers_send(int $status = 200): int {}
3333

0 commit comments

Comments
 (0)