WP Manifestindependent plugin directory
manifest / updates / wordpress-to-playground

WordPress to Playground

Export any WordPress site as a .zip that WordPress Playground can import, converting the MySQL database to SQLite.

by Finlay Nathan · github.com/finlayjn/wordpress-to-playground · website

0stars
0forks

Install

The author publishes release zips, so WP-CLI can install straight from GitHub:

wp plugin install https://github.com/finlayjn/wordpress-to-playground/releases/download/v0.3.1/wordpress-to-playground.zip

Readme

WordPress to Playground

A WordPress plugin that exports a running site as a .zip archive. Pick a destination on the export screen:

  • WordPress Playground — the database is converted to SQLite and bundled with your wp-content files in the layout WordPress Playground imports.
  • LocalWP or self-hosted WordPress — the database is written as a MySQL dump (database.sql) alongside your wp-content files, ready for LocalWP's "import site" or any MySQL host.

Both archives include the same files; only the database format differs, so you can move a site either direction. On most servers the round-trip keeps your database intact — a site can go WordPress → Playground → WordPress without losing table types, AUTO_INCREMENT, or indexes — see Two-way, lossless conversion.

What it produces

Playground destination — matches Playground's formatVersion: 2 archive:

playground-export.json     { "formatVersion": 2, "siteUrl": "<your site url>" }
wp-content/
  database/.ht.sqlite      (converted from MySQL)
  mu-plugins/wp2p-fix-urls.php (one-time boot-time URL fixer)
  themes/ plugins/ uploads/ ...

LocalWP / self-hosted destination — a MySQL dump beside your files, which LocalWP restores on import:

database.sql               (MySQL dump: SHOW CREATE TABLE + batched INSERTs)
wp-content/
  themes/ plugins/ uploads/ ...

Playground owns and manages wp-config.php (it wires up the SQLite runtime), so the archive intentionally does not ship one; importWordPressFiles fills in any required constants on import.

On import, Playground supplies its own runtime (the sqlite-database-integration mu-plugin, 0-playground.php, and a managed db.php) and discards any copies found in the archive, so this plugin only ships your data.

Requirements

  • PHP 7.2+
  • ext-zip (ZipArchive)
  • pdo_sqlite

Usage

  1. Install and activate the plugin on the source site.
  2. Go to Tools → Export to Playground.
  3. Choose a Destination — WordPress Playground, or LocalWP / self-hosted WordPress.
  4. Click Download export .zip. A progress bar shows while the archive is built; the screen then confirms the outcome (including whether round-trip database integrity was kept).
  5. Import the archive:
    • Playground: site-manager import, or an importWordPressFiles blueprint step.
    • LocalWP: drag the .zip onto LocalWP, or use "Import site".

Two-way, lossless conversion

WordPress stores its database in MySQL; Playground stores it in SQLite. Moving between the two normally loses MySQL-specific details (exact column types, unsigned, AUTO_INCREMENT, secondary indexes). This plugin avoids that by reusing the official sqlite-database-integration translator — the same engine Playground runs — so a WordPress → Playground → WordPress round-trip preserves the schema exactly.

  • On a MySQL site, the translator replays real MySQL DDL, so the SQLite it writes carries the exact schema. This is on by default whenever the bundled driver and a new-enough host SQLite (3.37+) are present; the export screen says whether round-trip database integrity is active.
  • On a SQLite site (e.g. a Playground export you are sending back to WordPress), the MySQL dump writer reconstructs MySQL DDL from the SQLite schema. Because SQLite normalizes every table to utf8mb4, MyISAM tables are emitted as InnoDB so wide multi-column indexes still fit — the archive imports cleanly on modern MySQL.

If the translator can't run for a given site, the export automatically falls back to a dependency-free generic converter: all your content still exports, but custom plugin table structures may be approximate. The export screen tells you when that happens.

How it works

Stage Class
Capability / integrity checks WP2P_Environment
Admin screen + download WP2P_Admin_Page
Pipeline coordination WP2P_Exporter
MySQL → SQLite (exact schema, default when available) WP2P_Driver_Database_Converter
MySQL → SQLite (generic fallback) WP2P_Database_Converter
SQLite/MySQL → MySQL dump (LocalWP target) WP2P_Mysql_Dump_Writer
Shared conversion base WP2P_Abstract_Database_Converter
Serialized-safe URL rewrite WP2P_Url_Rewriter
Boot-time URL fix (in export) templates/wp2p-fix-urls.php
wp-content collection WP2P_File_Collector
Archive packaging WP2P_Zip_Writer

Known limitations

  • URLs: absolute URLs to the source origin are rewritten (serialized-safe) to a scoped Playground sentinel URL, and the export manifest records that scope so Playground re-scopes every stored URL to the destination on import. For custom or scope-less Playground deployments (where Playground does not re-scope), the archive bundles a boot-time mu-plugin (wp-content/mu-plugins/wp2p-fix-urls.php) that rewrites the sentinel to the live home_url() on first load. It runs once and then no-ops.
  • Database fidelity: with the bundled translator (default when the host SQLite is new enough) exact MySQL schema is preserved. On hosts where it can't run, the generic converter is used and custom plugin tables with MySQL-specific SQL may translate approximately; the export screen flags this.
  • Multisite is not fully supported.
  • Table prefix: a non-default $table_prefix is not yet remapped; sites using the default wp_ prefix (the vast majority) import cleanly.
  • Large sites: very large uploads/databases may hit host memory or time limits; chunked streaming is on the roadmap.

Roadmap

  • [x] Official sqlite-database-integration translator strategy for exact schema fidelity (default when available).
  • [x] LocalWP / self-hosted MySQL-dump destination for two-way conversion.
  • [x] Serialized-data-safe URL search-replace at export time.
  • [x] Export status feedback and clear reporting when round-trip integrity can't be kept.
  • [ ] Background/chunked export for large sites.
  • [ ] WXR fallback path for hosts without pdo_sqlite.
  • [x] Verification harness (unit round-trip + real-MySQL end-to-end).

Testing

Unit-style tests (no WordPress or MySQL required):

tests/run.sh

These lint every PHP file and exercise the URL rewriter, the bundled boot-time fixer, and a full converter round-trip (driving the real converter with a fake $wpdb and asserting the produced .ht.sqlite).

End-to-end verification against a real MySQL WordPress site (requires Docker):

bin/verify-playground.sh

This boots WordPress on MySQL, seeds content, runs the export, asserts the archive layout, manifest, and converted SQLite (rows intact, URLs rewritten), and finally loads the converted database under the exact sqlite-database-integration driver WordPress Playground runs to confirm runtime compatibility.

Note: the boot check uses Playground's SQLite driver directly rather than playground-cli's importWordPressFiles. The CLI mounts the SQLite directory as a separate filesystem, so its cross-filesystem copy of .ht.sqlite fails — a CLI harness limitation. The browser Playground uses a unified filesystem and imports the archive normally.

Keeping secrets out of the archive

The exporter omits wp-config.php (so salts and DB credentials never leave the source site) and, when building the database, drops:

  • all transients (_transient_* / _site_transient_*);
  • the core mailserver_pass option (cleartext Post-by-email password);
  • any option names added via the wp2p_excluded_option_names filter.

Plugins can exclude their own secret-bearing options without this package referencing them:

add_filter( 'wp2p_excluded_option_names', function ( $names ) {
    $names[] = 'my_plugin_settings';
    return $names;
} );

Before committing an export (a private repo is recommended), audit it for likely secrets:

bin/audit-export.sh your-export.zip

This flags suspicious files (.env, *.pem, a stray wp-config.php, etc.), private-key/cloud-credential blocks in any file, and compound secret names (api_key, client_secret, access_token, …) in the database. Note that a database export still contains user password hashes and PII by nature.

Round-trip database integrity (default)

Exact-schema conversion reuses the official sqlite-database-integration translator — the same engine Playground runs — so the on-disk SQLite format matches precisely. It is used automatically whenever the driver is vendored and the host SQLite is new enough (3.37+); the export screen reports whether it is active.

  • The driver is fetched (pinned to Playground's version) with:
    bin/fetch-sqlite-driver.sh
  • To force it on or off regardless of auto-detection, use a constant in wp-config.php:
    define( 'WP2P_USE_SQLITE_DRIVER', true ); // or false

    or the wp2p_use_sqlite_driver filter.

When the translator can't run, the export falls back to the generic converter and the export screen says so.

AI Disclaimer

This plugin was developed with the assistance of Claude Opus 4.8 via GitHub Copilot. While all code has been reviewed and tested, users should carefully review code for their use case and export results for completeness.

License

GPL-2.0-or-later

Read the full README on GitHub →

Releases

TagPublishedAssetDownloads
v0.3.1 Aug 19, 2026 wordpress-to-playground.zip 0