Skip to content

Commit 5523d16

Browse files
committed
Limit Playground CLI workers to a single PHP instance
1 parent 7a23c7e commit 5523d16

File tree

4 files changed

+37
-7
lines changed

4 files changed

+37
-7
lines changed

packages/php-wasm/universal/src/lib/sandboxed-spawn-handler-factory.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createSpawnHandler } from '@php-wasm/util';
22
import type { PHPInstanceManager } from './php-instance-manager';
3+
import { logger } from '@php-wasm/logger';
34

45
/**
56
* An isomorphic proc_open() handler that implements typical shell in TypeScript
@@ -12,7 +13,7 @@ import type { PHPInstanceManager } from './php-instance-manager';
1213
* parser.
1314
*/
1415
export function sandboxedSpawnHandlerFactory(
15-
instanceManager: PHPInstanceManager
16+
instanceManager?: PHPInstanceManager
1617
) {
1718
return createSpawnHandler(async function (args, processApi, options) {
1819
processApi.notifySpawn();
@@ -63,6 +64,15 @@ export function sandboxedSpawnHandlerFactory(
6364
return;
6465
}
6566

67+
if (!instanceManager) {
68+
// 127 is the exit code "for command not found".
69+
processApi.exit(127);
70+
logger.warn(
71+
'sandboxedSpawnHandlerFactory tried to spawn a PHP subprocess but was called without an instance manager'
72+
);
73+
return;
74+
}
75+
6676
const { php, reap } = await instanceManager.acquirePHPInstance({
6777
considerPrimary: false,
6878
});

packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ export class PlaygroundCliBlueprintV1Worker extends PHPWorker {
178178
withXdebug,
179179
});
180180
},
181+
maxPhpInstances: 1,
181182
wordpressInstallMode,
182183
wordPressZip:
183184
wordPressZip !== undefined
@@ -281,6 +282,7 @@ export class PlaygroundCliBlueprintV1Worker extends PHPWorker {
281282
withXdebug,
282283
});
283284
},
285+
maxPhpInstances: 1,
284286
onPHPInstanceCreated: async (php) => {
285287
await mountResources(php, mountsBeforeWpInstall);
286288
await mountResources(php, mountsAfterWpInstall);

packages/playground/cli/src/blueprints-v2/worker-thread-v2.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ export class PlaygroundCliBlueprintV2Worker extends PHPWorker {
462462
withXdebug,
463463
});
464464
},
465+
maxPhpInstances: 1,
465466
onPHPInstanceCreated,
466467
sapiName: 'cli',
467468
createFiles,

packages/playground/wordpress/src/boot.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export async function bootWordPressAndRequestHandler(
5050
export interface BootRequestHandlerOptions {
5151
createPhpRuntime: (isPrimary?: boolean) => Promise<number>;
5252
onPHPInstanceCreated?: PHPInstanceCreatedHook;
53+
maxPhpInstances?: number;
5354
/**
5455
* PHP SAPI name to be returned by get_sapi_name(). Overriding
5556
* it is useful for running programs that check for this value,
@@ -62,7 +63,7 @@ export interface BootRequestHandlerOptions {
6263
*/
6364
siteUrl: string;
6465
documentRoot?: string;
65-
spawnHandler?: (instanceManager: PHPInstanceManager) => SpawnHandler;
66+
spawnHandler?: (instanceManager?: PHPInstanceManager) => SpawnHandler;
6667
/**
6768
* PHP.ini entries to define before running any code. They'll
6869
* be used for all requests.
@@ -356,8 +357,8 @@ async function assertValidDatabaseConnection(
356357
export async function bootRequestHandler(options: BootRequestHandlerOptions) {
357358
const spawnHandler = options.spawnHandler ?? sandboxedSpawnHandlerFactory;
358359
async function createPhp(
359-
requestHandler: PHPRequestHandler,
360-
isPrimary: boolean
360+
requestHandler?: PHPRequestHandler,
361+
isPrimary = false
361362
) {
362363
const runtimeId = await options.createPhpRuntime(isPrimary);
363364
const php = new PHP(runtimeId);
@@ -415,7 +416,7 @@ export async function bootRequestHandler(options: BootRequestHandlerOptions) {
415416
// `popen()`, `proc_open()` etc. calls.
416417
if (spawnHandler) {
417418
await php.setSpawnHandler(
418-
spawnHandler(requestHandler.processManager)
419+
spawnHandler(requestHandler?.processManager)
419420
);
420421
}
421422

@@ -434,14 +435,30 @@ export async function bootRequestHandler(options: BootRequestHandlerOptions) {
434435
}
435436

436437
const requestHandler: PHPRequestHandler = new PHPRequestHandler({
437-
phpFactory: async ({ isPrimary }) =>
438-
createPhp(requestHandler, isPrimary),
439438
documentRoot: options.documentRoot || '/wordpress',
440439
absoluteUrl: options.siteUrl,
441440
rewriteRules: wordPressRewriteRules,
442441
getFileNotFoundAction:
443442
options.getFileNotFoundAction ?? getFileNotFoundActionForWordPress,
444443
cookieStore: options.cookieStore,
444+
445+
/**
446+
* If maxPhpInstances is 1, we use a single PHP instance for all requests.
447+
*/
448+
php:
449+
options.maxPhpInstances === 1
450+
? await createPhp(undefined, true)
451+
: undefined,
452+
453+
/**
454+
* If maxPhpInstances is not 1, we use a PHPProcessManager that dynamically
455+
* spawns and reaps PHP instances as needed.
456+
*/
457+
phpFactory:
458+
options.maxPhpInstances !== 1
459+
? async ({ isPrimary }) => createPhp(requestHandler, isPrimary)
460+
: (undefined as any),
461+
maxPhpInstances: options.maxPhpInstances,
445462
});
446463

447464
return requestHandler;

0 commit comments

Comments
 (0)