wordpress/wordpress-importer
Official WordPress plugin for importing content from a WXR export file. Migrates posts, pages, comments, custom fields, categories, tags, and media attachments, making it easy to move or merge content between WordPress sites.
Architecture fit — poor as a runtime dependency, viable as a migration reference. wordpress/wordpress-importer is a WordPress plugin, not a framework-agnostic PHP library. Its public surface is WP_Import extends WP_Importer, and it calls deep into WordPress core: wp_insert_post, wp_insert_attachment, wp_insert_term, wp_update_post_meta, WP_Query, wp_upload_bits, wp_generate_attachment_metadata, current_user_can, plus the wp_import_* action/filter set. It also registers itself through register_importer() on admin_init and expects wp-load.php, $wpdb, and a logged-in admin context. Dropping it into a Laravel service provider yields fatal errors immediately — there is no adapter layer, no DI container, no PSR-4 autoload contract designed for non-WP consumers.
What it is genuinely good at is parsing WXR (WordPress eXtended RSS) via streaming XMLReader: namespaced elements (wp:, content:, excerpt:), authors, terms, post meta (including serialized PHP), comments, menus, and attachment sideloading. That parser is the reusable asset. The write path is not.
wp import, or a bootstrapped wp-load.php in a queue worker container) and have Laravel read the resulting schema via a package like Corcel; or (b) treat the plugin as a specification/reference and reimplement the WXR → domain mapping natively in Laravel. Direct Composer require into a Laravel app is not a supported usage and should be rejected at design review._wp_attached_file heuristics, so re-runs duplicate content; attachment sideloading performs outbound HTTP with default timeouts (an SSRF and slow-loris surface if the WXR is user-supplied); XMLReader without hardened libxml flags is exposed to entity-expansion abuse; serialized PHP meta is trusted implicitly, which is a deserialization hazard; custom post types that aren't registered in the target runtime are silently skipped.Stack fit. Laravel 9–11 on PHP 8.1+ can host the importer only inside an isolated WordPress container; nothing about Eloquent, queues, or the service container helps here. Compatibility is therefore a function of the WordPress version pinned in that sidecar, not of your application.
Recommended path — normalize, don't couple. Do not add the plugin to the app's composer.json. Instead:
wp import against the WXR to produce a canonical WordPress database (or run a thin WP-CLI command that emits normalized JSON per entity).wp_posts → your content models, wp_terms/wp_term_taxonomy → taxonomy tables or JSON columns, wp_postmeta → typed columns or a meta JSONB field (never unserialize() blindly — use allowed_classes => false), wp_users → author records with a stable email/username mapping table, attachments → object storage keys.Alternative paths, ranked. (a) Keep WordPress headless and read it live via Corcel — lowest migration effort, highest long-term coupling. (b) Use WP-CLI in a one-off runbook and skip Laravel integration entirely — cheapest for small sites. (c) Fork the XMLReader parsing logic into an internal, non-GPL-clean-room WXR parser — most work, cleanest architecture, and the only option if GPL is disqualifying.
Sequencing. Week 1: spike on a representative export, measure memory/peak runtime per 1k posts and per 100 attachments. Week 2: entity mapping spec + external-ID ledger schema + idempotency contract. Weeks 3–4: transform/load pipeline with dry-run mode and a diff report. Week 5: attachment backfill in a separate, resumable job. Week 6: reconciliation (counts, checksums, spot-check rendered content, redirect map for old URLs) and cutover rehearsal on a production snapshot. Gate each stage on a reconciliation assertion rather than "it ran without errors."
Compatibility gotchas to budget for: shortcode and Gutenberg block markup surviving as raw HTML (decide: preserve verbatim vs. strip vs. convert), character-encoding mismatches in older exports, menu structures with no Laravel analogue, and guid-based dedupe that breaks when the same site was exported twice.
Maintenance. Upstream tracks WordPress core; release cadence follows WP releases rather than semantic versioning, and internal APIs (WP_Import methods, filter signatures) are not a stability contract. If you vendor the code, you own the fork. If you run the sidecar, you own a WordPress container's patch cycle — core CVEs included — for what is functionally a one-shot migration tool. That is a poor long-term trade for a Laravel team.
Support. Community-only: GitHub issues and wordpress.org forums. No SLA, no security advisory channel you can subscribe to in the way you would for a Composer dependency with a CVE feed. Escalation within your org means reading WP core source.
Scaling. Single-threaded, synchronous, memory-bound. Streaming XMLReader keeps parse memory flat, but attachment processing dominates: each sideload is a sequential HTTP fetch plus image resampling. Expect hours, not minutes, for media-heavy sites, and plan to shard by post type or date range across parallel workers writing to disjoint key ranges. Execution-time and memory limits must be raised explicitly in the worker config; the importer does not chunk.
Failure modes to design against. Mid-run fatal → partial corpus with no resume point (mitigate with the external-ID ledger and per-batch commits). Re-run → duplicated posts and duplicated media (mitigate with idempotency keys on GUID + post type + site). Unregistered CPTs → content silently dropped, discovered only at reconciliation. Serialized-meta corruption across PHP versions. Remote media timeouts → missing thumbnails with live references in content. Encoding faults → mojibake in titles and excerpts that surfaces post-launch in search results.
Ramp-up. A mid-level Laravel engineer needs roughly 1–2 weeks to become productive with WordPress internals (hooks, wp_insert_post semantics, GUID behavior, attachment metadata), plus the same again for WXR edge cases. Budget 3–6 engineer-weeks for a production-grade migration on a nontrivial corpus, with the importer contributing perhaps 20% of that.
TPM recommendation. Treat this package as a reference implementation and a throwaway migration tool, not a dependency. Greenlight it only for a scoped, internal, one-way migration where GPL is acceptable and a WordPress sidecar is tolerable; block it for any long-lived or distributed integration. If WXR ingestion is a recurring product requirement, fund a native Laravel parser instead — the opportunity score of 28.78 is consistent with what the architecture review says: this package has essentially no pull in a Laravel ecosystem, and forcing it creates a WordPress-shaped maintenance burden inside a non-WordPress codebase.
How can I help you explore Laravel packages today?