@php-wasm/web 0.1.10 → 0.1.18

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
@@ -2,191 +2,256 @@
2
2
 
3
3
  import * as Comlink from 'comlink';
4
4
 
5
- export type JavascriptRuntime = "NODE" | "WEB" | "WORKER";
6
- export type PHPRequestHeaders = Record<string, string>;
7
- export interface FileInfo {
8
- key: string;
9
- name: string;
10
- type: string;
11
- data: Uint8Array;
12
- }
13
- export interface PHPRequest {
5
+ /**
6
+ * PHP response. Body is an `ArrayBuffer` because it can
7
+ * contain binary data.
8
+ */
9
+ export declare class PHPResponse {
14
10
  /**
15
- * Request path following the domain:port part.
11
+ * Response headers.
16
12
  */
17
- relativeUri?: string;
13
+ readonly headers: Record<string, string[]>;
18
14
  /**
19
- * Path of the .php file to execute.
15
+ * Response body. Contains the output from `echo`,
16
+ * `print`, inline HTML etc.
20
17
  */
21
- scriptPath?: string;
18
+ private readonly body;
22
19
  /**
23
- * Request protocol.
20
+ * Stderr contents, if any.
24
21
  */
25
- protocol?: string;
22
+ readonly errors: string;
26
23
  /**
27
- * Request method. Default: `GET`.
24
+ * The exit code of the script. `0` is a success, while
25
+ * `1` and `2` indicate an error.
28
26
  */
29
- method?: "GET" | "POST" | "HEAD" | "OPTIONS" | "PATCH" | "PUT" | "DELETE";
27
+ readonly exitCode: number;
30
28
  /**
31
- * Request headers.
29
+ * Response HTTP status code, e.g. 200.
32
30
  */
33
- headers?: PHPRequestHeaders;
31
+ readonly httpStatusCode: number;
32
+ constructor(httpStatusCode: number, headers: Record<string, string[]>, body: ArrayBuffer, errors?: string, exitCode?: number);
34
33
  /**
35
- * Request body without the files.
34
+ * Response body as JSON.
36
35
  */
37
- body?: string;
36
+ get json(): any;
38
37
  /**
39
- * Uploaded files.
38
+ * Response body as text.
40
39
  */
41
- fileInfos?: FileInfo[];
40
+ get text(): string;
42
41
  /**
43
- * The code snippet to eval instead of a php file.
42
+ * Response body as bytes.
44
43
  */
45
- code?: string;
44
+ get bytes(): ArrayBuffer;
46
45
  }
47
- export interface PHPResponse {
48
- /**
49
- * The exit code of the script. `0` is a success, while
50
- * `1` and `2` indicate an error.
51
- */
52
- exitCode: number;
53
- /**
54
- * Response body. Contains the output from `echo`,
55
- * `print`, inline HTML etc.
56
- */
57
- body: ArrayBuffer;
46
+ export type PHPRequest = Pick<PHPRunOptions, "method" | "headers"> & {
47
+ url: string;
48
+ files?: Record<string, File>;
49
+ } & ((Pick<PHPRunOptions, "body"> & {
50
+ formData?: never;
51
+ }) | {
52
+ body?: never;
53
+ formData: Record<string, unknown>;
54
+ });
55
+ export interface PHPRequestHandlerConfiguration {
58
56
  /**
59
- * PHP errors.
57
+ * The directory in the PHP filesystem where the server will look
58
+ * for the files to serve. Default: `/var/www`.
60
59
  */
61
- errors: string;
60
+ documentRoot?: string;
62
61
  /**
63
- * Response headers.
62
+ * Request Handler URL. Used to populate $_SERVER details like HTTP_HOST.
64
63
  */
65
- headers: Record<string, string[]>;
64
+ absoluteUrl?: string;
66
65
  /**
67
- * Response HTTP status code, e.g. 200.
66
+ * Callback used by the PHPRequestHandler to decide whether
67
+ * the requested path refers to a PHP file or a static file.
68
68
  */
69
- httpStatusCode: number;
69
+ isStaticFilePath?: (path: string) => boolean;
70
70
  }
71
- export type PHPRuntimeId = number;
72
71
  /**
73
- * Loads the PHP runtime with the given arguments and data dependencies.
74
- *
75
- * This function handles the entire PHP initialization pipeline. In particular, it:
76
- *
77
- * * Instantiates the Emscripten PHP module
78
- * * Wires it together with the data dependencies and loads them
79
- * * Ensures is all happens in a correct order
80
- * * Waits until the entire loading sequence is finished
81
- *
82
- * Basic usage:
72
+ * A fake PHP server that handles HTTP requests but does not
73
+ * bind to any port.
83
74
  *
75
+ * @public
76
+ * @example Use PHPRequestHandler implicitly with a new PHP instance:
84
77
  * ```js
85
- * const phpLoaderModule = await getPHPLoaderModule("7.4");
86
- * const php = await loadPHPRuntime( phpLoaderModule );
87
- * console.log(php.run(`<?php echo "Hello, world!"; `));
88
- * // { stdout: ArrayBuffer containing the string "Hello, world!", stderr: [''], exitCode: 0 }
89
- * ```
90
- *
91
- * **The PHP loader module:**
78
+ * import { PHP } from '@php-wasm/web';
92
79
  *
93
- * In the basic usage example, `phpLoaderModule` is **not** a vanilla Emscripten module. Instead,
94
- * it's an ESM module that wraps the regular Emscripten output and adds some
95
- * extra functionality. It's generated by the Dockerfile shipped with this repo.
96
- * Here's the API it provides:
80
+ * const php = await PHP.load( '7.4', {
81
+ * requestHandler: {
82
+ * // PHP FS path to serve the files from:
83
+ * documentRoot: '/www',
97
84
  *
98
- * ```js
99
- * // php.wasm size in bytes:
100
- * export const dependenciesTotalSize = 5644199;
85
+ * // Used to populate $_SERVER['SERVER_NAME'] etc.:
86
+ * absoluteUrl: 'http://127.0.0.1'
87
+ * }
88
+ * } );
101
89
  *
102
- * // php.wasm filename:
103
- * export const dependencyFilename = 'php.wasm';
90
+ * php.mkdirTree('/www');
91
+ * php.writeFile('/www/index.php', '<?php echo "Hi from PHP!"; ');
104
92
  *
105
- * // Run Emscripten's generated module:
106
- * export default function(jsEnv, emscriptenModuleArgs) {}
93
+ * const response = await php.request({ path: '/index.php' });
94
+ * console.log(response.text);
95
+ * // "Hi from PHP!"
107
96
  * ```
108
97
  *
109
- * **PHP Filesystem:**
110
- *
111
- * Once initialized, the PHP has its own filesystem separate from the project
112
- * files. It's provided by [Emscripten and uses its FS library](https://emscripten.org/docs/api_reference/Filesystem-API.html).
113
- *
114
- * The API exposed to you via the PHP class is succinct and abstracts
115
- * await certain unintuitive parts of low-level FS interactions.
116
- *
117
- * Here's how to use it:
118
- *
98
+ * @example Explicitly create a PHPRequestHandler instance and run a PHP script:
119
99
  * ```js
120
- * // Recursively create a /var/www directory
121
- * php.mkdirTree('/var/www');
122
- *
123
- * console.log(php.fileExists('/var/www/file.txt'));
124
- * // false
125
- *
126
- * php.writeFile('/var/www/file.txt', 'Hello from the filesystem!');
127
- *
128
- * console.log(php.fileExists('/var/www/file.txt'));
129
- * // true
130
- *
131
- * console.log(php.readFile('/var/www/file.txt'));
132
- * // "Hello from the filesystem!
133
- *
134
- * // Delete the file:
135
- * php.unlink('/var/www/file.txt');
136
- * ```
137
- *
138
- * For more details consult the PHP class directly.
139
- *
140
- * **Data dependencies:**
141
- *
142
- * Using existing PHP packages by manually recreating them file-by-file would
143
- * be quite inconvenient. Fortunately, Emscripten provides a "data dependencies"
144
- * feature.
145
- *
146
- * Data dependencies consist of a `dependency.data` file and a `dependency.js` loader and
147
- * can be packaged with the [file_packager.py tool]( https://emscripten.org/docs/porting/files/packaging_files.html#packaging-using-the-file-packager-tool).
148
- * This project requires wrapping the Emscripten-generated `dependency.js` file in an ES
149
- * module as follows:
150
- *
151
- * 1. Prepend `export default function(emscriptenPHPModule) {'; `
152
- * 2. Prepend `export const dependencyFilename = '<DATA FILE NAME>'; `
153
- * 3. Prepend `export const dependenciesTotalSize = <DATA FILE SIZE>;`
154
- * 4. Append `}`
155
- *
156
- * Be sure to use the `--export-name="emscriptenPHPModule"` file_packager.py option.
100
+ * import {
101
+ * loadPHPRuntime,
102
+ * PHP,
103
+ * PHPRequestHandler,
104
+ * getPHPLoaderModule,
105
+ * } from '@php-wasm/web';
157
106
  *
158
- * You want the final output to look as follows:
107
+ * const runtime = await loadPHPRuntime( await getPHPLoaderModule('7.4') );
108
+ * const php = new PHP( runtime );
159
109
  *
160
- * ```js
161
- * export const dependenciesTotalSize = 5644199;
162
- * export const dependencyFilename = 'dependency.data';
163
- * export default function(emscriptenPHPModule) {
164
- * // Emscripten-generated code:
165
- * var Module = typeof emscriptenPHPModule !== 'undefined' ? emscriptenPHPModule : {};
166
- * // ... the rest of it ...
167
- * }
168
- * ```
110
+ * php.mkdirTree('/www');
111
+ * php.writeFile('/www/index.php', '<?php echo "Hi from PHP!"; ');
169
112
  *
170
- * Such a constructions enables loading the `dependency.js` as an ES Module using
171
- * `import("/dependency.js")`.
113
+ * const server = new PHPRequestHandler(php, {
114
+ * // PHP FS path to serve the files from:
115
+ * documentRoot: '/www',
172
116
  *
173
- * Once it's ready, you can load PHP and your data dependencies as follows:
117
+ * // Used to populate $_SERVER['SERVER_NAME'] etc.:
118
+ * absoluteUrl: 'http://127.0.0.1'
119
+ * });
174
120
  *
175
- * ```js
176
- * const [phpLoaderModule, wordPressLoaderModule] = await Promise.all([
177
- * getPHPLoaderModule("7.4"),
178
- * import("/wp.js")
179
- * ]);
180
- * const php = await loadPHPRuntime(phpLoaderModule, {}, [wordPressLoaderModule]);
121
+ * const response = server.request({ path: '/index.php' });
122
+ * console.log(response.text);
123
+ * // "Hi from PHP!"
181
124
  * ```
125
+ */
126
+ export declare class PHPRequestHandler {
127
+ #private;
128
+ /**
129
+ * The PHP instance
130
+ */
131
+ php: BasePHP;
132
+ /**
133
+ * @param php - The PHP instance.
134
+ * @param config - Request Handler configuration.
135
+ */
136
+ constructor(php: BasePHP, config?: PHPRequestHandlerConfiguration);
137
+ /**
138
+ * Converts a path to an absolute URL based at the PHPRequestHandler
139
+ * root.
140
+ *
141
+ * @param path The server path to convert to an absolute URL.
142
+ * @returns The absolute URL.
143
+ */
144
+ pathToInternalUrl(path: string): string;
145
+ /**
146
+ * Converts an absolute URL based at the PHPRequestHandler to a relative path
147
+ * without the server pathname and scope.
148
+ *
149
+ * @param internalUrl An absolute URL based at the PHPRequestHandler root.
150
+ * @returns The relative path.
151
+ */
152
+ internalUrlToPath(internalUrl: string): string;
153
+ get isRequestRunning(): boolean;
154
+ /**
155
+ * The absolute URL of this PHPRequestHandler instance.
156
+ */
157
+ get absoluteUrl(): string;
158
+ /**
159
+ * The absolute URL of this PHPRequestHandler instance.
160
+ */
161
+ get documentRoot(): string;
162
+ /**
163
+ * Serves the request – either by serving a static file, or by
164
+ * dispatching it to the PHP runtime.
165
+ *
166
+ * @param request - The request.
167
+ * @returns The response.
168
+ */
169
+ request(request: PHPRequest): Promise<PHPResponse>;
170
+ }
171
+ export interface PHPBrowserConfiguration {
172
+ /**
173
+ * Should handle redirects internally?
174
+ */
175
+ handleRedirects?: boolean;
176
+ /**
177
+ * The maximum number of redirects to follow internally. Once
178
+ * exceeded, request() will return the redirecting response.
179
+ */
180
+ maxRedirects?: number;
181
+ }
182
+ /**
183
+ * A fake web browser that handles PHPRequestHandler's cookies and redirects
184
+ * internally without exposing them to the consumer.
182
185
  *
183
186
  * @public
184
- * @param phpLoaderModule - The ESM-wrapped Emscripten module. Consult the Dockerfile for the build process.
185
- * @param phpModuleArgs - The Emscripten module arguments, see https://emscripten.org/docs/api_reference/module.html#affecting-execution.
186
- * @param dataDependenciesModules - A list of the ESM-wrapped Emscripten data dependency modules.
187
- * @returns Loaded runtime id.
188
187
  */
189
- export declare function loadPHPRuntime(phpLoaderModule: PHPLoaderModule, phpModuleArgs?: EmscriptenOptions, dataDependenciesModules?: DataModule[]): Promise<number>;
188
+ export declare class PHPBrowser implements WithRequestHandler {
189
+ #private;
190
+ server: PHPRequestHandler;
191
+ /**
192
+ * @param server - The PHP server to browse.
193
+ * @param config - The browser configuration.
194
+ */
195
+ constructor(server: PHPRequestHandler, config?: PHPBrowserConfiguration);
196
+ /**
197
+ * Sends the request to the server.
198
+ *
199
+ * When cookies are present in the response, this method stores
200
+ * them and sends them with any subsequent requests.
201
+ *
202
+ * When a redirection is present in the response, this method
203
+ * follows it by discarding a response and sending a subsequent
204
+ * request.
205
+ *
206
+ * @param request - The request.
207
+ * @param redirects - Internal. The number of redirects handled so far.
208
+ * @returns PHPRequestHandler response.
209
+ */
210
+ request(request: PHPRequest, redirects?: number): Promise<PHPResponse>;
211
+ }
212
+ export type RuntimeType = "NODE" | "WEB" | "WORKER";
213
+ export type PHPRequestHeaders = Record<string, string>;
214
+ export interface FileInfo {
215
+ key: string;
216
+ name: string;
217
+ type: string;
218
+ data: Uint8Array;
219
+ }
220
+ export interface PHPRunOptions {
221
+ /**
222
+ * Request path following the domain:port part.
223
+ */
224
+ relativeUri?: string;
225
+ /**
226
+ * Path of the .php file to execute.
227
+ */
228
+ scriptPath?: string;
229
+ /**
230
+ * Request protocol.
231
+ */
232
+ protocol?: string;
233
+ /**
234
+ * Request method. Default: `GET`.
235
+ */
236
+ method?: "GET" | "POST" | "HEAD" | "OPTIONS" | "PATCH" | "PUT" | "DELETE";
237
+ /**
238
+ * Request headers.
239
+ */
240
+ headers?: PHPRequestHeaders;
241
+ /**
242
+ * Request body without the files.
243
+ */
244
+ body?: string;
245
+ /**
246
+ * Uploaded files.
247
+ */
248
+ fileInfos?: FileInfo[];
249
+ /**
250
+ * The code snippet to eval instead of a php file.
251
+ */
252
+ code?: string;
253
+ }
254
+ export type PHPRuntimeId = number;
190
255
  export interface WithPHPIniBindings {
191
256
  setPhpIniPath(path: string): void;
192
257
  setPhpIniEntry(key: string, value: string): void;
@@ -274,21 +339,30 @@ export interface WithFilesystem {
274
339
  * @returns True if the file exists, false otherwise.
275
340
  */
276
341
  fileExists(path: string): boolean;
342
+ /**
343
+ * Changes the current working directory in the PHP filesystem.
344
+ * This is the directory that will be used as the base for relative paths.
345
+ * For example, if the current working directory is `/root/php`, and the
346
+ * path is `data`, the absolute path will be `/root/php/data`.
347
+ *
348
+ * @param path - The new working directory.
349
+ */
350
+ chdir(path: string): void;
277
351
  }
278
352
  export interface WithRun {
279
353
  /**
280
- * Dispatches a PHP request.
354
+ * Runs PHP code.
281
355
  * Cannot be used in conjunction with `cli()`.
282
356
  *
283
357
  * @example
284
358
  * ```js
285
- * const output = php.run('<?php echo "Hello world!";');
359
+ * const output = await php.run('<?php echo "Hello world!";');
286
360
  * console.log(output.stdout); // "Hello world!"
287
361
  * ```
288
362
  *
289
363
  * @example
290
364
  * ```js
291
- * console.log(php.run(`<?php
365
+ * console.log(await php.run(`<?php
292
366
  * $fp = fopen('php://stderr', 'w');
293
367
  * fwrite($fp, "Hello, world!");
294
368
  * `));
@@ -297,13 +371,37 @@ export interface WithRun {
297
371
  *
298
372
  * @param request - PHP Request data.
299
373
  */
300
- run(request?: PHPRequest): PHPResponse;
374
+ run(request?: PHPRunOptions): Promise<PHPResponse>;
375
+ }
376
+ export interface WithRequestHandler {
377
+ /**
378
+ * Dispatches a HTTP request using PHP as a backend.
379
+ * Cannot be used in conjunction with `cli()`.
380
+ *
381
+ * @example
382
+ * ```js
383
+ * const output = await php.request({
384
+ * method: 'GET',
385
+ * url: '/index.php',
386
+ * headers: {
387
+ * 'X-foo': 'bar',
388
+ * },
389
+ * formData: {
390
+ * foo: 'bar',
391
+ * },
392
+ * });
393
+ * console.log(output.stdout); // "Hello world!"
394
+ * ```
395
+ *
396
+ * @param request - PHP Request data.
397
+ */
398
+ request(request?: PHPRequest): Promise<PHPResponse>;
301
399
  }
302
400
  export type PHPRuntime = any;
303
401
  export type PHPLoaderModule = {
304
402
  dependencyFilename: string;
305
403
  dependenciesTotalSize: number;
306
- default: (jsRuntime: string, options: EmscriptenOptions) => PHPRuntime;
404
+ init: (jsRuntime: string, options: EmscriptenOptions) => PHPRuntime;
307
405
  };
308
406
  export type DataModule = {
309
407
  dependencyFilename: string;
@@ -323,38 +421,35 @@ export type EmscriptenOptions = {
323
421
  } & Record<string, any>;
324
422
  export type MountSettings = {
325
423
  root: string;
326
- mountpoint: string;
327
424
  };
328
- /**
329
- * An environment-agnostic wrapper around the Emscripten PHP runtime
330
- * that abstracts the super low-level API and provides a more convenient
331
- * higher-level API.
332
- *
333
- * It exposes a minimal set of methods to run PHP scripts and to
334
- * interact with the PHP filesystem.
335
- *
336
- * @see {startPHP} This class is not meant to be used directly. Use `startPHP` instead.
337
- */
338
- export declare class PHP implements WithPHPIniBindings, WithFilesystem, WithNodeFilesystem, WithCLI, WithRun {
425
+ declare abstract class BasePHP implements WithPHPIniBindings, WithFilesystem, WithNodeFilesystem, WithCLI, WithRequestHandler, WithRun {
339
426
  #private;
427
+ requestHandler?: PHPBrowser;
340
428
  /**
341
429
  * Initializes a PHP runtime.
342
430
  *
343
431
  * @internal
344
432
  * @param PHPRuntime - Optional. PHP Runtime ID as initialized by loadPHPRuntime.
433
+ * @param serverOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
345
434
  */
346
- constructor(PHPRuntimeId?: PHPRuntimeId);
435
+ constructor(PHPRuntimeId?: PHPRuntimeId, serverOptions?: PHPRequestHandlerConfiguration);
347
436
  initializeRuntime(runtimeId: PHPRuntimeId): void;
348
437
  /** @inheritDoc */
349
438
  setPhpIniPath(path: string): void;
350
439
  /** @inheritDoc */
351
440
  setPhpIniEntry(key: string, value: string): void;
352
441
  /** @inheritDoc */
353
- run(request?: PHPRequest): PHPResponse;
442
+ chdir(path: string): void;
443
+ /** @inheritDoc */
444
+ request(request: PHPRequest, maxRedirects?: number): Promise<PHPResponse>;
445
+ /** @inheritDoc */
446
+ run(request?: PHPRunOptions): Promise<PHPResponse>;
354
447
  cli(argv: string[]): Promise<number>;
355
448
  setSkipShebang(shouldSkip: boolean): void;
356
449
  addServerGlobalEntry(key: string, value: string): void;
450
+ /** @inheritDoc */
357
451
  mkdirTree(path: string): void;
452
+ /** @inheritDoc */
358
453
  readFileAsText(path: string): string;
359
454
  /** @inheritDoc */
360
455
  readFileAsBuffer(path: string): Uint8Array;
@@ -364,8 +459,11 @@ export declare class PHP implements WithPHPIniBindings, WithFilesystem, WithNode
364
459
  unlink(path: string): void;
365
460
  /** @inheritDoc */
366
461
  listFiles(path: string): string[];
462
+ /** @inheritDoc */
367
463
  isDir(path: string): boolean;
464
+ /** @inheritDoc */
368
465
  fileExists(path: string): boolean;
466
+ /** @inheritDoc */
369
467
  mount(settings: MountSettings, path: string): void;
370
468
  }
371
469
  /**
@@ -380,164 +478,149 @@ export interface PHPOutput {
380
478
  stderr: string[];
381
479
  }
382
480
  /**
383
- * Emscripten's filesystem-related Exception.
481
+ * Loads the PHP runtime with the given arguments and data dependencies.
384
482
  *
385
- * @see https://emscripten.org/docs/api_reference/Filesystem-API.html
386
- * @see https://github.com/emscripten-core/emscripten/blob/main/system/lib/libc/musl/arch/emscripten/bits/errno.h
387
- */
388
- export type ErrnoError = Error;
389
- export type PHPServerRequest = Pick<PHPRequest, "method" | "headers"> & {
390
- files?: Record<string, File>;
391
- } & ({
392
- absoluteUrl: string;
393
- relativeUrl?: never;
394
- } | {
395
- absoluteUrl?: never;
396
- relativeUrl: string;
397
- }) & ((Pick<PHPRequest, "body"> & {
398
- formData?: never;
399
- }) | {
400
- body?: never;
401
- formData: Record<string, unknown>;
402
- });
403
- /**
404
- * A fake PHP server that handles HTTP requests but does not
405
- * bind to any port.
483
+ * This function handles the entire PHP initialization pipeline. In particular, it:
484
+ *
485
+ * * Instantiates the Emscripten PHP module
486
+ * * Wires it together with the data dependencies and loads them
487
+ * * Ensures is all happens in a correct order
488
+ * * Waits until the entire loading sequence is finished
489
+ *
490
+ * Basic usage:
406
491
  *
407
- * @public
408
- * @example
409
492
  * ```js
410
- * import {
411
- * loadPHPRuntime,
412
- * PHP,
413
- * PHPServer,
414
- * PHPBrowser,
415
- * getPHPLoaderModule,
416
- * } from '@php-wasm/web';
493
+ * const phpLoaderModule = await getPHPLoaderModule("7.4");
494
+ * const php = await loadPHPRuntime( phpLoaderModule );
495
+ * console.log(php.run(`<?php echo "Hello, world!"; `));
496
+ * // { stdout: ArrayBuffer containing the string "Hello, world!", stderr: [''], exitCode: 0 }
497
+ * ```
417
498
  *
418
- * const runtime = await loadPHPRuntime( await getPHPLoaderModule('7.4') );
419
- * const php = new PHP( runtime );
499
+ * **The PHP loader module:**
420
500
  *
421
- * php.mkdirTree('/www');
422
- * php.writeFile('/www/index.php', '<?php echo "Hi from PHP!"; ');
501
+ * In the basic usage example, `phpLoaderModule` is **not** a vanilla Emscripten module. Instead,
502
+ * it's an ESM module that wraps the regular Emscripten output and adds some
503
+ * extra functionality. It's generated by the Dockerfile shipped with this repo.
504
+ * Here's the API it provides:
423
505
  *
424
- * const server = new PHPServer(php, {
425
- * // PHP FS path to serve the files from:
426
- * documentRoot: '/www',
506
+ * ```js
507
+ * // php.wasm size in bytes:
508
+ * export const dependenciesTotalSize = 5644199;
427
509
  *
428
- * // Used to populate $_SERVER['SERVER_NAME'] etc.:
429
- * absoluteUrl: 'http://127.0.0.1'
430
- * });
510
+ * // php.wasm filename:
511
+ * export const dependencyFilename = 'php.wasm';
431
512
  *
432
- * const output = server.request({ path: '/index.php' }).body;
433
- * console.log(new TextDecoder().decode(output));
434
- * // "Hi from PHP!"
513
+ * // Run Emscripten's generated module:
514
+ * export default function(jsEnv, emscriptenModuleArgs) {}
515
+ * ```
516
+ *
517
+ * **PHP Filesystem:**
518
+ *
519
+ * Once initialized, the PHP has its own filesystem separate from the project
520
+ * files. It's provided by [Emscripten and uses its FS library](https://emscripten.org/docs/api_reference/Filesystem-API.html).
521
+ *
522
+ * The API exposed to you via the PHP class is succinct and abstracts
523
+ * await certain unintuitive parts of low-level FS interactions.
524
+ *
525
+ * Here's how to use it:
526
+ *
527
+ * ```js
528
+ * // Recursively create a /var/www directory
529
+ * php.mkdirTree('/var/www');
530
+ *
531
+ * console.log(php.fileExists('/var/www/file.txt'));
532
+ * // false
533
+ *
534
+ * php.writeFile('/var/www/file.txt', 'Hello from the filesystem!');
535
+ *
536
+ * console.log(php.fileExists('/var/www/file.txt'));
537
+ * // true
538
+ *
539
+ * console.log(php.readFile('/var/www/file.txt'));
540
+ * // "Hello from the filesystem!
541
+ *
542
+ * // Delete the file:
543
+ * php.unlink('/var/www/file.txt');
544
+ * ```
545
+ *
546
+ * For more details consult the PHP class directly.
547
+ *
548
+ * **Data dependencies:**
549
+ *
550
+ * Using existing PHP packages by manually recreating them file-by-file would
551
+ * be quite inconvenient. Fortunately, Emscripten provides a "data dependencies"
552
+ * feature.
553
+ *
554
+ * Data dependencies consist of a `dependency.data` file and a `dependency.js` loader and
555
+ * can be packaged with the [file_packager.py tool]( https://emscripten.org/docs/porting/files/packaging_files.html#packaging-using-the-file-packager-tool).
556
+ * This project requires wrapping the Emscripten-generated `dependency.js` file in an ES
557
+ * module as follows:
558
+ *
559
+ * 1. Prepend `export default function(emscriptenPHPModule) {'; `
560
+ * 2. Prepend `export const dependencyFilename = '<DATA FILE NAME>'; `
561
+ * 3. Prepend `export const dependenciesTotalSize = <DATA FILE SIZE>;`
562
+ * 4. Append `}`
563
+ *
564
+ * Be sure to use the `--export-name="emscriptenPHPModule"` file_packager.py option.
565
+ *
566
+ * You want the final output to look as follows:
567
+ *
568
+ * ```js
569
+ * export const dependenciesTotalSize = 5644199;
570
+ * export const dependencyFilename = 'dependency.data';
571
+ * export default function(emscriptenPHPModule) {
572
+ * // Emscripten-generated code:
573
+ * var Module = typeof emscriptenPHPModule !== 'undefined' ? emscriptenPHPModule : {};
574
+ * // ... the rest of it ...
575
+ * }
576
+ * ```
577
+ *
578
+ * Such a constructions enables loading the `dependency.js` as an ES Module using
579
+ * `import("/dependency.js")`.
580
+ *
581
+ * Once it's ready, you can load PHP and your data dependencies as follows:
582
+ *
583
+ * ```js
584
+ * const [phpLoaderModule, wordPressLoaderModule] = await Promise.all([
585
+ * getPHPLoaderModule("7.4"),
586
+ * import("/wp.js")
587
+ * ]);
588
+ * const php = await loadPHPRuntime(phpLoaderModule, {}, [wordPressLoaderModule]);
435
589
  * ```
590
+ *
591
+ * @public
592
+ * @param phpLoaderModule - The ESM-wrapped Emscripten module. Consult the Dockerfile for the build process.
593
+ * @param phpModuleArgs - The Emscripten module arguments, see https://emscripten.org/docs/api_reference/module.html#affecting-execution.
594
+ * @param dataDependenciesModules - A list of the ESM-wrapped Emscripten data dependency modules.
595
+ * @returns Loaded runtime id.
436
596
  */
437
- export declare class PHPServer {
438
- #private;
439
- /**
440
- * The PHP instance
441
- */
442
- php: PHP;
443
- /**
444
- * @param php - The PHP instance.
445
- * @param config - Server configuration.
446
- */
447
- constructor(php: PHP, config?: PHPServerConfigation);
448
- /**
449
- * Converts a path to an absolute URL based at the PHPServer
450
- * root.
451
- *
452
- * @param path The server path to convert to an absolute URL.
453
- * @returns The absolute URL.
454
- */
455
- pathToInternalUrl(path: string): string;
456
- /**
457
- * Converts an absolute URL based at the PHPServer to a relative path
458
- * without the server pathname and scope.
459
- *
460
- * @param internalUrl An absolute URL based at the PHPServer root.
461
- * @returns The relative path.
462
- */
463
- internalUrlToPath(internalUrl: string): string;
464
- /**
465
- * The absolute URL of this PHPServer instance.
466
- */
467
- get absoluteUrl(): string;
468
- /**
469
- * The absolute URL of this PHPServer instance.
470
- */
471
- get documentRoot(): string;
472
- /**
473
- * Serves the request – either by serving a static file, or by
474
- * dispatching it to the PHP runtime.
475
- *
476
- * @param request - The request.
477
- * @returns The response.
478
- */
479
- request(request: PHPServerRequest): Promise<PHPResponse>;
480
- }
481
- export interface PHPServerConfigation {
482
- /**
483
- * The directory in the PHP filesystem where the server will look
484
- * for the files to serve. Default: `/var/www`.
485
- */
486
- documentRoot?: string;
487
- /**
488
- * Server URL. Used to populate $_SERVER details like HTTP_HOST.
489
- */
490
- absoluteUrl?: string;
491
- /**
492
- * Callback used by the PHPServer to decide whether
493
- * the requested path refers to a PHP file or a static file.
494
- */
495
- isStaticFilePath?: (path: string) => boolean;
496
- }
497
- export interface WithRequest {
498
- /**
499
- * Sends the request to the server.
500
- *
501
- * When cookies are present in the response, this method stores
502
- * them and sends them with any subsequent requests.
503
- *
504
- * When a redirection is present in the response, this method
505
- * follows it by discarding a response and sending a subsequent
506
- * request.
507
- *
508
- * @param request - The request.
509
- * @param redirects - Internal. The number of redirects handled so far.
510
- * @returns PHPServer response.
511
- */
512
- request(request: PHPServerRequest, redirects?: number): Promise<PHPResponse>;
513
- }
597
+ export declare function loadPHPRuntime(phpLoaderModule: PHPLoaderModule, phpModuleArgs?: EmscriptenOptions, dataDependenciesModules?: DataModule[]): Promise<number>;
514
598
  /**
515
- * A fake web browser that handles PHPServer's cookies and redirects
516
- * internally without exposing them to the consumer.
599
+ * Emscripten's filesystem-related Exception.
517
600
  *
518
- * @public
601
+ * @see https://emscripten.org/docs/api_reference/Filesystem-API.html
602
+ * @see https://github.com/emscripten-core/emscripten/blob/main/system/lib/libc/musl/arch/emscripten/bits/errno.h
603
+ * @see https://github.com/emscripten-core/emscripten/blob/38eedc630f17094b3202fd48ac0c2c585dbea31e/system/include/wasi/api.h#L336
519
604
  */
520
- export declare class PHPBrowser implements WithRequest {
521
- #private;
522
- server: PHPServer;
523
- /**
524
- * @param server - The PHP server to browse.
525
- * @param config - The browser configuration.
526
- */
527
- constructor(server: PHPServer, config?: PHPBrowserConfiguration);
528
- request(request: PHPServerRequest, redirects?: number): Promise<PHPResponse>;
529
- }
530
- export interface PHPBrowserConfiguration {
531
- /**
532
- * Should handle redirects internally?
533
- */
534
- handleRedirects?: boolean;
535
- /**
536
- * The maximum number of redirects to follow internally. Once
537
- * exceeded, request() will return the redirecting response.
538
- */
539
- maxRedirects?: number;
605
+ export interface ErrnoError extends Error {
606
+ node?: any;
607
+ errno: number;
608
+ message: string;
540
609
  }
610
+ export declare const SupportedPHPVersions: readonly [
611
+ "8.2",
612
+ "8.1",
613
+ "8.0",
614
+ "7.4",
615
+ "7.3",
616
+ "7.2",
617
+ "7.1",
618
+ "7.0",
619
+ "5.6"
620
+ ];
621
+ export declare const LatestSupportedPHPVersion: "8.2";
622
+ export declare const SupportedPHPVersionsList: string[];
623
+ export type SupportedPHPVersion = (typeof SupportedPHPVersions)[number];
541
624
  export type WorkerStartupOptions<T extends Record<string, string> = Record<string, string>> = T;
542
625
  export declare function consumeAPI<APIType>(remote: Worker | Window): Comlink.Remote<APIType>;
543
626
  export type PublicAPI<Methods, PipedAPI = unknown> = Methods & PipedAPI & {
@@ -554,11 +637,45 @@ export interface MonitoredModule {
554
637
  declare class EmscriptenDownloadMonitor extends EventTarget {
555
638
  #private;
556
639
  constructor(modules?: MonitoredModule[]);
557
- getEmscriptenArgs(): {
640
+ getEmscriptenOptions(): {
558
641
  dataFileDownloads: Record<string, any>;
559
642
  };
560
643
  setModules(modules: MonitoredModule[]): void;
561
644
  }
645
+ export interface PHPWebLoaderOptions {
646
+ emscriptenOptions?: EmscriptenOptions;
647
+ downloadMonitor?: EmscriptenDownloadMonitor;
648
+ requestHandler?: PHPRequestHandlerConfiguration;
649
+ dataModules?: Array<DataModule | Promise<DataModule>>;
650
+ }
651
+ export declare class PHP extends BasePHP {
652
+ /**
653
+ * Creates a new PHP instance.
654
+ *
655
+ * Dynamically imports the PHP module, initializes the runtime,
656
+ * and sets up networking. It's a shorthand for the lower-level
657
+ * functions like `getPHPLoaderModule`, `loadPHPRuntime`, and
658
+ * `PHP.initializeRuntime`
659
+ *
660
+ * @param phpVersion The PHP Version to load
661
+ * @param options The options to use when loading PHP
662
+ * @returns A new PHP instance
663
+ */
664
+ static load(phpVersion: SupportedPHPVersion, options?: PHPWebLoaderOptions): Promise<PHP>;
665
+ /**
666
+ * Does what load() does, but synchronously returns
667
+ * an object with the PHP instance and a promise that
668
+ * resolves when the PHP instance is ready.
669
+ *
670
+ * @see load
671
+ * @inheritdoc load
672
+ */
673
+ static loadSync(phpVersion: SupportedPHPVersion, options?: PHPWebLoaderOptions): {
674
+ php: PHP;
675
+ phpReady: Promise<PHP>;
676
+ dataModules: Promise<DataModule[]>;
677
+ };
678
+ }
562
679
  /** @inheritdoc T */
563
680
  export type Promisify<T> = {
564
681
  [P in keyof T]: T[P] extends (...args: infer A) => infer R ? R extends void | Promise<any> ? T[P] : (...args: A) => Promise<ReturnType<T[P]>> : Promise<T[P]>;
@@ -573,22 +690,24 @@ export interface WithProgress {
573
690
  /**
574
691
  * A PHP client that can be used to run PHP code in the browser.
575
692
  */
576
- export declare class PHPClient implements Promisify<WithRequest & WithPHPIniBindings & WithFilesystem & WithRun & WithProgress & WithPathConversion> {
577
- /** @inheritDoc @php-wasm/web!PHPServer.absoluteUrl */
693
+ export declare class PHPClient implements Promisify<WithPHPIniBindings & WithFilesystem & WithRun & WithRequestHandler & WithProgress & WithPathConversion> {
694
+ /** @inheritDoc @php-wasm/web!PHPRequestHandler.absoluteUrl */
578
695
  absoluteUrl: Promise<string>;
579
- /** @inheritDoc @php-wasm/web!PHPServer.documentRoot */
696
+ /** @inheritDoc @php-wasm/web!PHPRequestHandler.documentRoot */
580
697
  documentRoot: Promise<string>;
581
698
  /** @inheritDoc */
582
- constructor(browser: PHPBrowser, monitor?: EmscriptenDownloadMonitor);
583
- /** @inheritDoc @php-wasm/web!PHPServer.pathToInternalUrl */
699
+ constructor(php: BasePHP, monitor?: EmscriptenDownloadMonitor);
700
+ /** @inheritDoc @php-wasm/web!PHPRequestHandler.pathToInternalUrl */
584
701
  pathToInternalUrl(path: string): Promise<string>;
585
- /** @inheritDoc @php-wasm/web!PHPServer.internalUrlToPath */
702
+ /** @inheritDoc @php-wasm/web!PHPRequestHandler.internalUrlToPath */
586
703
  internalUrlToPath(internalUrl: string): Promise<string>;
587
704
  onDownloadProgress(callback: (progress: CustomEvent<ProgressEvent>) => void): Promise<void>;
588
- /** @inheritDoc @php-wasm/web!PHPServer.request */
589
- request(request: PHPServerRequest, redirects?: number): Promise<PHPResponse>;
705
+ /** @inheritDoc @php-wasm/web!PHPRequestHandler.request */
706
+ request(request: PHPRequest, redirects?: number): Promise<PHPResponse>;
590
707
  /** @inheritDoc @php-wasm/web!PHP.run */
591
- run(request?: PHPRequest | undefined): Promise<PHPResponse>;
708
+ run(request?: PHPRunOptions): Promise<PHPResponse>;
709
+ /** @inheritDoc @php-wasm/web!PHP.chdir */
710
+ chdir(path: string): void;
592
711
  /** @inheritDoc @php-wasm/web!PHP.setPhpIniPath */
593
712
  setPhpIniPath(path: string): void;
594
713
  /** @inheritDoc @php-wasm/web!PHP.setPhpIniEntry */
@@ -610,8 +729,18 @@ export declare class PHPClient implements Promisify<WithRequest & WithPHPIniBind
610
729
  /** @inheritDoc @php-wasm/web!PHP.fileExists */
611
730
  fileExists(path: string): Promise<boolean>;
612
731
  }
613
- export declare const isAssignableToRemoteClient: PHPClient;
614
- export declare function getPHPLoaderModule(version?: string): Promise<PHPLoaderModule>;
732
+ export declare function getPHPLoaderModule(version?: SupportedPHPVersion): Promise<PHPLoaderModule>;
733
+ /**
734
+ * Run this in the main application to register the service worker or
735
+ * reload the registered worker if the app expects a different version
736
+ * than the currently registered one.
737
+ *
738
+ * @param {string} scriptUrl The URL of the service worker script.
739
+ * @param {string} expectedVersion The expected version of the service worker script. If
740
+ * mismatched with the actual version, the service worker
741
+ * will be re-registered.
742
+ */
743
+ export declare function registerServiceWorker<Client extends PHPClient>(phpApi: Client, scope: string, scriptUrl: string, expectedVersion: string): Promise<void>;
615
744
  export declare function parseWorkerStartupOptions<T extends Record<string, string>>(): WorkerStartupOptions<T>;
616
745
  /**
617
746
  * Recommended Worker Thread backend.
@@ -629,16 +758,5 @@ export declare const recommendedWorkerBackend: string;
629
758
  * @returns The spawned Worker Thread.
630
759
  */
631
760
  export declare function spawnPHPWorkerThread(workerUrl: string, workerBackend?: "webworker" | "iframe", startupOptions?: Record<string, string>): Promise<Window | Worker>;
632
- /**
633
- * Run this in the main application to register the service worker or
634
- * reload the registered worker if the app expects a different version
635
- * than the currently registered one.
636
- *
637
- * @param {string} scriptUrl The URL of the service worker script.
638
- * @param {string} expectedVersion The expected version of the service worker script. If
639
- * mismatched with the actual version, the service worker
640
- * will be re-registered.
641
- */
642
- export declare function registerServiceWorker<Client extends PHPClient>(phpApi: Client, scope: string, scriptUrl: string, expectedVersion: string): Promise<void>;
643
761
 
644
762
  export {};