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
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.zipReadme
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-contentfiles in the layout WordPress Playground imports. - LocalWP or self-hosted WordPress — the database is written as a MySQL
dump (
database.sql) alongside yourwp-contentfiles, 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
- Install and activate the plugin on the source site.
- Go to Tools → Export to Playground.
- Choose a Destination — WordPress Playground, or LocalWP / self-hosted WordPress.
- 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).
- Import the archive:
- Playground: site-manager import, or an
importWordPressFilesblueprint step. - LocalWP: drag the
.ziponto LocalWP, or use "Import site".
- Playground: site-manager import, or an
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,MyISAMtables are emitted asInnoDBso 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 livehome_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_prefixis not yet remapped; sites using the defaultwp_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-integrationtranslator 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'simportWordPressFiles. The CLI mounts the SQLite directory as a separate filesystem, so its cross-filesystem copy of.ht.sqlitefails — 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_passoption (cleartext Post-by-email password); - any option names added via the
wp2p_excluded_option_namesfilter.
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 falseor the
wp2p_use_sqlite_driverfilter.
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
| Tag | Published | Asset | Downloads |
|---|---|---|---|
| v0.3.1 | Aug 19, 2026 | wordpress-to-playground.zip | 0 |