Draft
Conversation
bda9d53 to
90815ba
Compare
18d442f to
9c61190
Compare
79c3c39 to
373d90a
Compare
Introduce LegacyPHPVersions and AllPHPVersions types alongside the existing SupportedPHPVersions. PHP 5.6 is classified as a "legacy" version — available but not in the standard supported list. Update Blueprint types to accept legacy PHP versions and regenerate the Blueprint JSON schema.
Add PHP 5.6.40 compilation support for both asyncify and JSPI build modes. Key changes to the build pipeline: - Add OpenSSL 1.1 compatibility patch (1100 lines) since PHP 5.6 directly accesses opaque OpenSSL structs removed in later versions - Replace PHP 5.6's bundled SQLite 3.8.10.2 with SQLite 3.51.0 (the v2.2.22 SQLite driver requires >= 3.37.0) - Add EMULATE_FUNCTION_POINTER_CASTS for JSPI build to fix a sort crash in PHP 5.6's zend_qsort - Patch C source files for compatibility with older PHP internal APIs - Add node-builds/5-6 and web-builds/5-6 packages with pre-compiled WASM binaries - Add PHP 5.6 loader entries in node and web runtime packages
Add functions that transform PHP 7+ code to PHP 5.6 compatible code for the SQLite integration plugin: - stripPhp7TypeDeclarations: removes parameter and return type declarations, including from interface/abstract methods (`;` terminators). Handles `mixed`, `never`, and nullable types. - replaceNullCoalescing: transforms ?? operators to isset() ternaries, with special handling for class constants and method calls to avoid double evaluation. Includes a safety limit to prevent infinite loops. - replacePhp7ErrorClasses: replaces Throwable with Exception, removes PHP 8 attributes and declare(strict_types), transforms Closure::call(), replaces callable property invocations with call_user_func(), and handles isset() on class constant arrays. These are used both at runtime (when extracting the SQLite plugin ZIP) and offline (in the pre-patching script).
Add an offline patcher script that transforms the SQLite Database Integration v2.2.22 plugin into a PHP 5.6-compatible version: - Removes all PHP 7+ syntax using the transpiler functions - Renames 'throw' method (reserved word in PHP 5.6) - Adds polyfills: placeholder_escape(), reinitialize_sqlite(), init_charset(), get_caller(), log_query(), and wpdb properties - Guards WordPress function calls that don't exist in old versions - Makes schema parse failures non-fatal for old WP schemas - Skips SQLite's wp_install() override for WP < 3.0 The pre-patched ZIP is committed as a build artifact. Add version entries for v2.2.22 and v2.2.22-php56 in the SQLite driver module details. Regenerate with: node --experimental-strip-types scripts/patch-sqlite-for-php56.mjs
Make WordPress 1.2 through 4.9 bootable on PHP 5.6 with SQLite by
patching WP source files at boot time. All legacy-specific code lives
in dedicated modules (legacy-wp-fixes.ts, mysql-shims.ts) to keep
boot.ts and index.ts focused on the core boot flow.
WP source file patching (legacy-wp-fixes.ts):
- wp-settings.php: disable extension_loaded('mysql') check, suppress
E_DEPRECATED/E_STRICT, remove set_magic_quotes_runtime, guard
get_magic_quotes_gpc, fix =& new syntax, replace $HTTP_SERVER_VARS
- wp-db.php: guard $wpdb creation, patch mysql_connect to call
db_connect(), inject wpdb method polyfills (set_prefix, timer_start,
timer_stop, init_charset, bail, check_connection)
- install.php: fix relative paths, replace $HTTP_GET_VARS/$HTTP_POST_VARS,
combine WP 1.x multi-step installer into single request
- functions.php: fix $all_options stdClass, eval db.php for parser bug
- schema.php: add wp_get_db_schema() polyfill for WP < 3.3
- Create wp-load.php shim for WP < 2.0, version.php stub for WP < 1.5
- Pre-create WP 1.x tables via PDO, seed admin user and default content
- Strip doc comments and skip large includes for WP 2.8 (parser bug)
SQLite integration (index.ts, legacy-wp-fixes.ts):
- Patch SQLite plugin for PHP 5.6 (strip type declarations, null
coalescing, rename reserved words, replace __DIR__ and require_once)
- Write db.php with MySQL/MySQLi stubs and PHP polyfills
- Lazy $wpdb loader calls reinitialize_sqlite() for old WP
- Functional mysql_* stubs (mysql-shims.ts) delegate to $wpdb
Boot flow adjustments (boot.ts):
- Skip ensureWpConfig for legacy PHP, copy wp-config-sample.php instead
- Non-fatal install errors and database validation for legacy PHP
- Skip isWordPressInstalled() for legacy PHP (crashes WASM on old WP)
Also add JavaScript unzip fallback for PHP builds without ZipArchive.
Web worker (playground-worker-endpoint-blueprints-v1.ts): - Select SQLite driver version based on PHP and WP version: PHP 5.6 uses pre-patched v2.2.22-php56, WP < 6.x on modern PHP uses v2.1.16, modern WP uses trunk - Pass phpVersion to bootWordPress for legacy detection - Disable WP_DEBUG for legacy PHP CLI (blueprints-v1-handler.ts, download.ts, worker-thread-v1.ts): - Add getPrebuiltWordPressPath() to use bundled WP ZIPs - Pass SQLite version parameter to fetchSqliteIntegration() - Pass phpVersion through to boot process Mu-plugins (0-playground.php, wp_http_fetch.php, wp_http_dummy.php): - Guard wp_doing_ajax() and wp_doing_cron() with function_exists() - Guard get_current_user_id() for WP < 3.0 compatibility - Guard Requests class and Requests_Transport interface checks - Replace [] syntax with array() for PHP 5.6 compatibility
Test that WordPress versions 1.2 through 4.9 boot on PHP 5.6 with SQLite and display "Hello world!". Starts the dev server, then runs each version through the browser via Playwright. WP 1.0.2 is excluded — the SQLite AST driver can't parse its enum() column types, causing WHERE clause failures.
When a non-minified WP version like "4.9" or "1.5" is passed via ?wp= parameter, construct a wordpress.org download URL and route it through the CORS proxy instead of silently falling back to the latest minified WP build. Versions >= 2.0 work as wordpress-<major>.<minor>.zip (wordpress.org redirects to latest patch). Versions < 2.0 need normalization since wordpress.org only hosts explicit patch versions (1.0.2, 1.2.2, 1.5.2). Also fix SQLite version selection to use the actual requested WP version instead of the minified fallback, ensuring old WP on modern PHP correctly gets the v2.1.16 SQLite driver.
Three issues prevented wp-admin from working on PHP 5.6: 1. template.php and media.php were replaced with empty stubs as a workaround for the WASM parser size limit. The real fix is to strip doc comments (which stripDocCommentsFromWpIncludes already does). Remove the stub replacement. Also preserve ?> tags during comment stripping — PHP's ?> closes PHP mode even inside // comments. 2. Old WordPress (< 3.7) uses relative paths in wp-admin scripts. Patch these to use dirname(__FILE__) for absolute resolution. Covers index.php, index-extra.php, and admin.php across all old WP versions. 3. Auth cookies don't persist correctly for old WordPress admin sessions. Fix by re-generating auth cookies on every admin request: mu-plugin for WP 2.8+, direct admin.php patch for WP < 2.8 (no mu-plugin support). Handles three auth eras: - WP 2.5+: wp_generate_auth_cookie + $_COOKIE population - WP 2.0-2.4: USER_COOKIE/PASS_COOKIE with md5(md5(password)) - WP 1.5: COOKIEHASH-based hardcoded cookie names Also skip prefetchUpdateChecks for WP < 5.0 (the functions it calls don't exist in old WP), skip 0-playground.php mu-plugin for WP < 3.0 (closures in hooks cause fatal errors), and set up wp_user_roles + usermeta when missing after install.
373d90a to
ad83845
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Run old WordPress versions (1.5 through 4.9 tested, any existing release should work) in the browser using PHP 5.6 WASM with SQLite as the database backend.
??,Throwable, attributes,Closure::call()) to PHP 5.6, used to patch the SQLite Database Integration plugin. Guards nullsafe operators (?->) from corruption.reinitialize_sqlite(),placeholder_escape(), etc.) and guards for old WP functionswp-load.phpshim for WP < 2.0,extension_loaded('mysql')bypass, lazy$wpdbloader withreinitialize_sqlite(), MySQL/MySQLi function stubs, WP 1.5 seed data insertion (SQLite NOT NULL constraint fix)?wp=4.9resolves to a wordpress.org download automatically (versions < 2.0 are normalized: 1.0→1.0.2, 1.2→1.2.2, 1.5→1.5.2)Tested with WP 1.2 through 4.9 (31 versions).
Test plan
Automated (Playwright)
Requires Chrome/Chromium with JSPI support (Chrome 131+).
Manual (browser)
npm run devUnit tests
npx nx test playground-wordpress --testFile=legacy-php-compat.spec.tsRuns 28 tests covering the PHP 5.6 transpiler functions (
stripPhp7TypeDeclarations,replaceNullCoalescing,replacePhp7ErrorClasses).Known issues
enum()column typespreg_replace /e modifier(WP 1.5 code predates PHP 5.4/5.5)prefetchUpdateChecksfails on all legacy WP versions (non-fatal)assertValidDatabaseConnectionalways fails for legacy PHP (treated as non-fatal)vite.config.tsneeds updating to copy version-specific ZIPsv2.1.16SQLite selection for WP < 6.x on modern PHP