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
| Flag | Description |
|---|---|
FILE | Path to the PHP file to execute |
[arg1 arg2 ...] | Optional positional arguments — accessible via $args inside the script |
--skip-wordpress | Execute the file without bootstrapping WordPress |
--skip-plugins | Run without loading active plugins |
--skip-themes | Run 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
| Rule | Reason |
|---|---|
Do not call require 'wp-load.php' | WP-CLI already bootstraps WordPress |
Do not call exit() in the middle | Terminates the WP-CLI process |
| Scripts run in global scope | Behave like a top-level PHP file |
Use echo for output | Standard output goes directly to the terminal |
Best Practices
- Use for long scripts — anything beyond 3-4 lines should be a file, not a
wp evalstring. - Version-control your scripts — keep them in a
scripts/ormaintenance/directory in your repo. - Use
$args— make scripts environment-agnostic (no hardcoded URLs). - Always test on staging —
eval-fileruns live; mistakes likewp_delete_post()in a loop are irreversible. - Add
echocheckpoints — track progress for long-running batch operations. - Use
--skip-pluginsif a plugin interferes with the script logic.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
File not found | Relative path issue | Use absolute path or run from the correct directory |
Call to undefined function | WordPress not bootstrapped or plugin not loaded | Remove --skip-plugins or check WP install integrity |
| Fatal error mid-script | PHP bug in script | Test incrementally with small batches first |
| Args not available | Wrong variable name | Use $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