Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ jobs:
with:
submodules: true
- uses: ./.github/actions/prepare-playground
- run: packages/playground/cli/tests/test-running-unbuilt-cli.sh
- run: packages/playground/cli/src/tests/test-running-unbuilt-cli.sh

build:
runs-on: ubuntu-latest
Expand Down
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,54 +1,145 @@
/**
* 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';
import { logger, LogSeverity } from '@php-wasm/logger';

const LogVerbosity = {
Quiet: { name: 'quiet', severity: LogSeverity.Fatal },
Normal: { name: 'normal', severity: LogSeverity.Info },
Debug: { name: 'debug', severity: LogSeverity.Debug },
} as const;

type LogVerbosity = (typeof LogVerbosity)[keyof typeof LogVerbosity]['name'];

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;
verbosity?: LogVerbosity;
help?: 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,
})
.option('verbosity', {
type: 'string',
describe: 'Output logs',
choices: Object.values(LogVerbosity).map(
(verbosity) => verbosity.name
),
default: 'normal',
})
.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 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,
});

if (args.help) {
return;
}

const hasDevtoolsOption = args.some((arg) =>
arg.startsWith('--experimental-devtools')
);
if (hasDevtoolsOption) {
args = args.filter((arg) => arg !== '--experimental-devtools');
if (args.verbosity) {
const severity = Object.values(LogVerbosity).find(
(v) => v.name === args.verbosity
)!.severity;
logger.setSeverityFilterLevel(severity);
}

// npm scripts set the TMPDIR env variable
Expand Down Expand Up @@ -99,24 +190,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 +234,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 @@ -31,18 +31,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: './',
})
.option('verbosity', {
Expand All @@ -66,7 +66,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
14 changes: 7 additions & 7 deletions packages/php-wasm/xdebug-bridge/src/tests/run-cli.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './mocker';
import { vi } from 'vitest';
import { main } from '../lib/run-cli';
import { parseOptionsAndRunCLI } from '../lib/run-cli';
import { logger, LogSeverity } from '@php-wasm/logger';
import { startBridge } from '../lib/start-bridge';
import type { XdebugCDPBridge } from '../lib/xdebug-cdp-bridge';
Expand All @@ -26,7 +26,7 @@ describe('CLI', () => {
});

it('calls startBridge with default arguments', async () => {
await main();
await parseOptionsAndRunCLI();

expect(startBridge).toHaveBeenCalledWith({
cdpPort: 9229,
Expand All @@ -49,7 +49,7 @@ describe('CLI', () => {
'/var/www'
);

await main();
await parseOptionsAndRunCLI();

expect(startBridge).toHaveBeenCalledWith({
cdpPort: 9229,
Expand All @@ -63,7 +63,7 @@ describe('CLI', () => {
process.argv.push('--help');

try {
await main();
await parseOptionsAndRunCLI();
} catch (e: any) {
expect(e.message).toBe('process.exit unexpectedly called with "0"');
}
Expand All @@ -74,7 +74,7 @@ describe('CLI', () => {
it('runs cli with verbosity option set to quiet', async () => {
process.argv.push('--verbosity', 'quiet');

await main();
await parseOptionsAndRunCLI();

expect(logger.setSeverityFilterLevel).toHaveBeenCalledWith(
LogSeverity.Fatal
Expand All @@ -84,7 +84,7 @@ describe('CLI', () => {
it('runs cli with verbosity option set to normal', async () => {
process.argv.push('--verbosity', 'normal');

await main();
await parseOptionsAndRunCLI();

expect(logger.setSeverityFilterLevel).toHaveBeenCalledWith(
LogSeverity.Info
Expand All @@ -94,7 +94,7 @@ describe('CLI', () => {
it('runs cli with verbosity option set to debug', async () => {
process.argv.push('--verbosity', 'debug');

await main();
await parseOptionsAndRunCLI();

expect(logger.setSeverityFilterLevel).toHaveBeenCalledWith(
LogSeverity.Debug
Expand Down
1 change: 0 additions & 1 deletion packages/playground/cli/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/packages/playground/cli"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../../coverage/packages/playground/cli"
}
},
Expand Down
Loading