Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ php.js.bak
# @php-wasm/cli saves this file to disk so PHP has access to it.
# Since it is dynamically generated from the Node.js TLS module,
# we do not want to commit it to the repository.
packages/php-wasm/cli/src/ca-bundle.crt
packages/php-wasm/cli/src/lib/ca-bundle.crt

# PHPUnit
.phpunit.result.cache
Expand Down
2 changes: 1 addition & 1 deletion packages/php-wasm/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"license": "GPL-2.0-or-later",
"type": "module",
"main": "main.js",
"main": "cli.js",
"bin": {
"php-wasm-cli": "php-wasm.js"
},
Expand Down
10 changes: 5 additions & 5 deletions packages/php-wasm/cli/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"options": {
"main": "dist/packages/php-wasm/cli/main.js",
"main": "dist/packages/php-wasm/cli/cli.js",
"outputPath": "dist/packages/php-wasm/cli"
},
"defaultConfiguration": "production",
Expand All @@ -45,7 +45,7 @@
"dev": {
"executor": "nx:run-commands",
"options": {
"command": "node --no-warnings --experimental-wasm-stack-switching --experimental-wasm-jspi --loader=./packages/meta/src/node-es-module-loader/loader.mts ./packages/php-wasm/cli/src/main.ts",
"command": "node --no-warnings --experimental-wasm-stack-switching --experimental-wasm-jspi --loader=./packages/meta/src/node-es-module-loader/loader.mts ./packages/php-wasm/cli/src/cli.ts",
"tty": true
}
},
Expand All @@ -57,7 +57,7 @@
"command": "node -e \"if (parseInt(process.versions.node) < 22) { console.error('Node.js version 22 or greater is required'); process.exit(1); }\"",
"forwardAllArgs": false
},
"node --stack-trace-limit=100 --inspect --experimental-strip-types --experimental-transform-types --import ./packages/meta/src/node-es-module-loader/register.mts --watch ./packages/php-wasm/cli/src/main.ts"
"node --stack-trace-limit=100 --inspect --experimental-strip-types --experimental-transform-types --import ./packages/meta/src/node-es-module-loader/register.mts --watch ./packages/php-wasm/cli/src/cli.ts"
],
"tty": true,
"parallel": false
Expand All @@ -71,7 +71,7 @@
"command": "node -e \"if (parseInt(process.versions.node) < 22) { console.error('Node.js version 22 or greater is required'); process.exit(1); }\"",
"forwardAllArgs": false
},
"node --inspect --experimental-strip-types --experimental-transform-types --import ./packages/meta/src/node-es-module-loader/register.mts ./packages/php-wasm/cli/src/main.ts"
"node --inspect --experimental-strip-types --experimental-transform-types --import ./packages/meta/src/node-es-module-loader/register.mts ./packages/php-wasm/cli/src/cli.ts"
],
"tty": true,
"parallel": false
Expand All @@ -80,7 +80,7 @@
"start": {
"executor": "@wp-playground/nx-extensions:built-script",
"options": {
"scriptPath": "dist/packages/php-wasm/cli/main.js"
"scriptPath": "dist/packages/php-wasm/cli/php-wasm.js"
},
"dependsOn": ["build"]
},
Expand Down
2 changes: 1 addition & 1 deletion packages/php-wasm/cli/public/php-wasm.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env node
import './main.js';
import './run-cli.js';
3 changes: 3 additions & 0 deletions packages/php-wasm/cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { parseOptionsAndRunCLI } from './lib/run-cli';

parseOptionsAndRunCLI();
Original file line number Diff line number Diff line change
@@ -1,56 +1,117 @@
/**
* A CLI script that runs PHP CLI via the WebAssembly build.
*/
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import os from 'os';
import { writeFileSync, existsSync, mkdtempSync, chmodSync } from 'fs';
import { rootCertificates } from 'tls';

import {
LatestSupportedPHPVersion,
SupportedPHPVersionsList,
} from '@php-wasm/universal';
import type { SupportedPHPVersion } from '@php-wasm/universal';

import { FileLockManagerForNode } from '@php-wasm/node';
import { PHP } from '@php-wasm/universal';
import { loadNodeRuntime, useHostFilesystem } from '@php-wasm/node';
import { startBridge } from '@php-wasm/xdebug-bridge';
import path from 'path';

let args = process.argv.slice(2);
if (!args.length) {
args = ['--help'];
interface CLIDefaults {
directive: string[];
config: string;
}

const baseUrl = (import.meta || {}).url;
interface CLIArgs {
_: string[];
config: string;
directive: string[];
xdebug?: boolean;
experimentalDevtools?: boolean;
}

// Write the ca-bundle.crt file to disk so that PHP can find it.
const caBundlePath = new URL('ca-bundle.crt', baseUrl).pathname;
if (!existsSync(caBundlePath)) {
writeFileSync(caBundlePath, rootCertificates.join('\n'));
function parseCliArgs(defaults: CLIDefaults) {
return yargs(hideBin(process.argv))
.usage(
`
PHP.wasm CLI

Usage: php-wasm-cli <command> [options]
`
)
.option('config', {
alias: 'c',
type: 'string',
describe: 'Path to configuration file',
default: defaults.config,
})
.option('directive', {
alias: 'd',
type: 'array',
describe: 'Set configuration directives (key=value)',
default: defaults.directive,
})
.option('xdebug', {
type: 'boolean',
describe: 'Enable Xdebug.',
default: false,
})
.option('experimental-devtools', {
type: 'boolean',
describe: 'Enable experimental browser development tools.',
default: false,
})
.strictCommands()
.help()
.epilog(
`
Examples:
php-wasm-cli # Start with default settings
php-wasm-cli --config /path/to/php.ini # Run PHP CLI with custom config file
php-wasm-cli --xdebug --experimental-devtools # Enable Xdebug and Devtools
`
)
.parseSync() as CLIArgs;
}
args.unshift('-d', `openssl.cafile=${caBundlePath}`);

async function run() {
const defaultPhpIniPath = new URL('php.ini', baseUrl).pathname;
function mapArgsArray(args: CLIArgs): string[] {
const argv: string[] = [];

if (args.config) {
argv.push('-c', args.config);
}

if (Array.isArray(args.directive)) {
args.directive.forEach((d) => {
argv.push('-d', d);
});
}

if (Array.isArray(args._)) {
argv.push(...args._);
}

return argv;
}

export async function parseOptionsAndRunCLI(): Promise<void> {
const phpVersion = (process.env['PHP'] ||
LatestSupportedPHPVersion) as SupportedPHPVersion;
if (!SupportedPHPVersionsList.includes(phpVersion)) {
throw new Error(`Unsupported PHP version ${phpVersion}`);
}

const hasXdebugOption = args.some((arg) => arg.startsWith('--xdebug'));
if (hasXdebugOption) {
args = args.filter((arg) => arg !== '--xdebug');
}
const baseUrl = (import.meta || {}).url;

const hasDevtoolsOption = args.some((arg) =>
arg.startsWith('--experimental-devtools')
);
if (hasDevtoolsOption) {
args = args.filter((arg) => arg !== '--experimental-devtools');
const defaultPhpIniPath = new URL('php.ini', baseUrl).pathname;
// Write the ca-bundle.crt file to disk so that PHP can find it.
const caBundlePath = new URL('ca-bundle.crt', baseUrl).pathname;
if (!existsSync(caBundlePath)) {
writeFileSync(caBundlePath, rootCertificates.join('\n'));
}

const args = parseCliArgs({
directive: [`openssl.cafile=${caBundlePath}`],
config: defaultPhpIniPath,
});

// npm scripts set the TMPDIR env variable
// PHP accepts a TMPDIR env variable and expects it to
// be a writable directory within the PHP filesystem.
Expand Down Expand Up @@ -99,24 +160,20 @@ ${process.argv[0]} ${process.execArgv.join(' ')} ${process.argv[1]}
PATH: `${tempDir}:${envVariables['PATH']}`,
},
},
withXdebug: hasXdebugOption,
withXdebug: args.xdebug,
})
);

useHostFilesystem(php);

if (hasDevtoolsOption && hasXdebugOption) {
if (args.experimentalDevtools && args.xdebug) {
const bridge = await startBridge({});

bridge.start();
}

const hasMinusCOption = args.some((arg) => arg.startsWith('-c'));
if (!hasMinusCOption) {
args.unshift('-c', defaultPhpIniPath);
}
const response = await php.cli(['php', ...mapArgsArray(args)]);

const response = await php.cli(['php', ...args]);
response.stderr.pipeTo(
new WritableStream({
write(chunk) {
Expand Down Expand Up @@ -147,5 +204,3 @@ ${process.argv[0]} ${process.execArgv.join(' ')} ${process.argv[1]}
}, 100);
});
}

run();
2 changes: 1 addition & 1 deletion packages/php-wasm/cli/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default defineConfig({
'ws',
'os',
],
input: 'packages/php-wasm/cli/src/main.ts',
input: 'packages/php-wasm/cli/src/cli.ts',
output: {
format: 'esm',
entryFileNames: '[name].js',
Expand Down
4 changes: 2 additions & 2 deletions packages/php-wasm/xdebug-bridge/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { main } from './lib/run-cli';
import { parseOptionsAndRunCLI } from './lib/run-cli';

main();
parseOptionsAndRunCLI();
8 changes: 4 additions & 4 deletions packages/php-wasm/xdebug-bridge/src/lib/run-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ Usage: xdebug-bridge [options]
.option('port', {
alias: 'p',
type: 'number',
description: 'Xdebug port to listen on',
describe: 'Xdebug port to listen on',
default: 9003,
})
.option('host', {
alias: 'h',
type: 'string',
description: 'Xdebug host to bind to',
describe: 'Xdebug host to bind to',
default: 'localhost',
})
.option('php-root', {
type: 'string',
description: 'Path to PHP root directory',
describe: 'Path to PHP root directory',
default: './',
})
.help()
Expand All @@ -49,7 +49,7 @@ Examples:
.parseSync() as CLIArgs;
}

export async function main(): Promise<void> {
export async function parseOptionsAndRunCLI(): Promise<void> {
const args = parseCliArgs();

if (args.help) {
Expand Down
8 changes: 7 additions & 1 deletion packages/playground/cli/src/run-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ export async function parseOptionsAndRunCLI() {
* Perhaps the two could be handled by the same code?
*/
const yargsObject = yargs(process.argv.slice(2))
.usage('Usage: wp-playground <command> [options]')
.usage(
`
WordPress Playground CLI

Usage: wp-playground-cli <command> [options]
`
)
.positional('command', {
describe: 'Command to run',
choices: ['server', 'run-blueprint', 'build-snapshot'] as const,
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"noPropertyAccessFromIndexSignature": false,
"baseUrl": ".",
"paths": {
"@php-wasm/cli": ["packages/php-wasm/cli/src/main.ts"],
"@php-wasm/cli": ["packages/php-wasm/cli/src/cli.ts"],
"@php-wasm/fs-journal": [
"packages/php-wasm/fs-journal/src/index.ts"
],
Expand Down