@wp-playground/blueprints 0.7.20 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -182,156 +182,607 @@ declare class PHPResponse implements PHPResponseData {
182
182
  */
183
183
  get text(): string;
184
184
  }
185
- /**
186
- * Represents an event related to the PHP request.
187
- */
188
- export interface PHPRequestEndEvent {
189
- type: "request.end";
190
- }
191
- /**
192
- * Represents an error event related to the PHP request.
193
- */
194
- export interface PHPRequestErrorEvent {
195
- type: "request.error";
196
- error: Error;
197
- source?: "request" | "php-wasm";
198
- }
199
- /**
200
- * Represents a PHP runtime initialization event.
201
- */
202
- export interface PHPRuntimeInitializedEvent {
203
- type: "runtime.initialized";
185
+ export type PHPRuntimeId = number;
186
+ /** Other WebAssembly declarations, for compatibility with older versions of Typescript */
187
+ export declare namespace Emscripten {
188
+ export interface RootFS extends Emscripten.FileSystemInstance {
189
+ filesystems: Record<string, Emscripten.FileSystemType>;
190
+ }
191
+ export interface FileSystemType {
192
+ mount(mount: FS.Mount): FS.FSNode;
193
+ syncfs(mount: FS.Mount, populate: () => unknown, done: (err?: number | null) => unknown): void;
194
+ }
195
+ export type EnvironmentType = "WEB" | "NODE" | "SHELL" | "WORKER";
196
+ export type JSType = "number" | "string" | "array" | "boolean";
197
+ export type TypeCompatibleWithC = number | string | any[] | boolean;
198
+ export type CIntType = "i8" | "i16" | "i32" | "i64";
199
+ export type CFloatType = "float" | "double";
200
+ export type CPointerType = "i8*" | "i16*" | "i32*" | "i64*" | "float*" | "double*" | "*";
201
+ export type CType = CIntType | CFloatType | CPointerType;
202
+ export interface CCallOpts {
203
+ async?: boolean | undefined;
204
+ }
205
+ type NamespaceToInstance<T> = {
206
+ [K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never;
207
+ };
208
+ export type FileSystemInstance = NamespaceToInstance<typeof FS> & {
209
+ mkdirTree(path: string): void;
210
+ lookupPath(path: string, opts?: any): FS.Lookup;
211
+ };
212
+ export interface EmscriptenModule {
213
+ print(str: string): void;
214
+ printErr(str: string): void;
215
+ arguments: string[];
216
+ environment: Emscripten.EnvironmentType;
217
+ preInit: Array<{
218
+ (): void;
219
+ }>;
220
+ preRun: Array<{
221
+ (): void;
222
+ }>;
223
+ postRun: Array<{
224
+ (): void;
225
+ }>;
226
+ onAbort: {
227
+ (what: any): void;
228
+ };
229
+ onRuntimeInitialized: {
230
+ (): void;
231
+ };
232
+ preinitializedWebGLContext: WebGLRenderingContext;
233
+ noInitialRun: boolean;
234
+ noExitRuntime: boolean;
235
+ logReadFiles: boolean;
236
+ filePackagePrefixURL: string;
237
+ wasmBinary: ArrayBuffer;
238
+ destroy(object: object): void;
239
+ getPreloadedPackage(remotePackageName: string, remotePackageSize: number): ArrayBuffer;
240
+ instantiateWasm(imports: WebAssembly.Imports, successCallback: (module: WebAssembly.Instance) => void): WebAssembly.Exports | undefined;
241
+ locateFile(url: string, scriptDirectory: string): string;
242
+ onCustomMessage(event: MessageEvent): void;
243
+ HEAP: Int32Array;
244
+ IHEAP: Int32Array;
245
+ FHEAP: Float64Array;
246
+ HEAP8: Int8Array;
247
+ HEAP16: Int16Array;
248
+ HEAP32: Int32Array;
249
+ HEAPU8: Uint8Array;
250
+ HEAPU16: Uint16Array;
251
+ HEAPU32: Uint32Array;
252
+ HEAPF32: Float32Array;
253
+ HEAPF64: Float64Array;
254
+ HEAP64: BigInt64Array;
255
+ HEAPU64: BigUint64Array;
256
+ TOTAL_STACK: number;
257
+ TOTAL_MEMORY: number;
258
+ FAST_MEMORY: number;
259
+ addOnPreRun(cb: () => any): void;
260
+ addOnInit(cb: () => any): void;
261
+ addOnPreMain(cb: () => any): void;
262
+ addOnExit(cb: () => any): void;
263
+ addOnPostRun(cb: () => any): void;
264
+ preloadedImages: any;
265
+ preloadedAudios: any;
266
+ _malloc(size: number): number;
267
+ _free(ptr: number): void;
268
+ }
269
+ /**
270
+ * A factory function is generated when setting the `MODULARIZE` build option
271
+ * to `1` in your Emscripten build. It return a Promise that resolves to an
272
+ * initialized, ready-to-call `EmscriptenModule` instance.
273
+ *
274
+ * By default, the factory function will be named `Module`. It's recommended to
275
+ * use the `EXPORT_ES6` option, in which the factory function will be the
276
+ * default export. If used without `EXPORT_ES6`, the factory function will be a
277
+ * global variable. You can rename the variable using the `EXPORT_NAME` build
278
+ * option. It's left to you to export any global variables as needed in your
279
+ * application's types.
280
+ * @param moduleOverrides Default properties for the initialized module.
281
+ */
282
+ export type EmscriptenModuleFactory<T extends EmscriptenModule = EmscriptenModule> = (moduleOverrides?: Partial<T>) => Promise<T>;
283
+ export namespace FS {
284
+ interface Lookup {
285
+ path: string;
286
+ node: FSNode;
287
+ }
288
+ interface Analyze {
289
+ isRoot: boolean;
290
+ exists: boolean;
291
+ error: Error;
292
+ name: string;
293
+ path: Lookup["path"];
294
+ object: Lookup["node"];
295
+ parentExists: boolean;
296
+ parentPath: Lookup["path"];
297
+ parentObject: Lookup["node"];
298
+ }
299
+ interface Mount {
300
+ type: Emscripten.FileSystemType;
301
+ opts: object;
302
+ mountpoint: string;
303
+ mounts: Mount[];
304
+ root: FSNode;
305
+ }
306
+ class FSStream {
307
+ constructor();
308
+ object: FSNode;
309
+ readonly isRead: boolean;
310
+ readonly isWrite: boolean;
311
+ readonly isAppend: boolean;
312
+ flags: number;
313
+ position: number;
314
+ }
315
+ class FSNode {
316
+ parent: FSNode;
317
+ mount: Mount;
318
+ mounted?: Mount;
319
+ id: number;
320
+ name: string;
321
+ mode: number;
322
+ rdev: number;
323
+ readMode: number;
324
+ writeMode: number;
325
+ constructor(parent: FSNode, name: string, mode: number, rdev: number);
326
+ read: boolean;
327
+ write: boolean;
328
+ readonly isFolder: boolean;
329
+ readonly isDevice: boolean;
330
+ }
331
+ interface ErrnoError extends Error {
332
+ name: "ErronoError";
333
+ errno: number;
334
+ code: string;
335
+ }
336
+ function lookupPath(path: string, opts: any): Lookup;
337
+ function getPath(node: FSNode): string;
338
+ function analyzePath(path: string, dontResolveLastLink?: boolean): Analyze;
339
+ function isFile(mode: number): boolean;
340
+ function isDir(mode: number): boolean;
341
+ function isLink(mode: number): boolean;
342
+ function isChrdev(mode: number): boolean;
343
+ function isBlkdev(mode: number): boolean;
344
+ function isFIFO(mode: number): boolean;
345
+ function isSocket(mode: number): boolean;
346
+ function major(dev: number): number;
347
+ function minor(dev: number): number;
348
+ function makedev(ma: number, mi: number): number;
349
+ function registerDevice(dev: number, ops: any): void;
350
+ function syncfs(populate: boolean, callback: (e: any) => any): void;
351
+ function syncfs(callback: (e: any) => any, populate?: boolean): void;
352
+ function mount(type: Emscripten.FileSystemType, opts: any, mountpoint: string): any;
353
+ function unmount(mountpoint: string): void;
354
+ function mkdir(path: string, mode?: number): any;
355
+ function mkdev(path: string, mode?: number, dev?: number): any;
356
+ function symlink(oldpath: string, newpath: string): any;
357
+ function rename(old_path: string, new_path: string): void;
358
+ function rmdir(path: string): void;
359
+ function readdir(path: string): any;
360
+ function unlink(path: string): void;
361
+ function readlink(path: string): string;
362
+ function stat(path: string, dontFollow?: boolean): any;
363
+ function lstat(path: string): any;
364
+ function chmod(path: string, mode: number, dontFollow?: boolean): void;
365
+ function lchmod(path: string, mode: number): void;
366
+ function fchmod(fd: number, mode: number): void;
367
+ function chown(path: string, uid: number, gid: number, dontFollow?: boolean): void;
368
+ function lchown(path: string, uid: number, gid: number): void;
369
+ function fchown(fd: number, uid: number, gid: number): void;
370
+ function truncate(path: string, len: number): void;
371
+ function ftruncate(fd: number, len: number): void;
372
+ function utime(path: string, atime: number, mtime: number): void;
373
+ function open(path: string, flags: string, mode?: number, fd_start?: number, fd_end?: number): FSStream;
374
+ function close(stream: FSStream): void;
375
+ function llseek(stream: FSStream, offset: number, whence: number): any;
376
+ function read(stream: FSStream, buffer: ArrayBufferView, offset: number, length: number, position?: number): number;
377
+ function write(stream: FSStream, buffer: ArrayBufferView, offset: number, length: number, position?: number, canOwn?: boolean): number;
378
+ function allocate(stream: FSStream, offset: number, length: number): void;
379
+ function mmap(stream: FSStream, buffer: ArrayBufferView, offset: number, length: number, position: number, prot: number, flags: number): any;
380
+ function ioctl(stream: FSStream, cmd: any, arg: any): any;
381
+ function readFile(path: string, opts: {
382
+ encoding: "binary";
383
+ flags?: string | undefined;
384
+ }): Uint8Array;
385
+ function readFile(path: string, opts: {
386
+ encoding: "utf8";
387
+ flags?: string | undefined;
388
+ }): string;
389
+ function readFile(path: string, opts?: {
390
+ flags?: string | undefined;
391
+ }): Uint8Array;
392
+ function writeFile(path: string, data: string | ArrayBufferView, opts?: {
393
+ flags?: string | undefined;
394
+ }): void;
395
+ function cwd(): string;
396
+ function chdir(path: string): void;
397
+ function init(input: null | (() => number | null), output: null | ((c: number) => any), error: null | ((c: number) => any)): void;
398
+ function createLazyFile(parent: string | FSNode, name: string, url: string, canRead: boolean, canWrite: boolean): FSNode;
399
+ function createPreloadedFile(parent: string | FSNode, name: string, url: string, canRead: boolean, canWrite: boolean, onload?: () => void, onerror?: () => void, dontCreateFile?: boolean, canOwn?: boolean): void;
400
+ function createDataFile(parent: string | FSNode, name: string, data: ArrayBufferView, canRead: boolean, canWrite: boolean, canOwn: boolean): FSNode;
401
+ }
402
+ export const MEMFS: Emscripten.FileSystemType;
403
+ export const NODEFS: Emscripten.FileSystemType;
404
+ export const IDBFS: Emscripten.FileSystemType;
405
+ type StringToType<R> = R extends Emscripten.JSType ? {
406
+ number: number;
407
+ string: string;
408
+ array: number[] | string[] | boolean[] | Uint8Array | Int8Array;
409
+ boolean: boolean;
410
+ null: null;
411
+ }[R] : never;
412
+ type ArgsToType<T extends Array<Emscripten.JSType | null>> = Extract<{
413
+ [P in keyof T]: StringToType<T[P]>;
414
+ }, any[]>;
415
+ type ReturnToType<R extends Emscripten.JSType | null> = R extends null ? null : StringToType<Exclude<R, null>>;
416
+ export function cwrap<I extends Array<Emscripten.JSType | null> | [
417
+ ], R extends Emscripten.JSType | null>(ident: string, returnType: R, argTypes: I, opts?: Emscripten.CCallOpts): (...arg: ArgsToType<I>) => ReturnToType<R>;
418
+ export function ccall<I extends Array<Emscripten.JSType | null> | [
419
+ ], R extends Emscripten.JSType | null>(ident: string, returnType: R, argTypes: I, args: ArgsToType<I>, opts?: Emscripten.CCallOpts): ReturnToType<R>;
420
+ export function setValue(ptr: number, value: any, type: Emscripten.CType, noSafe?: boolean): void;
421
+ export function getValue(ptr: number, type: Emscripten.CType, noSafe?: boolean): number;
422
+ export function allocate(slab: number[] | ArrayBufferView | number, types: Emscripten.CType | Emscripten.CType[], allocator: number, ptr?: number): number;
423
+ export function stackAlloc(size: number): number;
424
+ export function stackSave(): number;
425
+ export function stackRestore(ptr: number): void;
426
+ export function UTF8ToString(ptr: number, maxBytesToRead?: number): string;
427
+ export function stringToUTF8(str: string, outPtr: number, maxBytesToRead?: number): void;
428
+ export function lengthBytesUTF8(str: string): number;
429
+ export function allocateUTF8(str: string): number;
430
+ export function allocateUTF8OnStack(str: string): number;
431
+ export function UTF16ToString(ptr: number): string;
432
+ export function stringToUTF16(str: string, outPtr: number, maxBytesToRead?: number): void;
433
+ export function lengthBytesUTF16(str: string): number;
434
+ export function UTF32ToString(ptr: number): string;
435
+ export function stringToUTF32(str: string, outPtr: number, maxBytesToRead?: number): void;
436
+ export function lengthBytesUTF32(str: string): number;
437
+ export function intArrayFromString(stringy: string, dontAddNull?: boolean, length?: number): number[];
438
+ export function intArrayToString(array: number[]): string;
439
+ export function writeStringToMemory(str: string, buffer: number, dontAddNull: boolean): void;
440
+ export function writeArrayToMemory(array: number[], buffer: number): void;
441
+ export function writeAsciiToMemory(str: string, buffer: number, dontAddNull: boolean): void;
442
+ export function addRunDependency(id: any): void;
443
+ export function removeRunDependency(id: any): void;
444
+ export function addFunction(func: (...args: any[]) => any, signature?: string): number;
445
+ export function removeFunction(funcPtr: number): void;
446
+ export const ALLOC_NORMAL: number;
447
+ export const ALLOC_STACK: number;
448
+ export const ALLOC_STATIC: number;
449
+ export const ALLOC_DYNAMIC: number;
450
+ export const ALLOC_NONE: number;
451
+ export {};
204
452
  }
205
- /**
206
- * Represents a PHP runtime destruction event.
207
- */
208
- export interface PHPRuntimeBeforeDestroyEvent {
209
- type: "runtime.beforedestroy";
453
+ export interface RmDirOptions {
454
+ /**
455
+ * If true, recursively removes the directory and all its contents.
456
+ * Default: true.
457
+ */
458
+ recursive?: boolean;
210
459
  }
211
- /**
212
- * Represents an event related to the PHP instance.
213
- * This is intentionally not an extension of CustomEvent
214
- * to make it isomorphic between different JavaScript runtimes.
215
- */
216
- export type PHPEvent = PHPRequestEndEvent | PHPRequestErrorEvent | PHPRuntimeInitializedEvent | PHPRuntimeBeforeDestroyEvent;
217
- /**
218
- * A callback function that handles PHP events.
219
- */
220
- export type PHPEventListener = (event: PHPEvent) => void;
221
- export interface IsomorphicLocalPHP {
222
- /** @deprecated Use PHPRequestHandler instead. */
223
- request(request: PHPRequest): Promise<PHPResponse>;
224
- /** @deprecated Use PHPRequestHandler instead. */
225
- pathToInternalUrl(path: string): string;
226
- /** @deprecated Use PHPRequestHandler instead. */
227
- internalUrlToPath(internalUrl: string): string;
228
- /** @deprecated Use PHPRequestHandler instead. */
229
- absoluteUrl: string;
230
- /** @deprecated Use PHPRequestHandler instead. */
231
- documentRoot: string;
460
+ export interface ListFilesOptions {
232
461
  /**
233
- * Sets the SAPI name exposed by the PHP module.
234
- * @param newName - The new SAPI name.
462
+ * If true, prepend given folder path to all file names.
463
+ * Default: false.
235
464
  */
236
- setSapiName(newName: string): void;
465
+ prependPath: boolean;
466
+ }
467
+ export interface SemaphoreOptions {
237
468
  /**
238
- * Defines a constant in the PHP runtime.
239
- * @param key - The name of the constant.
240
- * @param value - The value of the constant.
469
+ * The maximum number of concurrent locks.
241
470
  */
242
- defineConstant(key: string, value: boolean | string | number | null): void;
471
+ concurrency: number;
243
472
  /**
244
- * Adds an event listener for a PHP event.
245
- * @param eventType - The type of event to listen for.
246
- * @param listener - The listener function to be called when the event is triggered.
473
+ * The maximum time to wait for a lock to become available.
247
474
  */
248
- addEventListener(eventType: PHPEvent["type"], listener: PHPEventListener): void;
475
+ timeout?: number;
476
+ }
477
+ declare class Semaphore {
478
+ private _running;
479
+ private concurrency;
480
+ private timeout?;
481
+ private queue;
482
+ constructor({ concurrency, timeout }: SemaphoreOptions);
483
+ get remaining(): number;
484
+ get running(): number;
485
+ acquire(): Promise<() => void>;
486
+ run<T>(fn: () => T | Promise<T>): Promise<T>;
487
+ }
488
+ export type PHPFactoryOptions = {
489
+ isPrimary: boolean;
490
+ };
491
+ export type PHPFactory = (options: PHPFactoryOptions) => Promise<PHP>;
492
+ export interface ProcessManagerOptions {
249
493
  /**
250
- * Removes an event listener for a PHP event.
251
- * @param eventType - The type of event to remove the listener from.
252
- * @param listener - The listener function to be removed.
494
+ * The maximum number of PHP instances that can exist at
495
+ * the same time.
253
496
  */
254
- removeEventListener(eventType: PHPEvent["type"], listener: PHPEventListener): void;
497
+ maxPhpInstances?: number;
255
498
  /**
256
- * Recursively creates a directory with the given path in the PHP filesystem.
257
- * For example, if the path is `/root/php/data`, and `/root` already exists,
258
- * it will create the directories `/root/php` and `/root/php/data`.
499
+ * The number of milliseconds to wait for a PHP instance when
500
+ * we have reached the maximum number of PHP instances and
501
+ * cannot spawn a new one. If the timeout is reached, we assume
502
+ * all the PHP instances are deadlocked and a throw MaxPhpInstancesError.
259
503
  *
260
- * @param path - The directory path to create.
504
+ * Default: 5000
261
505
  */
262
- mkdir(path: string): void;
506
+ timeout?: number;
263
507
  /**
264
- * @deprecated Use mkdir instead.
508
+ * The primary PHP instance that's never killed. This instance
509
+ * contains the reference filesystem used by all other PHP instances.
265
510
  */
266
- mkdirTree(path: string): void;
511
+ primaryPhp?: PHP;
267
512
  /**
268
- * Reads a file from the PHP filesystem and returns it as a string.
269
- *
270
- * @throws {@link @php-wasm/universal:ErrnoError} – If the file doesn't exist.
271
- * @param path - The file path to read.
272
- * @returns The file contents.
513
+ * A factory function used for spawning new PHP instances.
273
514
  */
274
- readFileAsText(path: string): string;
515
+ phpFactory?: PHPFactory;
516
+ }
517
+ export interface SpawnedPHP {
518
+ php: PHP;
519
+ reap: () => void;
520
+ }
521
+ declare class PHPProcessManager implements AsyncDisposable {
522
+ private primaryPhp?;
523
+ private primaryIdle;
524
+ private nextInstance;
275
525
  /**
276
- * Reads a file from the PHP filesystem and returns it as an array buffer.
526
+ * All spawned PHP instances, including the primary PHP instance.
527
+ * Used for bookkeeping and reaping all instances on dispose.
528
+ */
529
+ private allInstances;
530
+ private phpFactory?;
531
+ private maxPhpInstances;
532
+ private semaphore;
533
+ constructor(options?: ProcessManagerOptions);
534
+ /**
535
+ * Get the primary PHP instance.
277
536
  *
278
- * @throws {@link @php-wasm/universal:ErrnoError} If the file doesn't exist.
279
- * @param path - The file path to read.
280
- * @returns The file contents.
537
+ * If the primary PHP instance is not set, it will be spawned
538
+ * using the provided phpFactory.
539
+ *
540
+ * @throws {Error} when called twice before the first call is resolved.
281
541
  */
282
- readFileAsBuffer(path: string): Uint8Array;
542
+ getPrimaryPhp(): Promise<PHP>;
283
543
  /**
284
- * Overwrites data in a file in the PHP filesystem.
285
- * Creates a new file if one doesn't exist yet.
544
+ * Get a PHP instance.
286
545
  *
287
- * @param path - The file path to write to.
288
- * @param data - The data to write to the file.
546
+ * It could be either the primary PHP instance, an idle disposable PHP instance,
547
+ * or a newly spawned PHP instance depending on the resource availability.
548
+ *
549
+ * @throws {MaxPhpInstancesError} when the maximum number of PHP instances is reached
550
+ * and the waiting timeout is exceeded.
289
551
  */
290
- writeFile(path: string, data: string | Uint8Array): void;
552
+ acquirePHPInstance(): Promise<SpawnedPHP>;
291
553
  /**
292
- * Removes a file from the PHP filesystem.
554
+ * Initiated spawning of a new PHP instance.
555
+ * This function is synchronous on purpose – it needs to synchronously
556
+ * add the spawn promise to the allInstances array without waiting
557
+ * for PHP to spawn.
558
+ */
559
+ private spawn;
560
+ /**
561
+ * Actually acquires the lock and spawns a new PHP instance.
562
+ */
563
+ private doSpawn;
564
+ [Symbol.asyncDispose](): Promise<void>;
565
+ }
566
+ export type RewriteRule = {
567
+ match: RegExp;
568
+ replacement: string;
569
+ };
570
+ export interface BaseConfiguration {
571
+ /**
572
+ * The directory in the PHP filesystem where the server will look
573
+ * for the files to serve. Default: `/var/www`.
574
+ */
575
+ documentRoot?: string;
576
+ /**
577
+ * Request Handler URL. Used to populate $_SERVER details like HTTP_HOST.
578
+ */
579
+ absoluteUrl?: string;
580
+ /**
581
+ * Rewrite rules
582
+ */
583
+ rewriteRules?: RewriteRule[];
584
+ }
585
+ export type PHPRequestHandlerFactoryArgs = PHPFactoryOptions & {
586
+ requestHandler: PHPRequestHandler;
587
+ };
588
+ export type PHPRequestHandlerConfiguration = BaseConfiguration & ({
589
+ /**
590
+ * PHPProcessManager is required because the request handler needs
591
+ * to make a decision for each request.
293
592
  *
294
- * @throws {@link @php-wasm/universal:ErrnoError} If the file doesn't exist.
295
- * @param path - The file path to remove.
593
+ * Static assets are served using the primary PHP's filesystem, even
594
+ * when serving 100 static files concurrently. No new PHP interpreter
595
+ * is ever created as there's no need for it.
596
+ *
597
+ * Dynamic PHP requests, however, require grabbing an available PHP
598
+ * interpreter, and that's where the PHPProcessManager comes in.
599
+ */
600
+ processManager: PHPProcessManager;
601
+ } | {
602
+ phpFactory: (requestHandler: PHPRequestHandlerFactoryArgs) => Promise<PHP>;
603
+ /**
604
+ * The maximum number of PHP instances that can exist at
605
+ * the same time.
606
+ */
607
+ maxPhpInstances?: number;
608
+ });
609
+ declare class PHPRequestHandler {
610
+ #private;
611
+ rewriteRules: RewriteRule[];
612
+ processManager: PHPProcessManager;
613
+ /**
614
+ * The request handler needs to decide whether to serve a static asset or
615
+ * run the PHP interpreter. For static assets it should just reuse the primary
616
+ * PHP even if there's 50 concurrent requests to serve. However, for
617
+ * dynamic PHP requests, it needs to grab an available interpreter.
618
+ * Therefore, it cannot just accept PHP as an argument as serving requests
619
+ * requires access to ProcessManager.
620
+ *
621
+ * @param php - The PHP instance.
622
+ * @param config - Request Handler configuration.
296
623
  */
297
- unlink(path: string): void;
624
+ constructor(config: PHPRequestHandlerConfiguration);
625
+ getPrimaryPhp(): Promise<PHP>;
298
626
  /**
299
- * Moves a file or directory in the PHP filesystem to a
300
- * new location.
627
+ * Converts a path to an absolute URL based at the PHPRequestHandler
628
+ * root.
301
629
  *
302
- * @param oldPath The path to rename.
303
- * @param newPath The new path.
630
+ * @param path The server path to convert to an absolute URL.
631
+ * @returns The absolute URL.
304
632
  */
305
- mv(oldPath: string, newPath: string): void;
633
+ pathToInternalUrl(path: string): string;
306
634
  /**
307
- * Removes a directory from the PHP filesystem.
635
+ * Converts an absolute URL based at the PHPRequestHandler to a relative path
636
+ * without the server pathname and scope.
308
637
  *
309
- * @param path The directory path to remove.
310
- * @param options Options for the removal.
638
+ * @param internalUrl An absolute URL based at the PHPRequestHandler root.
639
+ * @returns The relative path.
311
640
  */
312
- rmdir(path: string, options?: RmDirOptions): void;
641
+ internalUrlToPath(internalUrl: string): string;
313
642
  /**
314
- * Lists the files and directories in the given directory.
643
+ * The absolute URL of this PHPRequestHandler instance.
644
+ */
645
+ get absoluteUrl(): string;
646
+ /**
647
+ * The directory in the PHP filesystem where the server will look
648
+ * for the files to serve. Default: `/var/www`.
649
+ */
650
+ get documentRoot(): string;
651
+ /**
652
+ * Serves the request – either by serving a static file, or by
653
+ * dispatching it to the PHP runtime.
315
654
  *
316
- * @param path - The directory path to list.
317
- * @param options - Options for the listing.
318
- * @returns The list of files and directories in the given directory.
655
+ * The request() method mode behaves like a web server and only works if
656
+ * the PHP was initialized with a `requestHandler` option (which the online version
657
+ * of WordPress Playground does by default).
658
+ *
659
+ * In the request mode, you pass an object containing the request information
660
+ * (method, headers, body, etc.) and the path to the PHP file to run:
661
+ *
662
+ * ```ts
663
+ * const php = PHP.load('7.4', {
664
+ * requestHandler: {
665
+ * documentRoot: "/www"
666
+ * }
667
+ * })
668
+ * php.writeFile("/www/index.php", `<?php echo file_get_contents("php://input");`);
669
+ * const result = await php.request({
670
+ * method: "GET",
671
+ * headers: {
672
+ * "Content-Type": "text/plain"
673
+ * },
674
+ * body: "Hello world!",
675
+ * path: "/www/index.php"
676
+ * });
677
+ * // result.text === "Hello world!"
678
+ * ```
679
+ *
680
+ * The `request()` method cannot be used in conjunction with `cli()`.
681
+ *
682
+ * @example
683
+ * ```js
684
+ * const output = await php.request({
685
+ * method: 'GET',
686
+ * url: '/index.php',
687
+ * headers: {
688
+ * 'X-foo': 'bar',
689
+ * },
690
+ * body: {
691
+ * foo: 'bar',
692
+ * },
693
+ * });
694
+ * console.log(output.stdout); // "Hello world!"
695
+ * ```
696
+ *
697
+ * @param request - PHP Request data.
319
698
  */
320
- listFiles(path: string, options?: ListFilesOptions): string[];
699
+ request(request: PHPRequest): Promise<PHPResponse>;
700
+ }
701
+ declare const __private__dont__use: unique symbol;
702
+ export type UnmountFunction = (() => Promise<any>) | (() => any);
703
+ export type MountHandler = (php: PHP, FS: Emscripten.RootFS, vfsMountPoint: string) => UnmountFunction | Promise<UnmountFunction>;
704
+ declare class PHP implements Disposable {
705
+ #private;
706
+ protected [__private__dont__use]: any;
707
+ requestHandler?: PHPRequestHandler;
321
708
  /**
322
- * Checks if a directory exists in the PHP filesystem.
709
+ * An exclusive lock that prevent multiple requests from running at
710
+ * the same time.
711
+ */
712
+ semaphore: Semaphore;
713
+ /**
714
+ * Initializes a PHP runtime.
323
715
  *
324
- * @param path – The path to check.
325
- * @returns True if the path is a directory, false otherwise.
716
+ * @internal
717
+ * @param PHPRuntime - Optional. PHP Runtime ID as initialized by loadPHPRuntime.
718
+ * @param requestHandlerOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
326
719
  */
327
- isDir(path: string): boolean;
720
+ constructor(PHPRuntimeId?: PHPRuntimeId);
328
721
  /**
329
- * Checks if a file (or a directory) exists in the PHP filesystem.
722
+ * Adds an event listener for a PHP event.
723
+ * @param eventType - The type of event to listen for.
724
+ * @param listener - The listener function to be called when the event is triggered.
725
+ */
726
+ addEventListener(eventType: PHPEvent["type"], listener: PHPEventListener): void;
727
+ /**
728
+ * Removes an event listener for a PHP event.
729
+ * @param eventType - The type of event to remove the listener from.
730
+ * @param listener - The listener function to be removed.
731
+ */
732
+ removeEventListener(eventType: PHPEvent["type"], listener: PHPEventListener): void;
733
+ dispatchEvent<Event extends PHPEvent>(event: Event): void;
734
+ /**
735
+ * Listens to message sent by the PHP code.
330
736
  *
331
- * @param path - The file path to check.
332
- * @returns True if the file exists, false otherwise.
737
+ * To dispatch messages, call:
738
+ *
739
+ * post_message_to_js(string $data)
740
+ *
741
+ * Arguments:
742
+ * $data (string) – Data to pass to JavaScript.
743
+ *
744
+ * @example
745
+ *
746
+ * ```ts
747
+ * const php = await PHP.load('8.0');
748
+ *
749
+ * php.onMessage(
750
+ * // The data is always passed as a string
751
+ * function (data: string) {
752
+ * // Let's decode and log the data:
753
+ * console.log(JSON.parse(data));
754
+ * }
755
+ * );
756
+ *
757
+ * // Now that we have a listener in place, let's
758
+ * // dispatch a message:
759
+ * await php.run({
760
+ * code: `<?php
761
+ * post_message_to_js(
762
+ * json_encode([
763
+ * 'post_id' => '15',
764
+ * 'post_title' => 'This is a blog post!'
765
+ * ])
766
+ * ));
767
+ * `,
768
+ * });
769
+ * ```
770
+ *
771
+ * @param listener Callback function to handle the message.
333
772
  */
334
- fileExists(path: string): boolean;
773
+ onMessage(listener: MessageListener): void;
774
+ setSpawnHandler(handler: SpawnHandler | string): Promise<void>;
775
+ /** @deprecated Use PHPRequestHandler instead. */
776
+ get absoluteUrl(): string;
777
+ /** @deprecated Use PHPRequestHandler instead. */
778
+ get documentRoot(): string;
779
+ /** @deprecated Use PHPRequestHandler instead. */
780
+ pathToInternalUrl(path: string): string;
781
+ /** @deprecated Use PHPRequestHandler instead. */
782
+ internalUrlToPath(internalUrl: string): string;
783
+ initializeRuntime(runtimeId: PHPRuntimeId): void;
784
+ /** @inheritDoc */
785
+ setSapiName(newName: string): Promise<void>;
335
786
  /**
336
787
  * Changes the current working directory in the PHP filesystem.
337
788
  * This is the directory that will be used as the base for relative paths.
@@ -341,6 +792,11 @@ export interface IsomorphicLocalPHP {
341
792
  * @param path - The new working directory.
342
793
  */
343
794
  chdir(path: string): void;
795
+ /**
796
+ * Do not use. Use new PHPRequestHandler() instead.
797
+ * @deprecated
798
+ */
799
+ request(request: PHPRequest): Promise<PHPResponse>;
344
800
  /**
345
801
  * Runs PHP code.
346
802
  *
@@ -410,57 +866,181 @@ export interface IsomorphicLocalPHP {
410
866
  *
411
867
  * @param options - PHP runtime options.
412
868
  */
413
- run(options: PHPRunOptions): Promise<PHPResponse>;
869
+ run(request: PHPRunOptions): Promise<PHPResponse>;
414
870
  /**
415
- * Listens to message sent by the PHP code.
871
+ * Defines a constant in the PHP runtime.
872
+ * @param key - The name of the constant.
873
+ * @param value - The value of the constant.
874
+ */
875
+ defineConstant(key: string, value: string | boolean | number | null): void;
876
+ /**
877
+ * Recursively creates a directory with the given path in the PHP filesystem.
878
+ * For example, if the path is `/root/php/data`, and `/root` already exists,
879
+ * it will create the directories `/root/php` and `/root/php/data`.
416
880
  *
417
- * To dispatch messages, call:
881
+ * @param path - The directory path to create.
882
+ */
883
+ mkdir(path: string): void;
884
+ /**
885
+ * @deprecated Use mkdir instead.
886
+ */
887
+ mkdirTree(path: string): void;
888
+ /**
889
+ * Reads a file from the PHP filesystem and returns it as a string.
418
890
  *
419
- * post_message_to_js(string $data)
891
+ * @throws {@link @php-wasm/universal:ErrnoError} – If the file doesn't exist.
892
+ * @param path - The file path to read.
893
+ * @returns The file contents.
894
+ */
895
+ readFileAsText(path: string): string;
896
+ /**
897
+ * Reads a file from the PHP filesystem and returns it as an array buffer.
420
898
  *
421
- * Arguments:
422
- * $data (string) Data to pass to JavaScript.
899
+ * @throws {@link @php-wasm/universal:ErrnoError} – If the file doesn't exist.
900
+ * @param path - The file path to read.
901
+ * @returns The file contents.
902
+ */
903
+ readFileAsBuffer(path: string): Uint8Array;
904
+ /**
905
+ * Overwrites data in a file in the PHP filesystem.
906
+ * Creates a new file if one doesn't exist yet.
423
907
  *
424
- * @example
908
+ * @param path - The file path to write to.
909
+ * @param data - The data to write to the file.
910
+ */
911
+ writeFile(path: string, data: string | Uint8Array): void;
912
+ /**
913
+ * Removes a file from the PHP filesystem.
425
914
  *
426
- * ```ts
427
- * const php = await PHP.load('8.0');
915
+ * @throws {@link @php-wasm/universal:ErrnoError} – If the file doesn't exist.
916
+ * @param path - The file path to remove.
917
+ */
918
+ unlink(path: string): void;
919
+ /**
920
+ * Moves a file or directory in the PHP filesystem to a
921
+ * new location.
428
922
  *
429
- * php.onMessage(
430
- * // The data is always passed as a string
431
- * function (data: string) {
432
- * // Let's decode and log the data:
433
- * console.log(JSON.parse(data));
434
- * }
435
- * );
923
+ * @param oldPath The path to rename.
924
+ * @param newPath The new path.
925
+ */
926
+ mv(fromPath: string, toPath: string): void;
927
+ /**
928
+ * Removes a directory from the PHP filesystem.
436
929
  *
437
- * // Now that we have a listener in place, let's
438
- * // dispatch a message:
439
- * await php.run({
440
- * code: `<?php
441
- * post_message_to_js(
442
- * json_encode([
443
- * 'post_id' => '15',
444
- * 'post_title' => 'This is a blog post!'
445
- * ])
446
- * ));
447
- * `,
448
- * });
449
- * ```
930
+ * @param path The directory path to remove.
931
+ * @param options Options for the removal.
932
+ */
933
+ rmdir(path: string, options?: RmDirOptions): void;
934
+ /**
935
+ * Lists the files and directories in the given directory.
450
936
  *
451
- * @param listener Callback function to handle the message.
937
+ * @param path - The directory path to list.
938
+ * @param options - Options for the listing.
939
+ * @returns The list of files and directories in the given directory.
452
940
  */
453
- onMessage(listener: MessageListener): void;
941
+ listFiles(path: string, options?: ListFilesOptions): string[];
942
+ /**
943
+ * Checks if a directory exists in the PHP filesystem.
944
+ *
945
+ * @param path – The path to check.
946
+ * @returns True if the path is a directory, false otherwise.
947
+ */
948
+ isDir(path: string): boolean;
949
+ /**
950
+ * Checks if a file (or a directory) exists in the PHP filesystem.
951
+ *
952
+ * @param path - The file path to check.
953
+ * @returns True if the file exists, false otherwise.
954
+ */
955
+ fileExists(path: string): boolean;
956
+ /**
957
+ * Hot-swaps the PHP runtime for a new one without
958
+ * interrupting the operations of this PHP instance.
959
+ *
960
+ * @param runtime
961
+ * @param cwd. Internal, the VFS path to recreate in the new runtime.
962
+ * This arg is temporary and will be removed once BasePHP
963
+ * is fully decoupled from the request handler and
964
+ * accepts a constructor-level cwd argument.
965
+ */
966
+ hotSwapPHPRuntime(runtime: number, cwd?: string): void;
967
+ /**
968
+ * Mounts a filesystem to a given path in the PHP filesystem.
969
+ *
970
+ * @param virtualFSPath - Where to mount it in the PHP virtual filesystem.
971
+ * @param mountHandler - The mount handler to use.
972
+ * @return Unmount function to unmount the filesystem.
973
+ */
974
+ mount(virtualFSPath: string, mountHandler: MountHandler): Promise<UnmountFunction>;
975
+ /**
976
+ * Starts a PHP CLI session with given arguments.
977
+ *
978
+ * This method can only be used when PHP was compiled with the CLI SAPI
979
+ * and it cannot be used in conjunction with `run()`.
980
+ *
981
+ * Once this method finishes running, the PHP instance is no
982
+ * longer usable and should be discarded. This is because PHP
983
+ * internally cleans up all the resources and calls exit().
984
+ *
985
+ * @param argv - The arguments to pass to the CLI.
986
+ * @returns The exit code of the CLI session.
987
+ */
988
+ cli(argv: string[]): Promise<number>;
989
+ setSkipShebang(shouldSkip: boolean): void;
990
+ exit(code?: number): void;
991
+ [Symbol.dispose](): void;
454
992
  }
455
- export type MessageListener = (data: string) => Promise<string | Uint8Array | void> | string | void;
993
+ export type LimitedPHPApi = Pick<PHP, "request" | "defineConstant" | "addEventListener" | "removeEventListener" | "mkdir" | "mkdirTree" | "readFileAsText" | "readFileAsBuffer" | "writeFile" | "unlink" | "mv" | "rmdir" | "listFiles" | "isDir" | "fileExists" | "chdir" | "run" | "onMessage"> & {
994
+ documentRoot: PHP["documentRoot"];
995
+ absoluteUrl: PHP["absoluteUrl"];
996
+ };
997
+ /**
998
+ * Represents an event related to the PHP request.
999
+ */
1000
+ export interface PHPRequestEndEvent {
1001
+ type: "request.end";
1002
+ }
1003
+ /**
1004
+ * Represents an error event related to the PHP request.
1005
+ */
1006
+ export interface PHPRequestErrorEvent {
1007
+ type: "request.error";
1008
+ error: Error;
1009
+ source?: "request" | "php-wasm";
1010
+ }
1011
+ /**
1012
+ * Represents a PHP runtime initialization event.
1013
+ */
1014
+ export interface PHPRuntimeInitializedEvent {
1015
+ type: "runtime.initialized";
1016
+ }
1017
+ /**
1018
+ * Represents a PHP runtime destruction event.
1019
+ */
1020
+ export interface PHPRuntimeBeforeDestroyEvent {
1021
+ type: "runtime.beforedestroy";
1022
+ }
1023
+ /**
1024
+ * Represents an event related to the PHP instance.
1025
+ * This is intentionally not an extension of CustomEvent
1026
+ * to make it isomorphic between different JavaScript runtimes.
1027
+ */
1028
+ export type PHPEvent = PHPRequestEndEvent | PHPRequestErrorEvent | PHPRuntimeInitializedEvent | PHPRuntimeBeforeDestroyEvent;
456
1029
  /**
457
- * The omited methods must either be called synchronously before
458
- * the PHP internal state is initialized, or with a complex argument
459
- * that can't be serialized over a remote connection. Therefeore,
460
- * they don't make sense in a remote PHP instance.
1030
+ * A callback function that handles PHP events.
461
1031
  */
462
- export type IsomorphicRemotePHP = Remote<Omit<IsomorphicLocalPHP, "setSapiName">>;
463
- export type UniversalPHP = IsomorphicLocalPHP | IsomorphicRemotePHP;
1032
+ export type PHPEventListener = (event: PHPEvent) => void;
1033
+ export type UniversalPHP = LimitedPHPApi | Remote<LimitedPHPApi>;
1034
+ export type MessageListener = (data: string) => Promise<string | Uint8Array | void> | string | void;
1035
+ export interface EventEmitter {
1036
+ on(event: string, listener: (...args: any[]) => void): this;
1037
+ emit(event: string, ...args: any[]): boolean;
1038
+ }
1039
+ export type ChildProcess = EventEmitter & {
1040
+ stdout: EventEmitter;
1041
+ stderr: EventEmitter;
1042
+ };
1043
+ export type SpawnHandler = (command: string, args: string[]) => ChildProcess;
464
1044
  export type HTTPMethod = "GET" | "POST" | "HEAD" | "OPTIONS" | "PATCH" | "PUT" | "DELETE";
465
1045
  export type PHPRequestHeaders = Record<string, string>;
466
1046
  export interface PHPRequest {
@@ -521,41 +1101,6 @@ export interface PHPRunOptions {
521
1101
  */
522
1102
  code?: string;
523
1103
  }
524
- export interface RmDirOptions {
525
- /**
526
- * If true, recursively removes the directory and all its contents.
527
- * Default: true.
528
- */
529
- recursive?: boolean;
530
- }
531
- export interface ListFilesOptions {
532
- /**
533
- * If true, prepend given folder path to all file names.
534
- * Default: false.
535
- */
536
- prependPath: boolean;
537
- }
538
- export interface SemaphoreOptions {
539
- /**
540
- * The maximum number of concurrent locks.
541
- */
542
- concurrency: number;
543
- /**
544
- * The maximum time to wait for a lock to become available.
545
- */
546
- timeout?: number;
547
- }
548
- declare class Semaphore {
549
- private _running;
550
- private concurrency;
551
- private timeout?;
552
- private queue;
553
- constructor({ concurrency, timeout }: SemaphoreOptions);
554
- get remaining(): number;
555
- get running(): number;
556
- acquire(): Promise<() => void>;
557
- run<T>(fn: () => T | Promise<T>): Promise<T>;
558
- }
559
1104
  declare const SupportedPHPVersions: readonly [
560
1105
  "8.3",
561
1106
  "8.2",
@@ -1524,6 +2069,28 @@ export interface WPCLIStep {
1524
2069
  * Runs PHP code using [WP-CLI](https://developer.wordpress.org/cli/commands/).
1525
2070
  */
1526
2071
  export declare const wpCLI: StepHandler<WPCLIStep, Promise<PHPResponse>>;
2072
+ /**
2073
+ * Deletes WordPress posts and comments and sets the auto increment sequence for the
2074
+ * posts and comments tables to 0.
2075
+ *
2076
+ * @private
2077
+ * @internal
2078
+ * @inheritDoc resetData
2079
+ * @example
2080
+ *
2081
+ * <code>
2082
+ * {
2083
+ * "step": "resetData"
2084
+ * }
2085
+ * </code>
2086
+ */
2087
+ export interface ResetDataStep {
2088
+ step: "resetData";
2089
+ }
2090
+ /**
2091
+ * @param playground Playground client.
2092
+ */
2093
+ export declare const resetData: StepHandler<ResetDataStep>;
1527
2094
  /**
1528
2095
  * Used by the export step to exclude the Playground-specific files
1529
2096
  * from the zip file. Keep it in sync with the list of files created
@@ -1541,7 +2108,7 @@ export type StepDefinition = Step & {
1541
2108
  * If you add a step here, make sure to also
1542
2109
  * add it to the exports below.
1543
2110
  */
1544
- export type GenericStep<Resource> = ActivatePluginStep | ActivateThemeStep | CpStep | DefineWpConfigConstsStep | DefineSiteUrlStep | EnableMultisiteStep | ImportWxrStep<Resource> | ImportWordPressFilesStep<Resource> | InstallPluginStep<Resource> | InstallThemeStep<Resource> | LoginStep | MkdirStep | MvStep | RequestStep | RmStep | RmdirStep | RunPHPStep | RunPHPWithOptionsStep | RunWpInstallationWizardStep | RunSqlStep<Resource> | SetSiteOptionsStep | UnzipStep<Resource> | UpdateUserMetaStep | WriteFileStep<Resource> | WPCLIStep;
2111
+ export type GenericStep<Resource> = ActivatePluginStep | ActivateThemeStep | CpStep | DefineWpConfigConstsStep | DefineSiteUrlStep | EnableMultisiteStep | ImportWxrStep<Resource> | ImportWordPressFilesStep<Resource> | InstallPluginStep<Resource> | InstallThemeStep<Resource> | LoginStep | MkdirStep | MvStep | ResetDataStep | RequestStep | RmStep | RmdirStep | RunPHPStep | RunPHPWithOptionsStep | RunWpInstallationWizardStep | RunSqlStep<Resource> | SetSiteOptionsStep | UnzipStep<Resource> | UpdateUserMetaStep | WriteFileStep<Resource> | WPCLIStep;
1545
2112
  /**
1546
2113
  * Progress reporting details.
1547
2114
  */