Skip to main content

wp eval-file

Overview

Execute a PHP file after the full WordPress environment has been bootstrapped. This is the preferred tool for complex scripts, data migrations, and batch operations that are too long to fit comfortably into a single wp eval string.

What It Does

wp eval-file loads WordPress (config, core, plugins, themes) and then includes the specified PHP file. The script has full access to all WordPress functions, $wpdb, and any global registered by active plugins or themes. WP-CLI handles the WordPress bootstrap — your script does not need to call wp-load.php.

Syntax

wp eval-file <file> [arg1 arg2 ...] [--skip-wordpress]

Options

FlagDescription
FILEPath to the PHP file to execute
[arg1 arg2 ...]Optional positional arguments — accessible via $args inside the script
--skip-wordpressExecute the file without bootstrapping WordPress
--skip-pluginsRun without loading active plugins
--skip-themesRun without loading active themes

Basic Examples

Simple script — print site info

<?php
// site-info.php
echo "Site URL: " . get_option("siteurl") . "\n";
echo "Site name: " . get_bloginfo("name") . "\n";
echo "WordPress version: " . get_bloginfo("version") . "\n";
wp eval-file site-info.php

Output:

Site URL: https://example.com
Site name: My WordPress Site
WordPress version: 6.4.2

Pass arguments to a script

<?php
// say-hello.php
$name = $args[0] ?? "World";
echo "Hello, $name!\n";
wp eval-file say-hello.php "WordPress"

Output:

Hello, WordPress!

Real-World Scenarios

Scenario 1: Bulk post title capitalisation

<?php
// fix-titles.php
$posts = get_posts(['numberposts' => -1, 'post_type' => 'post']);
$count = 0;
foreach ($posts as $post) {
$new_title = ucwords(strtolower($post->post_title));
wp_update_post(['ID' => $post->ID, 'post_title' => $new_title]);
$count++;
}
echo "Updated $count post titles.\n";
wp eval-file fix-titles.php

Output:

Updated 142 post titles.

Scenario 2: Assign a category to all uncategorised posts

<?php
// fix-categories.php
$cat_id = get_cat_ID('Uncategorised');
$posts = get_posts(['numberposts' => -1, 'category' => -$cat_id]);
$target_cat = get_cat_ID('General');
$count = 0;
foreach ($posts as $post) {
wp_set_post_categories($post->ID, [$target_cat]);
$count++;
}
echo "Reassigned $count posts to 'General'.\n";
wp eval-file fix-categories.php

Output:

Reassigned 34 posts to 'General'.

Scenario 3: Database cleanup — delete orphaned post meta

<?php
// clean-orphaned-meta.php
global $wpdb;
$deleted = $wpdb->query("
DELETE pm FROM $wpdb->postmeta pm
LEFT JOIN $wpdb->posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL
");
echo "Deleted $deleted orphaned postmeta rows.\n";
wp eval-file clean-orphaned-meta.php

Output:

Deleted 287 orphaned postmeta rows.

Scenario 4: Multisite — loop all subsites

<?php
// multisite-audit.php
$sites = get_sites(['number' => 0]);
foreach ($sites as $site) {
switch_to_blog($site->blog_id);
$name = get_bloginfo('name');
$url = get_option('siteurl');
$users = count_users();
echo "$url ($name) — {$users['total_users']} users\n";
restore_current_blog();
}
wp eval-file multisite-audit.php

Output:

https://example.com (Main Site) — 154 users
https://shop.example.com (Shop) — 12 users
https://blog.example.com (Blog) — 78 users

Scenario 5: Email digest of pending posts

<?php
// email-pending.php
$admin_email = get_option('admin_email');
$pending = get_posts(['post_status' => 'pending', 'numberposts' => -1]);
if (empty($pending)) {
echo "No pending posts.\n";
return;
}
$body = "Pending posts:\n\n";
foreach ($pending as $p) {
$body .= "- [{$p->ID}] {$p->post_title}\n";
}
wp_mail($admin_email, 'Pending Posts Report', $body);
echo "Sent report with " . count($pending) . " pending posts to $admin_email.\n";
wp eval-file email-pending.php

Output:

Sent report with 5 pending posts to admin@example.com.

Scenario 6: Dynamic args for environment-specific scripts

<?php
// migrate-env.php
$env = $args[0] ?? 'staging';
$old_url = $args[1] ?? 'https://staging.example.com';
$new_url = $args[2] ?? 'https://example.com';

global $wpdb;
$count = $wpdb->query(
$wpdb->prepare(
"UPDATE $wpdb->posts SET post_content = REPLACE(post_content, %s, %s)",
$old_url, $new_url
)
);
echo "[$env] Updated $count posts replacing '$old_url' with '$new_url'.\n";
wp eval-file migrate-env.php production https://staging.example.com https://example.com

Output:

[production] Updated 89 posts replacing 'https://staging.example.com' with 'https://example.com'.

Accessing Arguments ($args)

Arguments passed after the filename are accessible in the script as a PHP array named $args:

wp eval-file script.php "first" "second" 42
<?php
// script.php
echo $args[0]; // first
echo $args[1]; // second
echo $args[2]; // 42

PHP File Rules

RuleReason
Do not call require 'wp-load.php'WP-CLI already bootstraps WordPress
Do not call exit() in the middleTerminates the WP-CLI process
Scripts run in global scopeBehave like a top-level PHP file
Use echo for outputStandard output goes directly to the terminal

Best Practices

  1. Use for long scripts — anything beyond 3-4 lines should be a file, not a wp eval string.
  2. Version-control your scripts — keep them in a scripts/ or maintenance/ directory in your repo.
  3. Use $args — make scripts environment-agnostic (no hardcoded URLs).
  4. Always test on stagingeval-file runs live; mistakes like wp_delete_post() in a loop are irreversible.
  5. Add echo checkpoints — track progress for long-running batch operations.
  6. Use --skip-plugins if a plugin interferes with the script logic.

Troubleshooting

ProblemCauseFix
File not foundRelative path issueUse absolute path or run from the correct directory
Call to undefined functionWordPress not bootstrapped or plugin not loadedRemove --skip-plugins or check WP install integrity
Fatal error mid-scriptPHP bug in scriptTest incrementally with small batches first
Args not availableWrong variable nameUse $args, not $argv

Quick Reference

wp eval-file script.php                     # Run script
wp eval-file migrate.php production # With arg
wp eval-file cleanup.php --skip-plugins # Without plugins
wp eval-file check.php --skip-wordpress # Raw PHP only

Next Steps

  • wp eval — for quick, one-line PHP expressions.
  • wp shell — for interactive REPL exploration.
  • wp help — to look up any WP-CLI command.