Skip to content

Commit 2620d45

Browse files
committed
[PHP] Rotate the spawn handler in hotSwapPHPRuntime()
This PR ensures setting the spawn handler once is effective for the entire lifetime of a given PHP instance. Before this PR, the spawn handler would not be re-bound to the new PHP runtime in the hotSwapPHPRuntime() call. Follows up on #2559. The spawn handler was not preserved even before more prominent. ## Testing instructions CI – there's a new test to ensure the spawn handler is preserved after the runtime rotation / swapping
1 parent f0e4956 commit 2620d45

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

packages/php-wasm/node/src/test/rotate-php-runtime.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
PHP,
77
__private__dont__use,
88
} from '@php-wasm/universal';
9+
import { createSpawnHandler } from '@php-wasm/util';
910
import { loadNodeRuntime } from '../lib';
1011
import { createNodeFsMountHandler } from '../lib/node-fs-mount';
1112

@@ -379,4 +380,49 @@ describe('php.enableRuntimeRotation()', () => {
379380
php.exit();
380381
}
381382
}, 30_000);
383+
384+
it('Should preserve the spawn handler through PHP runtime recreation', async () => {
385+
const php = new PHP(await recreateRuntime());
386+
php.enableRuntimeRotation({
387+
cwd: '/test-root',
388+
recreateRuntime,
389+
maxRequests: 1,
390+
});
391+
392+
// Set up a custom spawn handler that tracks calls
393+
let spawnHandlerCallCount = 0;
394+
const customSpawnHandler = createSpawnHandler(
395+
async (command: string[], processApi: any) => {
396+
spawnHandlerCallCount++;
397+
// Simple echo command handler
398+
if (command[0] === 'echo') {
399+
processApi.stdout(
400+
new TextEncoder().encode(
401+
command.slice(1).join(' ') + '\n'
402+
)
403+
);
404+
}
405+
processApi.exit(0);
406+
}
407+
);
408+
409+
php.setSpawnHandler(customSpawnHandler);
410+
411+
// Test the spawn handler works before rotation
412+
const result1 = await php.run({
413+
code: `<?php echo exec("echo Hello World");`,
414+
});
415+
expect(result1.text).toBe('Hello World');
416+
expect(spawnHandlerCallCount).toBe(1);
417+
418+
// Rotate the PHP runtime
419+
await php.run({ code: `` });
420+
421+
// Test the spawn handler still works after rotation
422+
const result2 = await php.run({
423+
code: `<?php echo exec("echo Hello Again");`,
424+
});
425+
expect(result2.text).toBe('Hello Again');
426+
expect(spawnHandlerCallCount).toBe(2);
427+
}, 30_000);
382428
});

packages/php-wasm/universal/src/lib/php.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,7 @@ export class PHP implements Disposable {
13361336
// runtime.
13371337

13381338
const oldFS = this[__private__dont__use].FS;
1339+
const oldSpawnProcess = this[__private__dont__use].spawnProcess;
13391340

13401341
// Unmount all the mount handlers
13411342
const mountHandlers: { mountHandler: MountHandler; vfsPath: string }[] =
@@ -1355,6 +1356,10 @@ export class PHP implements Disposable {
13551356
// Initialize the new runtime
13561357
this.initializeRuntime(runtime);
13571358

1359+
if (oldSpawnProcess) {
1360+
this[__private__dont__use].spawnProcess = oldSpawnProcess;
1361+
}
1362+
13581363
if (this.#sapiName) {
13591364
this.setSapiName(this.#sapiName);
13601365
}

0 commit comments

Comments
 (0)