wp-cli/search-replace-command
WP-CLI command for safe search-and-replace across a WordPress database. Update URLs, domains, or any string in all tables with dry-run previews, regex, case-insensitive matching, and familiar WP-CLI syntax—ideal for migrations and environment changes.
Category mismatch — read this first. wp-cli/search-replace-command is not a Laravel/PHP package in any conventional sense. It is a WP-CLI command package that performs serialized-safe search-and-replace against a WordPress database. It cannot be composer require'd into a Laravel app, has no service provider, no facade, no PSR-compatible API surface, and no meaningful presence in the Laravel ecosystem. Any evaluation framed as "how do we integrate this into our Laravel stack" must start by reframing it as a migration and data-operations tool that sits adjacent to the Laravel application, not inside it.
Architecture fit. Near-zero for runtime; meaningful for one specific and common scenario: extracting or rehydrating WordPress content as part of a headless/decoupled migration into a Laravel application, or performing domain/URL rewrites across a legacy WordPress corpus before or during that migration. The package's actual asset is its recursive, serialized-data-aware replacement engine (it unserializes, walks, replaces, re-serializes), plus table/column targeting, case-insensitive defaults, --regex, --precise, --dry-run, and change reporting. That logic is genuinely non-trivial to reimplement correctly and is the reason to consider it at all.
Integration feasibility. High, in the only pattern that makes sense: invoke it as an external CLI process against a WordPress database, from a Laravel console command, queued job, or CI/deploy step. Low-to-nil as an embedded library. If the team's actual need is "search and replace inside Laravel-owned data," this package is the wrong tool — Laravel's own migration/seeder layer or a small custom walker is the correct answer.
Signal quality caveats. 61 stars and a composite score of ~20.5 understate real-world adoption: search-replace is a bundled WP-CLI command, so most usage never registers as a star on a satellite repo. Conversely, the repo field is "unknown" and the last-release date (2026-08-05) is anomalous — both are data-quality flags that must be resolved against the canonical wp-cli GitHub org before any procurement or dependency decision, purely for supply-chain hygiene.
Technical risk. Concentrated almost entirely in blast radius rather than code quality. This command rewrites production data in place, with no built-in rollback, no cross-table transaction, and no undo. Risks: serialized-PHP corruption when third-party plugins store serialized data outside standard columns; JSON-encoded payloads embedded in postmeta/options that this tool will happily mangle if the replacement string appears inside them; missed replacements in case-sensitive collations; and silent partial completion on timeout. The engine is mature; the failure modes are operator error and unmodeled data shapes.
Key questions for the team:
wp-cli org rather than a mirror?Stack fit. PHP-version compatible with modern Laravel runtimes (7.4/8.x), but it bootstraps a full WordPress install to run — it needs wp-config.php, $wpdb, and the WP table prefix. Treat it as a separate container or sidecar with its own PHP/WP runtime, not as a dependency of the application image.
Three viable patterns, in order of preference:
wpcli image (or a service in docker-compose) with the WordPress tree and the package installed. A Laravel console command shells out with --dry-run by default, streams the report to a log channel, and requires an explicit --confirm flag plus a feature-flag guard to execute for real. This keeps the destructive capability behind application-level authorization, audit logging, and scheduling.Migration path / sequencing. This package is a step within a migration, not a migration path itself.
--all-tables --dry-run --report-changed-only, capture the report, review table-by-table for surprises.wp_posts, wp_postmeta, wp_options) and use --skip-columns for known-hazardous fields, rather than defaulting to --all-tables.post_content checksums before/after.Compatibility notes to encode in the runbook. Default behavior is case-insensitive — use --precise when case matters and accept the performance cost. --regex is substantially slower and riskier; require review. --precise and --regex are mutually exclusive. Newer versions default to all tables, which is precisely the setting most likely to cause harm; the runbook should pin behavior explicitly per version rather than relying on defaults.
Maintenance. Low and favorable. Upstream is the WP-CLI organization, MIT licensed, with a long track record and a conservative release cadence. Because it ships as a bundled command, most users receive fixes through WP-CLI itself rather than tracking this repo directly. There is no meaningful dependency-management burden for a Laravel team — pin the WP-CLI and package versions in the sidecar image and rebuild deliberately.
Support. Community-only: GitHub issues and WP-CLI Slack. No vendor, no SLA, no commercial escalation path. For a Laravel team this is acceptable for a migration-window tool and unacceptable as a production dependency with uptime expectations. Document an internal owner and an escalation path to "we restore the snapshot and do it manually."
Scaling. Single-threaded and table-scanning. Runtime scales roughly with total row count and total bytes, not with the number of matches. Realistic pain points: large wp_postmeta and wp_options tables, and --precise/--regex modes that multiply runtime. Long-running single statements mean lock contention and replication lag — on a read-replica topology, plan for the primary and for replica catch-up before cutting the Laravel app over. Memory is generally bounded per-row, but a single oversized longtext row can still breach limits. Set an explicit --max-execution-time and chunk by table or ID range for very large corpora rather than attempting one heroic run.
Failure modes and mitigations:
| Failure | Impact | Mitigation |
|---|---|---|
| Partial completion on timeout | Silent partial replacement; no rollback | Chunk by table/ID range; verify with zero-diff dry run |
| Serialized data corruption in plugin columns | PHP fatals, blank admin screens | Audit serialized columns first; --skip-columns; sample-deserialize after |
| JSON/encoded payloads mangled | Broken block markup, page-builder content | Skip post_content where page builders are in use; migrate those separately |
| Case-insensitive over-match | Unintended replacements in unrelated strings | --precise where correctness matters; dry-run review of changed counts |
| Concurrent application writes | Inconsistent state | Maintenance window, writes frozen, app in read-only mode |
| Unpinned version drift in sidecar | Behavior change between runs | Digest-pin the image; rebuild on a schedule |
Ramp-up. Effectively zero for anyone with WordPress operational experience; this is a well-known, well-documented command they have probably already run. For a Laravel-only team, budget roughly half a day to a day: the command syntax is trivial, the risk model is not. The real ramp-up cost is building the safety scaffolding — snapshot/restore rehearsal, dry-run gating, report review, and verification queries — not learning the tool. That scaffolding is also the deliverable that makes the tool reusable if a second migration ever appears.
Bottom line. Approve as a scoped, sidecar-invoked migration utility with a mandatory dry-run gate and rehearsed restore; reject as anything resembling a dependency of the Laravel application. The low star count is a measurement artifact, not a quality signal, but the unknown repository and anomalous release date should be resolved before the tool enters the deploy pipeline.
How can I help you explore Laravel packages today?