diff --git a/packages/playground/remote/src/lib/worker-thread.ts b/packages/playground/remote/src/lib/worker-thread.ts index 100e45dcf4..0dbf0d31de 100644 --- a/packages/playground/remote/src/lib/worker-thread.ts +++ b/packages/playground/remote/src/lib/worker-thread.ts @@ -303,6 +303,10 @@ try { // let's always fail. php.setSpawnHandler( createSpawnHandler(async function (args, processApi, options) { + if (args[0] === 'exec') { + args.shift(); + } + // Mock programs required by wp-cli: if ( args[0] === '/usr/bin/env' && @@ -323,6 +327,24 @@ try { }); processApi.flushStdin(); processApi.exit(0); + } else if (args[0] === 'fetch') { + processApi.flushStdin(); + fetch(args[1]).then(async (res) => { + const reader = res.body?.getReader(); + if (!reader) { + processApi.exit(1); + return; + } + while (true) { + const { done, value } = await reader.read(); + if (done) { + processApi.exit(0); + break; + } + processApi.stdout(value); + } + }); + return; } else if (args[0] === 'php') { if (!childPHP) { childPHP = new WebPHP(await recreateRuntime(), { @@ -360,7 +382,7 @@ try { $GLOBALS['argv'] = array_merge([ "/wordpress/wp-cli.phar", "--path=/wordpress" - ], ${phpVar(args.slice(1))}); + ], ${phpVar(args.slice(2))}); // Provide stdin, stdout, stderr streams outside of // the CLI SAPI. @@ -379,10 +401,10 @@ try { }`, env: options.env, }); - } else if (args[1].includes('wp-cli.phar')) { + } else if (args[1] === 'wp-cli.phar') { result = await childPHP.run({ throwOnError: true, - code: `${cliBootstrapScript} require( "/wordpress/wp-cli.phar" )`, + code: `${cliBootstrapScript} require( "/wordpress/wp-cli.phar" );`, env: { ...options.env, // Set SHELL_PIPE to 0 to ensure WP-CLI formats @@ -395,6 +417,7 @@ try { result = await childPHP.run({ throwOnError: true, scriptPath: args[1], + env: options.env, }); } processApi.stdout(result.bytes); diff --git a/packages/playground/website/demos/blueprints.phar b/packages/playground/website/demos/blueprints.phar new file mode 100755 index 0000000000..7335950d12 Binary files /dev/null and b/packages/playground/website/demos/blueprints.phar differ diff --git a/packages/playground/website/demos/php-blueprints.html b/packages/playground/website/demos/php-blueprints.html new file mode 100644 index 0000000000..9724b568e1 --- /dev/null +++ b/packages/playground/website/demos/php-blueprints.html @@ -0,0 +1,30 @@ + + + Blueprints PHP library + + + + + +

Blueprints PHP library

+

+ Uses the + PHP implementation of Blueprints + to set up a WordPress site. Open the devtools for full output. +

+ + + +

PHP Blueprint execution result output – it may take a while to load

+

+	
+
diff --git a/packages/playground/website/demos/php-blueprints.ts b/packages/playground/website/demos/php-blueprints.ts
new file mode 100644
index 0000000000..04aa3d0a41
--- /dev/null
+++ b/packages/playground/website/demos/php-blueprints.ts
@@ -0,0 +1,125 @@
+import { startPlaygroundWeb } from '@wp-playground/client';
+import { getRemoteUrl } from '../src/lib/config';
+import { joinPaths } from '@php-wasm/util';
+export {};
+
+const iframe = document.querySelector('iframe')!;
+const playground = await startPlaygroundWeb({
+	iframe,
+	remoteUrl: getRemoteUrl().toString(),
+	// Blueprint v1, implemented in TypeScript:
+	blueprint: {
+		preferredVersions: {
+			wp: 'latest',
+			// Required for the PHP library to run:
+			php: '8.2',
+		},
+		features: {
+			networking: true,
+		},
+		// landingPage: '/wp-content/index.php',
+		landingPage: '/',
+		// Required for the PHP library to run:
+		phpExtensionBundles: ['kitchen-sink'],
+	},
+});
+
+const response = await fetch('./blueprints.phar');
+const phar = new Uint8Array(await response.arrayBuffer());
+await playground.writeFile(
+	joinPaths(await playground.documentRoot, 'blueprints.phar'),
+	phar
+);
+const outputDiv = document.getElementById('output')!;
+
+try {
+	const wpCliRequest = fetch(
+		'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar'
+	);
+	const wpCliResponse = await wpCliRequest;
+	const wpCli = await wpCliResponse.arrayBuffer();
+	await playground.writeFile('/wordpress/wp-cli.phar', new Uint8Array(wpCli));
+
+	// Blueprint v2, implemented in PHP. The PHP builder is not required. It only
+	// produces a JSON document that is then used to run the Blueprint.
+	const result = await playground.run({
+		code: ` "check-requirements": false,
+		 * Then requiring it breaks http and https requests:
+		 *
+		 * > echo file_get_contents('http://localhost:5400/website-server/');
+		 * > Warning:  PHP Request Startup: Failed to open stream: Operation timed out in php-wasm run script on line 13
+ * + * The check is therefore disabled for now. + */ + require '/wordpress/blueprints.phar'; + + $blueprint = BlueprintBuilder::create() + // This isn't a WordPress zip file since wordpress.org + // doesn't expose the right CORS headers. It is a HTTPS-hosted + // zip file nonetheless, and we can use it for testing. + // Uncomment this as needed + // ->setWordPressVersion( 'https://downloads.wordpress.org/plugin/hello-dolly.1.7.3.zip' ) + + ->withFile( 'wordpress.txt', (new UrlResource())->setUrl('https://downloads.wordpress.org/plugin/hello-dolly.zip') ) + ->withSiteOptions( [ + 'blogname' => 'My Playground Blog', + ] ) + ->withWpConfigConstants( [ + 'WP_DEBUG' => true, + 'WP_DEBUG_LOG' => true, + 'WP_DEBUG_DISPLAY' => true, + 'WP_CACHE' => true, + ] ) + ->withPlugins( [ + 'https://downloads.wordpress.org/plugin/hello-dolly.zip', + // When the regular UrlDataSource is used, the second + // downloaded zip file always errors with: + // > Failed to open stream: Operation timed out + 'https://downloads.wordpress.org/plugin/classic-editor.zip', + 'https://downloads.wordpress.org/plugin/gutenberg.17.7.0.zip', + ] ) + ->withTheme( 'https://downloads.wordpress.org/theme/pendant.zip' ) + ->withContent( 'https://raw.githubusercontent.com/WordPress/theme-test-data/master/themeunittestdata.wordpress.xml' ) + ->andRunSQL( <<<'SQL' + CREATE TABLE tmp_table ( id INT ); + INSERT INTO tmp_table VALUES (1); + INSERT INTO tmp_table VALUES (2); + SQL + ) + ->withFile( 'wordpress.txt', 'Data' ) + ->toBlueprint() + ; + + echo "Running the following Blueprint:\n"; + echo json_encode($blueprint, JSON_PRETTY_PRINT)."\n\n"; + $results = run_blueprint( $blueprint, '/wordpress' ); + echo "Blueprint execution finished!\n"; + echo "Contents of /wordpress/wp-content/plugins:"; + print_r(glob('/wordpress/wp-content/plugins/*')); + `, + throwOnError: true, + }); + + outputDiv.textContent = result.text; + console.log(result.text); +} catch (e) { + console.error(e); + outputDiv.textContent = e + ''; + throw e; +} + +console.log(await playground.listFiles('/wordpress/wp-content/plugins')); diff --git a/packages/playground/website/demos/wordpress.zip b/packages/playground/website/demos/wordpress.zip new file mode 100644 index 0000000000..54825cf516 Binary files /dev/null and b/packages/playground/website/demos/wordpress.zip differ diff --git a/packages/playground/website/tsconfig.app.json b/packages/playground/website/tsconfig.app.json index 9b9b776920..2edc05ecaf 100644 --- a/packages/playground/website/tsconfig.app.json +++ b/packages/playground/website/tsconfig.app.json @@ -29,6 +29,7 @@ "demos/peer.ts", "demos/terminal.ts", "demos/terminal-component.tsx", + "demos/php-blueprints.ts", "./cypress.config.ts" ] }