Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 51 additions & 7 deletions packages/php-wasm/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@ Here's how to use it:

```js
import { PHP } from '@php-wasm/web';
import { loadWebRuntime } from '@php-wasm/web'

// PHP.load() calls import('php.wasm') internally
// loadWebRuntime() calls import('php.wasm') and import('icudt74l.dat') internally.
// Your bundler must resolve import('php.wasm') as a static file URL.
// If you use Webpack, you can use the file-loader to do so.
const php = await PHP.load('8.0', {
requestHandler: {
documentRoot: '/www',
},
});
const php = new PHP(await loadWebRuntime('8.3'))

// Create and run a script directly
php.mkdirTree('/www');
php.mkdir('/www');
php.writeFile('/www/index.php', `<?php echo "Hello " . $_POST['name']; ?>`);
await php.run({ scriptPath: './index.php' });

Comment on lines 8 to 20
Copy link
Collaborator

@mho22 mho22 Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to reproduce this in a separate project and lots of things have been deprecated, like php.run or php.request. scriptPath had to be /www/index.php and it returned an error since we do not inject name. The second use case is in favor of a PHPRequestHandler instead of php.request.

So here is an updated version that will run a simple script first, and then a HTTP request in the console :

import { PHP, PHPRequestHandler } from '@php-wasm/universal';
import { loadWebRuntime } from '@php-wasm/web';

// loadWebRuntime() calls import('php.wasm') and import('icudt74l.dat') internally.
// Your bundler must resolve import('php.wasm') as a static file URL.
// If you use Webpack, you can use the file-loader to do so.
const php = new PHP(await loadWebRuntime('8.3'));

let response;

php.writeFile('/test.php', `<?php echo "Hello, World!"; ?>`);

// Run a script directly:
response = await php.runStream( {
    scriptPath: '/test.php',
} );

console.log(await response.stdoutText);

php.mkdir('/www');
php.writeFile('/www/index.php', `<?php echo "Hello " . $_POST['name']; ?>`);

// Or use the familiar HTTP concepts:
const handler = new PHPRequestHandler({ phpFactory: async () => php });

response = await handler.request({
	method: 'POST',
	url: 'index.php',
	body: { name: 'John' },
});

console.log(response.text);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggestion has been merged in #2778

Expand All @@ -30,6 +27,53 @@ const response = await php.request({
console.log(response.text);
```

## Usage with bundlers

If you use `@php-wasm/web` with a bundler such as Vite, you may see the following errors:

```
01:31:50 [vite] (client) error while updating dependencies:
Error: Error during dependency optimization:

✘ [ERROR] No loader is configured for ".dat" files: app/node_modules/@php-wasm/web/shared/icudt74l.dat

app/node_modules/@php-wasm/web/shared/icudt74l.js:1:25:
1 │ import dataFilename from './icudt74l.dat';
```

The `@php-wasm/web` package imports a few non-JavaScript assets file using the import syntax. This ensures
all the required dependencies may be tracked statically, but it creates an inconvenience for apps relying
on bundlers.

To resolve that error, you'll need to configure your bundler to resolve the import above to the URL
of the `icudt74l.dat` in your app, e.g. `https://playground.wordpress.net/assets/icudt74l.dat`.
Comment on lines +30 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that, I should have created that section when we struggled with this a few months back with @bgrgicak.


In Vite, you can use the following options to support importing all the required assets types:

```js
export default defineConfig({
assetsInclude: [/\.dat$/, /\.wasm$/, /\.so$/, /\.la$/],
optimizeDeps: {
esbuildOptions: {
loader: {
'.dat': 'file',
'.wasm': 'file',
'.so': 'file',
'.la': 'file',
Comment on lines +59 to +62
Copy link
Collaborator

@mho22 mho22 Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced file with text and the build part was no more needed. I don't know if that matters.

import { defineConfig } from 'vite';

export default defineConfig( {
	assetsInclude: [/\.dat$/, /\.wasm$/, /\.so$/, /\.la$/],
    optimizeDeps : {
		esbuildOptions : {
			loader : {
				'.dat': 'text',
				'.wasm': 'text',
				'.so': 'text',
				'.la': 'text',
			}
		}
	}
} );

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mho22 what build part are you referring to. I feel like I'm missing something obvious.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I posted this and only then noticed the build key directly below this comment. ✅

},
},
},
build: {
// outDir is required with the 'file' loader
outDir: path.resolve(workspaceRoot, 'dist'),
}
});
```

Other bundlers will typically have analogous options or plugins. If you create a working configuration for
WebPack, esbuild, or another bundler, feel free to propose a new configuration example for this README at
https://github.com/WordPress/wordpress-playground/edit/trunk/packages/php-wasm/web/README.md

## Attribution

`@php-wasm/web` started as a fork of the original PHP to WebAssembly build published by Oraoto in https://github.com/oraoto/pib and modified by Sean Morris in https://github.com/seanmorris/php-wasm.