Skip to content
Draft
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
Binary file modified apps/cli/lib/pull/reprint.phar
Binary file not shown.
6 changes: 6 additions & 0 deletions apps/cli/lib/pull/wasm-extensions/wp_mysql_parser/SHA256SUMS
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
f3a4cf8ab6e755287a9b8a7fdefc4095712a01454039d2db86984df5425311e2 wp_mysql_parser-php8.0-jspi.so
60f694799adee3721fbbb7e6f41159dc68d17686f2c5df8f6e55a484dc90d207 wp_mysql_parser-php8.1-jspi.so
8612e0d9885adbb5684ab3cae160780cca31280ce2ae4b5ad0b4bdad74347e82 wp_mysql_parser-php8.2-jspi.so
13b27c54c2f1deac0d20c00ef922887971b56955cf450d18e8a0fa10ca68485c wp_mysql_parser-php8.3-jspi.so
c13e56c4ea128df4510e3f7303660fc1d8715457229162d1d8f7af63a4f63cbe wp_mysql_parser-php8.4-jspi.so
3193ea32c51a09a7e10ef5c66fc3c3ffd8fafb1e3c6d5b6a4dc170b21ea1b159 wp_mysql_parser-php8.5-jspi.so
31 changes: 31 additions & 0 deletions apps/cli/lib/pull/wasm-extensions/wp_mysql_parser/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "wp_mysql_parser",
"mode": "php-extension",
"artifacts": [
{
"phpVersion": "8.5",
"sourcePath": "wp_mysql_parser-php8.5-jspi.so"
},
{
"phpVersion": "8.4",
"sourcePath": "wp_mysql_parser-php8.4-jspi.so"
},
{
"phpVersion": "8.3",
"sourcePath": "wp_mysql_parser-php8.3-jspi.so"
},
{
"phpVersion": "8.2",
"sourcePath": "wp_mysql_parser-php8.2-jspi.so"
},
{
"phpVersion": "8.1",
"sourcePath": "wp_mysql_parser-php8.1-jspi.so"
},
{
"phpVersion": "8.0",
"sourcePath": "wp_mysql_parser-php8.0-jspi.so"
}
],
"version": "b31fc53ea599d1a2211b75f4a3486b39e63ce01f"
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
783d5cf85b490e773c996591c6f86e54cd1b98924026b00c0eb99b86f2a6630f wp_native_apis-php8.4-jspi.so
11 changes: 11 additions & 0 deletions apps/cli/lib/pull/wasm-extensions/wp_native_apis/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "wp_native_apis",
"version": "cb3aa252c65f9c8a592ef45838948c9d11de79df",
"artifacts": [
{
"phpVersion": "8.4",
"sourcePath": "wp_native_apis-php8.4-jspi.so"
}
],
"mode": "php-extension"
}
Binary file not shown.
86 changes: 81 additions & 5 deletions apps/cli/reprint-child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@
* the parent's event loop stays responsive for Ctrl+C handling and
* progress reporting. The parent communicates via IPC messages.
*/
import { existsSync } from 'node:fs';
import path from 'node:path';
import { rootCertificates } from 'node:tls';
import { createNodeFsMountHandler, loadNodeRuntime } from '@php-wasm/node';
import { createNodeFsMountHandler, loadNodeRuntime, withNetworking } from '@php-wasm/node';
import { PHP, ProcessIdAllocator, setPhpIniEntries } from '@php-wasm/universal';
import { createSpawnHandler } from '@php-wasm/util';
import { LatestSupportedPHPVersion } from '@studio/common/types/php-versions';
import type { SupportedPHPVersion } from '@studio/common/types/php-versions';

const processIdAllocator = new ProcessIdAllocator();
const REPRINT_PHP_VERSION = '8.4' satisfies SupportedPHPVersion;
const WASM_EXTENSIONS_DIR = path.join( import.meta.dirname, 'wasm-extensions' );
const REQUIRED_EXTENSION_MANIFESTS = [
path.join( WASM_EXTENSIONS_DIR, 'wp_native_apis', 'manifest.json' ),
path.join( WASM_EXTENSIONS_DIR, 'wp_mysql_parser', 'manifest.json' ),
] as const;

// Proxy configuration env vars must reach reprint.phar so it can route
// outbound HTTP through the user's proxy (e.g. tsocks / corporate
Expand Down Expand Up @@ -93,6 +101,71 @@ async function mountDirectory( php: PHP, mount: ReprintMount ) {
await php.mount( mount.vfsPath, createNodeFsMountHandler( mount.hostPath ) );
}

function assertExtensionManifestsExist() {
const missing = REQUIRED_EXTENSION_MANIFESTS.filter(
( manifestPath ) => ! existsSync( manifestPath )
);
if ( missing.length > 0 ) {
throw new Error(
`Missing bundled PHP WASM extension manifest(s): ${ missing.join(
', '
) }. Rebuild the Studio CLI so pull-reprint can load its native extensions.`
);
}
}

function createRequiredExtensionOptions() {
assertExtensionManifestsExist();

return REQUIRED_EXTENSION_MANIFESTS.map( ( manifestUrl ) => ( {
source: {
format: 'manifest' as const,
manifestUrl,
},
} ) );
}

function writeNativeExtensionAssertion( php: PHP ) {
php.writeFile(
'/tmp/assert-native-reprint-extensions.php',
`<?php
$missing = array();
foreach ( array( 'wp_native_apis', 'wp_mysql_parser' ) as $extension ) {
if ( ! extension_loaded( $extension ) ) {
$missing[] = "extension_loaded('{$extension}')";
}
}

foreach (
array(
'WP_HTML_Native_Tag_Processor',
'WP_HTML_Native_Processor',
'WordPress\\\\XML\\\\NativeXMLProcessor',
'WordPress\\\\DataLiberation\\\\URL\\\\NativeURLInTextProcessor',
'WP_MySQL_Native_Grammar',
'WP_MySQL_Native_Lexer',
'WP_MySQL_Native_Parser',
'WP_MySQL_Native_Token_Stream',
) as $class_name
) {
if ( ! class_exists( $class_name, false ) ) {
$missing[] = "class_exists('{$class_name}')";
}
}

if ( $missing ) {
fwrite(
STDERR,
"pull-reprint expected native PHP WASM extensions, but these checks failed: " .
implode( ', ', $missing ) .
". This would silently fall back to much slower PHP parsing, so Studio is aborting.\\n"
);
exit( 1 );
}
`
);
}

/**
* Pipes a PHP stream to the parent process via IPC, calling `onChunk`
* for each decoded text fragment so the caller can track whatever
Expand Down Expand Up @@ -140,11 +213,12 @@ async function pipePhpStream(
async function runReprint( msg: RunMessage ) {
const { pharPath, stateDir, fsRoot, tmpDir, args, mounts = [] } = msg;

const id = await loadNodeRuntime( LatestSupportedPHPVersion, {
const id = await loadNodeRuntime( REPRINT_PHP_VERSION, {
followSymlinks: true,
emscriptenOptions: {
extensions: createRequiredExtensionOptions(),
emscriptenOptions: await withNetworking( {
processId: processIdAllocator.claim(),
},
} ),
} );
const php = new PHP( id );

Expand All @@ -162,8 +236,10 @@ async function runReprint( msg: RunMessage ) {
await php.mount( '/tmp/reprint.phar', createNodeFsMountHandler( pharPath ) );

php.writeFile( '/tmp/ca-bundle.crt', rootCertificates.join( '\n' ) );
writeNativeExtensionAssertion( php );
await setPhpIniEntries( php, {
'openssl.cafile': '/tmp/ca-bundle.crt',
auto_prepend_file: '/tmp/assert-native-reprint-extensions.php',
allow_url_fopen: 1,
memory_limit: '512M',
error_reporting: String( 32767 & ~8192 ),
Expand Down
6 changes: 6 additions & 0 deletions apps/cli/vite.config.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ if ( ! minimumNodeVersion ) {
const bundledWpFilesPath = resolve( __dirname, '..', '..', 'wp-files' );
const phpSourceCodePath = resolve( __dirname, 'php' );
const bundledReprintPhar = resolve( __dirname, 'lib/pull/reprint.phar' );
const bundledReprintWasmExtensionsPath = resolve( __dirname, 'lib/pull/wasm-extensions' );

export const baseConfig = defineConfig( {
plugins: [
Expand All @@ -51,6 +52,11 @@ export const baseConfig = defineConfig( {
if ( existsSync( bundledReprintPhar ) ) {
copyFileSync( bundledReprintPhar, resolve( outDir, 'reprint.phar' ) );
}
if ( existsSync( bundledReprintWasmExtensionsPath ) ) {
cpSync( bundledReprintWasmExtensionsPath, resolve( outDir, 'wasm-extensions' ), {
recursive: true,
} );
}
},
},
],
Expand Down