Skip to content

Add WordPress 1.0 support via PHP 5.6 and MySQL proxy#3394

Draft
wp-playground-bot wants to merge 11 commits intoWordPress:trunkfrom
wp-playground-bot:wordpress-1.0-support
Draft

Add WordPress 1.0 support via PHP 5.6 and MySQL proxy#3394
wp-playground-bot wants to merge 11 commits intoWordPress:trunkfrom
wp-playground-bot:wordpress-1.0-support

Conversation

@wp-playground-bot
Copy link
Copy Markdown

Summary

WordPress 1.0 (released January 2004) can now run in Playground. It boots
through the MySQL binary protocol proxy from #3393 — WordPress 1.0 calls
mysql_connect() thinking it's talking to a real MySQL server, while a
PHP 8.x instance translates the queries to SQLite behind the scenes.

The boot process detects legacy WordPress by checking for the absence
of wp-load.php (introduced in WP 2.6) and takes a separate code path
that handles three key differences from modern WordPress:

  1. PHP compatibility shims for old superglobals ($HTTP_GET_VARS etc.)
    that WordPress 1.x relies on but were removed in PHP 5.4
  2. A multi-step installer (steps 1, 2, 3) instead of the modern
    single-POST install endpoint
  3. No mu-plugins or modern hooks, so the platform-level setup is skipped

WordPress 1.0.2 is fetched directly from wordpress.org as a remote
module (272KB — no minification needed for such a small release).

Depends on #3393 (MySQL binary protocol proxy) and #3284 (legacy PHP versions).

Test plan

  • WordPress 1.0 boot integration test passes (wordpress-1.0-boot.spec.ts)
  • Existing tests remain green
  • WordPress 1.0 homepage renders with "WordPress" in output

Enable compiling PHP 5.6 to WebAssembly so WordPress Playground can run
WordPress 1.x through 4.x, which require PHP 5.2+. PHP 5.6 is backward
compatible with PHP 5.2 code, so a single legacy version covers all older
WordPress releases.

Changes:
- Dockerfile: Add PHP 5.x version guards (fiber-asm, imagick, chunk-alloc
  patches, ASM arithmetic, cli_server)
- C sources: Add PHP 5.x compatibility for proc_open, dns_polyfill,
  post_message_to_js, and wasm_memory_storage extensions
- Package scaffolding: node-builds/5-6 and web-builds/5-6 with full NX
  project configuration
- Loader modules: Add case '5.6' to node and web getPHPLoaderModule()
- Version registry: Add PHP 5.6 to supported-php-versions.mjs and
  LegacyPHPVersions in supported-php-versions.ts (separate from
  SupportedPHPVersions to avoid test regressions)
- Test suite: Dedicated legacy-php-versions.spec.ts with 12 tests
  covering execution, file I/O, networking, proc_open, SQLite, JSON,
  sessions, mbstring, error handling, memory, and filesystem ops
- tsconfig.base.json: Path aliases for new packages

Note: WASM binaries must be compiled in a Docker-enabled environment:
  node packages/php-wasm/compile/build.js --PLATFORM=node --PHP_VERSION=5.6 \
    --WITH_IMAGICK=no --WITH_OPCACHE=no

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PHP 5.6 is now functional in Playground with both web JSPI and node
asyncify variants. This required fixing several compatibility issues
between PHP 5.6's C codebase and modern Emscripten/clang:

Dockerfile: SQLite duplicate symbol fix for PHP 5.x bundled sqlite3,
readdir_r compatibility, x86_64 inline assembly guards, PCRE JIT
disable, curl-config shim for PHP 5.x detection, and compiler warning
suppression flags (-Wno-implicit-int, -Wno-implicit-function-declaration,
-Wno-incompatible-function-pointer-types, etc).

php_wasm.c: PHP 5.x API compat for size_t vs int return types in
SAPI callbacks, and dup parameter in add_next_index_stringl /
RETVAL_STRINGL macros.

The test suite's isVersionBuilt() now checks for actual .wasm binary
files instead of just directory existence, preventing crashes when
placeholder stubs are present but no real binaries exist.

Built with limited extensions (no OpenSSL, cURL, GD, ImageMagick,
MySQL, mbregex) for this initial release. Verified: PHP boots,
initializes SAPI, serves HTTP 200 with X-Powered-By: PHP/5.6.40,
and executes PHP code producing correct output.
Resolved three merge conflicts:
- packages/php-wasm/supported-php-versions.mjs: Kept PR's newer version numbers and PHP 5.6 entry
- packages/php-wasm/node/project.json: Kept both test-legacy-php (PR) and test-file-locking targets (trunk)
- packages/php-wasm/node/src/lib/load-runtime.ts: Merged LegacyPHPVersion import with trunk's new imports, kept modernVersion cast with trunk's truthy check
WordPress Playground uses SQLite under the hood, but PHP 5.6 can't run the
sqlite-database-integration plugin (it requires PHP 7.2+). This adds a MySQL
wire protocol proxy that lets PHP 5.6 connect to what it thinks is a real MySQL
server. Behind the scenes, a second PHP 8.x instance translates the MySQL
queries to SQLite using the sqlite-database-integration plugin.

The proxy implements the MySQL binary protocol: handshake, authentication,
COM_QUERY, COM_PING, COM_QUIT, result sets, OK/ERR/EOF packets. The boot
process accepts a new `mysqlProxyPort` option that configures WordPress with
standard MySQL database constants instead of the db.php drop-in approach.
@wp-playground-bot wp-playground-bot requested review from a team, Copilot and zaerl March 14, 2026 23:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds the ability to boot very old WordPress (1.0.x) in Playground by introducing PHP 5.6 runtimes and a MySQL wire-protocol proxy that translates queries to SQLite via a separate PHP 8.x instance.

Changes:

  • Add PHP 5.6 “legacy” loader support for Node and Web builds (new packages + version typing).
  • Implement a MySQL binary-protocol proxy (and an SQLite-backed variant) to support WP 1.0’s mysql_* calls.
  • Add WordPress 1.0.2 module metadata and integration tests for MySQL-proxy-based boot.

Reviewed changes

Copilot reviewed 44 out of 49 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
tsconfig.base.json Adds TS path aliases for new 5.6 build packages.
packages/playground/wordpress/src/test/wordpress-1.0-boot.spec.ts New integration test for WP 1.0 boot via proxy + PHP 5.6.
packages/playground/wordpress/src/test/mysql-proxy-boot.spec.ts New integration test for MySQL proxy boot path using PHP 5.6.
packages/playground/wordpress/src/boot.ts Adds legacy WP boot path + MySQL proxy configuration option.
packages/playground/wordpress-builds/src/wordpress/wp-versions.json Adds WP 1.0 mapping to 1.0.2.
packages/playground/wordpress-builds/src/wordpress/get-wordpress-module-details.ts Provides remote module details for WP 1.0.2 zip.
packages/php-wasm/web/src/lib/get-php-loader-module.ts Enables selecting PHP 5.6 loader on web.
packages/php-wasm/web-builds/5-6/tsconfig.lib.json Build TS config for web PHP 5.6 package.
packages/php-wasm/web-builds/5-6/tsconfig.json Project references for web PHP 5.6 package.
packages/php-wasm/web-builds/5-6/src/index.ts Loader entry that chooses JSPI vs asyncify.
packages/php-wasm/web-builds/5-6/project.json Nx project config for building/publishing web 5.6 package.
packages/php-wasm/web-builds/5-6/package.json Package manifest for @php-wasm/web-5-6.
packages/php-wasm/web-builds/5-6/build.js Bundle script for web 5.6 loader package.
packages/php-wasm/web-builds/5-6/asyncify/php_5_6.js Placeholder asyncify loader artifact for 5.6.
packages/php-wasm/web-builds/5-6/README.md Basic docs for @php-wasm/web-5-6.
packages/php-wasm/universal/src/lib/supported-php-versions.ts Introduces LegacyPHPVersions / LegacyPHPVersion.
packages/php-wasm/universal/src/lib/index.ts Re-exports legacy version types/lists.
packages/php-wasm/supported-php-versions.mjs Adds 5.6 metadata to supported versions list.
packages/php-wasm/node/src/test/mysql-proxy.spec.ts Adds unit/integration tests for MySQL proxy protocol.
packages/php-wasm/node/src/test/legacy-php-versions.spec.ts Adds a separate test suite for legacy PHP 5.6 runtime basics.
packages/php-wasm/node/src/lib/networking/sqlite-over-mysql-proxy.ts Implements SQLite-backed MySQL protocol proxy adapter.
packages/php-wasm/node/src/lib/networking/mysql-proxy.ts Implements MySQL wire protocol server + query delegation.
packages/php-wasm/node/src/lib/networking/mysql-protocol.ts Adds MySQL protocol packet encode/decode utilities.
packages/php-wasm/node/src/lib/load-runtime.ts Allows loading legacy PHP versions in Node runtime loader.
packages/php-wasm/node/src/lib/index.ts Exports new networking proxy APIs.
packages/php-wasm/node/src/lib/get-php-loader-module.ts Enables selecting PHP 5.6 loader on Node.
packages/php-wasm/node/project.json Registers new MySQL proxy test + adds legacy test target.
packages/php-wasm/node-builds/5-6/tsconfig.lib.json Build TS config for node PHP 5.6 package.
packages/php-wasm/node-builds/5-6/tsconfig.json Project references for node PHP 5.6 package.
packages/php-wasm/node-builds/5-6/src/index.ts Node loader entry that chooses JSPI vs asyncify.
packages/php-wasm/node-builds/5-6/project.json Nx project config for building/publishing node 5.6 package.
packages/php-wasm/node-builds/5-6/package.json Package manifest for @php-wasm/node-5-6.
packages/php-wasm/node-builds/5-6/jspi/php_5_6.js Placeholder JSPI loader artifact for 5.6.
packages/php-wasm/node-builds/5-6/build.js Bundle script for node 5.6 loader package.
packages/php-wasm/node-builds/5-6/README.md Basic docs for @php-wasm/node-5-6.
packages/php-wasm/compile/php/proc_open.h Adds PHP 5.x compatible struct/headers for proc_open stubs.
packages/php-wasm/compile/php/proc_open.c Adds PHP 5.x stub proc_open implementation + conditional includes.
packages/php-wasm/compile/php/php_wasm.c Fixes PHP 5.x API differences in string/write function signatures.
packages/php-wasm/compile/php/php5.6.patch Adds PHP 5.6 patch (copy() empty file crash workaround).
packages/php-wasm/compile/php/apply-mysqlnd-patch.sh Makes mysqlnd patch application non-fatal on missing/mismatched code.
packages/php-wasm/compile/php/Dockerfile Updates build steps for PHP 5.6 compatibility and conditional features.
packages/php-wasm/compile/php-wasm-memory-storage/wasm_memory_storage.c Disables custom memory storage for PHP 5.x via no-op hooks.
packages/php-wasm/compile/php-wasm-dns-polyfill/dns_polyfill.c Adds PHP 5.x compatible parameter parsing and TSRMLS usage.
packages/php-wasm/compile/php-post-message-to-js/post_message_to_js.c Fixes PHP 5.x parameter parsing + return handling for postMessage bridge.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +275 to +276
const usesMyqlProxy = !!options.mysqlProxyPort;
if (usesMyqlProxy) {
Comment on lines +279 to +281
* via standard MySQL functions (mysqli). No db.php drop-in
* is needed. This is used for PHP 5.6 where the modern
* sqlite-database-integration plugin can't run directly.
// If SQLite integration is preloaded via core, we're good
// If a MySQL proxy is configured (usesSqlite is true in that case),
// the database is handled through the proxy. Skip further checks.
if (php.isFile('/internal/shared/preload/0-sqlite.php')) {
Comment on lines +291 to 303
// Extensions are only available for modern PHP versions.
const modernVersion = phpVersion as SupportedPHPVersion;
if (options?.withXdebug) {
emscriptenOptions = await withXdebug(
phpVersion,
modernVersion,
emscriptenOptions,
typeof options.withXdebug === 'object' ? options.withXdebug : {}
);
}

if (options?.withIntl === true) {
emscriptenOptions = await withIntl(phpVersion, emscriptenOptions);
emscriptenOptions = await withIntl(modernVersion, emscriptenOptions);
}
*/
export async function getPHPLoaderModule(
version: SupportedPHPVersion | string = LatestSupportedPHPVersion
version: SupportedPHPVersion | LegacyPHPVersion = LatestSupportedPHPVersion
Comment on lines +84 to +95
return async (query: string): Promise<QueryResult | null> => {
if (!initialized) {
await initializeDriver(php, sqlitePluginPath, sqliteDatabasePath);
initialized = true;
}

// Write the query to a temp file to avoid escaping issues
// with embedded quotes, backslashes, etc.
php.writeFile('/tmp/mysql-proxy-query.sql', query);

const result = await php.run({
code: `<?php
Comment on lines +214 to +222
port: addr.port,
host: addr.address,
close: () =>
new Promise<void>((res) => {
server.close(() => res());
// Force close all existing connections
server.unref();
}),
});
Comment on lines +6 to +9
throw new Error(
'PHP 5.6 WASM binaries have not been compiled yet. ' +
'Run: node packages/php-wasm/compile/build.js --PLATFORM=web --PHP_VERSION=5.6'
);
Comment on lines +5 to +6
"declaration": true,
"types": ["node"]
Address review feedback for the MySQL binary protocol proxy:

- Serialize packet processing with a promise chain to prevent overlapping
  async handlers when TCP delivers data events in quick succession
- Track active sockets and destroy them on server.close() so the close
  promise resolves immediately instead of hanging
- Remove findFreePorts() TOCTOU race by passing port 0 directly to
  server.listen() and reading the assigned port from server.address()
- Use unique temp file names per query to prevent concurrent overwrites
- Escape PHP string literals to prevent path injection
- Fix typo: usesMyqlProxy → usesMysqlProxy
- Rewrite DB constants in wp-config.php via defineWpConfigConstants
  instead of php.defineConstant to avoid duplicate define() notices
- Strengthen test assertions to check HTTP 200 and specific page content
claude added 4 commits March 15, 2026 00:52
WordPress doesn't use the COM_FIELD_LIST command, and the constant
was exported but never imported anywhere. The proxy's default case
already handles unknown commands with a proper error response.
WordPress 1.0 (2004) can now boot in Playground through the MySQL binary
protocol proxy introduced in the previous PR. The boot process detects
legacy WordPress versions by the absence of wp-load.php (introduced in
WP 2.6) and takes a separate code path:

- Sets up PHP compatibility shims for old superglobals ($HTTP_GET_VARS,
  $HTTP_POST_VARS) that were removed in PHP 5.4 but WP 1.x needs
- Runs the multi-step 1.x installer (steps 1, 2, 3) instead of the
  modern single-POST installer
- Configures MySQL proxy connection so WP 1.0's mysql_connect() talks
  to our proxy, which translates queries to SQLite via PHP 8.x

WordPress 1.0.2 is registered as a remote-fetched version from
wordpress.org (272KB, no minification needed for such a small release).
…tallation

The legacy WordPress boot path had three issues from review feedback:

configureWordPressForMySQLProxy was called before ensureWpConfig, but it
needs wp-config.php to exist first. Reordered so ensureWpConfig runs first,
then the MySQL proxy constants are written into the existing config file.

WordPress 1.x was written for register_globals=On, which was removed in
PHP 5.4. Without emulation, query string and form variables aren't
available as globals, breaking core WordPress code paths. Added EGPCS
extraction loop to the compatibility preload shim.

The installer ran three HTTP requests with no verification — silent
failures would leave WordPress in a broken state. Each step now checks
for 5xx responses and throws with diagnostic output. A final homepage
request confirms the install actually worked.

Strengthened the test assertions from toBeLessThan(500) to toBe(200)
with an HTML structure check, and documented the network dependency
for the WordPress 1.0.2 download.
The comment incorrectly described the SQLite preload file check as
a MySQL proxy check. The preload file is created by
preloadSqliteIntegration or mounted via hooks — the MySQL proxy case
is handled separately via the usesSqlite flag.
@adamziel adamziel marked this pull request as draft March 16, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants