wp-cli/eval-command
Run arbitrary PHP code or files from the command line with WP-CLI. wp eval executes a code string, while wp eval-file loads a PHP file with positional arguments. Both support --skip-wordpress to run without WordPress and --hook to run after a specific WordPress hook fires.
Architecture fit
The first thing to correct at intake: this is not a Laravel package. wp-cli/eval-command is a WP-CLI bundled command that adds wp eval and wp eval-file to the WordPress CLI. It has no Laravel service provider, no facade, no config surface, and nothing to autoload into a Laravel application. Framing it as a "Laravel/PHP package" will produce the wrong adoption decision.
Where it actually fits our estate:
wp eval-file is a viable escape hatch for one-off data operations and incident triage — invoked as a subprocess from an Artisan command, never as a Composer dependency.artisan tinker, Laravel migrations, seeders, or scheduled jobs. It is not a migration tool, not idempotent, and not transactional.The execution model is the key architectural fact: code is evaluated inside a method, so globals must be explicitly globalized. Since v2.2.7 $args is available as a global for eval-file; since v2.2.9 $assoc_args is available in eval. Since v2.2.8 --hook=<hook> defers execution until a named WordPress hook fires, which is the only reliable way to reach code paths that depend on wp_loaded or later lifecycle state. --skip-wordpress skips the WordPress bootstrap entirely — that mode is effectively plain PHP and is the only variant I would consider safe in a Laravel-only context.
Integration feasibility
Low coupling, but also low leverage. Concretely:
wp package install) exists only to override the bundled version. That means version management is really WP-CLI version management, not Composer dependency management. Our upgrade lever is "which WP-CLI release is on this host," which is usually baked into a Docker image or a global binary — frequently unowned.wp-cli/wp-cli v2.13. Modern Laravel hosts on PHP 8.1+ are fine on the PHP side, but the WP-CLI binary itself must be current enough to match.wp eval will fatal. This is a hard boundary, not a configuration detail.--format=json envelope on eval/eval-file; if an engineer echos inside the script and we also echo a JSON payload, we get corrupt output. Any automation must own its output discipline explicitly.Technical risk
wp-config.php, wp eval is equivalent to root-adjacent database access. This is a governance problem before it is a technical one.eval-file against failures while preparing the file") addresses file preparation, not mid-execution rollback. We must assume no atomicity.init/wp_loaded that sends mail, fires webhooks, or purges caches will run. --skip-wordpress avoids this but also removes the WordPress APIs, which is usually why we're there.--hook=<hook> on a hook that never fires yields a successful exit code and zero work. Easy to misread in a runbook.ps output, CI logs, and shell audit trails. eval-file - reading from STDIN is the mitigation, and it should be the default in our standards.Key questions
wp eval currently permitted on production hosts, and by whom? If nobody knows, we have an unmonitored arbitrary-code-execution path today.wp invocations, or do we need to proxy it through a controlled runner?wp eval that should be promoted to a proper wp scaffold package command with tests?Stack fit
Treat this as operational tooling, not a dependency. There is nothing to add to composer.json in a Laravel service. The integration surfaces are:
| Scenario | Fit | Approach |
|---|---|---|
| Laravel-only app | None | Do not adopt; use artisan tinker / migrations |
| WordPress-only site | Strong | Already installed; define usage policy and runbooks |
| Laravel orchestrating WordPress (content/order sync) | Moderate | Laravel Artisan command shells out to wp eval-file - with script on STDIN |
| Laravel plugin inside WordPress | Weak | Do not use eval to reach Laravel; use a proper WP-CLI command package |
The third row is the only genuinely interesting hybrid pattern, and it should be constrained: the Laravel command owns idempotency, batching, logging, and exit-code interpretation; WordPress just executes a narrow, versioned script.
Migration path
wp eval. In my experience most organizations have more of these than anyone expects, and they are undocumented tribal knowledge.wp eval-file with a path in a checked-out repo is auditable; wp eval '...' inline is not.wp eval from production as the end state; retain eval-file against versioned scripts with an approval gate.Compatibility
--hook requires ≥2.2.8; --use-include requires ≥2.2.0; $assoc_args in eval requires ≥2.2.9. If scripts rely on these flags, that sets an effective WP-CLI floor that must be enforced, not assumed.--use-include vs default eval changes __FILE__/__DIR__ behavior. WP-CLI rewrites magic constants, but the semantics differ between the two modes and scripts that depend on relative paths will break when someone flips the flag. Pick one convention per script and document it.Sequencing
wp eval is arbitrary code execution and classify it accordingly in our access-control model. No code changes.eval-file scripts with STDIN and explicit exit codes. Validate exit-code behavior under fatal errors and under a non-firing --hook, since both matter for CI gating.Maintenance
Genuinely low, and that is the package's main virtue. It is bundled with WP-CLI, so maintenance collapses into "keep WP-CLI current," which we should be doing regardless for security patches. The package itself has a narrow surface — two commands, five flags — and a steady but unremarkable release history (mostly CI, PHPCS, and PHP-version housekeeping, with functional changes like --hook, --use-include, and $args/$assoc_args exposure arriving incrementally). Expect it to track WP-CLI releases rather than drive its own roadmap.
The real maintenance burden is the scripts we write with it, not the package. Ungoverned, this becomes a shadow codebase of untested, unversioned PHP living in shell history and Confluence pages. Budget for curation.
Support
Community-supported under the WP-CLI project. GitHub issues accept bugs and PRs; general support is explicitly redirected to the WP-CLI handbook and WordPress forums. There is no vendor SLA. That is fine for an ops tool and unacceptable as a dependency in a revenue-critical path — which reinforces the "tooling, not dependency" framing.
Internal support model should be: a named owner for the WP-CLI binary, a documented runbook per approved script, and a review requirement for new production scripts.
Scaling
Poorly suited to high-frequency or high-volume work, and this should be stated plainly rather than discovered. Each invocation pays full WordPress bootstrap — loading core, all plugins, and the theme — typically hundreds of milliseconds to several seconds before a single line of our code runs. It is a per-invocation CLI process with no daemon, no connection pooling, and no batching primitive. Using it in a loop over thousands of records is an anti-pattern; batch inside one script, or build a proper command.
Horizontal scale is a non-issue (it's a CLI process), but concurrency is: two operators running write scripts against the same database have no coordination mechanism, no locking, and no visibility into each other.
Failure modes
| Failure | Symptom | Mitigation |
|---|---|---|
| Fatal error mid-script | Partial writes, non-zero exit, truncated stdout | Idempotent scripts; explicit pre-flight checks; backups before writes |
--hook never fires |
Exit 0, no work done | Assert a sentinel was reached; validate hook name against target WP version |
| Plugin bootstrap side effects | Emails sent, webhooks fired, caches purged | --skip-wordpress where possible; test in staging first |
| Global scope collision | Variables clobbered, unexpected behavior | Explicit global; unique variable prefixes |
| Stdout pollution | Corrupt JSON for downstream consumers | Enforce "no bare echo" convention; separate logging channel |
| Secret leakage | Credentials in history/CI logs | Standardize on eval-file - via STDIN |
| Version skew | Works locally, fails in container | Pin WP-CLI in images; assert version in CI |
| Unbounded runtime | Hung process holding DB connections | timeout wrapper; memory limit awareness |
The dangerous failure mode is the quiet one: exit code 0, no error, wrong or incomplete state. Automation gating on wp eval must never trust exit code alone — every production script needs a verification step that re-reads the result.
Ramp-up
Low for competent PHP developers — an hour to be productive, because the mental model is just "PHP with WordPress loaded." The friction is in the non-obvious semantics, and those deserve explicit documentation rather than discovery:
__FILE__/__DIR__ are rewritten; behavior differs between eval and --use-include.--skip-wordpress removes the WordPress APIs that usually motivated the call.New engineers should be given a template script and a rule: if you run it twice, it becomes a command package. That single norm prevents the sprawl that otherwise makes this tool an audit finding rather than an accelerant.
Recommendation: adopt as sanctioned operational tooling on WordPress hosts with a written usage policy and a controlled production runner; do not add to any Laravel application's dependency graph; treat every recurring script as a candidate for promotion into a tested WP-CLI command.
How can I help you explore Laravel packages today?