
The crude script will loop through each plugin and check load time. See here for an example output: https://app.warp.dev/block/P0mRiYKGhE2B6wQAD7q2x2.
function find_slow_plugin() {
_get_wp_execution_time() {
local output
local extracted_time
# Capture stderr (where --debug output goes) to stdout.
# 'command wp' ensures we call the actual wp binary, not an alias.
output=$(command wp "$@" --debug 2>&1)
# Perl script to extract the time like "0.123s". It handles debug formats
# like "(0.123s)" or "(0.123s|more_info)".
extracted_time=$(echo "$output" | perl -ne '/Debug \(bootstrap\): Running command: .+\(([^s]+s)/ && print $1')
if [[ -n "$extracted_time" ]]; then
echo "$extracted_time"
else
echo "" # Return empty on failure
fi
}
local base_time_s
local time_no_themes_s
local time_no_plugins_s
local time_with_skip_s
local active_plugins # Array to hold active plugin slugs
local plugin # Loop variable for plugin slug
local results=() # Array to store results for sorting
# Check 1: Is WP-CLI installed?
if ! command -v wp &>/dev/null; then
echo "❌ Error: WP-CLI (wp command) not found. Please install WP-CLI." >&2
echo "See: https://wp-cli.org/" >&2
return 1
fi
# Check 2: Are we in a WordPress directory?
if ! command wp core is-installed --quiet; then
echo "❌ Error: This does not appear to be a WordPress installation, or WP-CLI cannot connect." >&2
echo "Please run this script from the root directory of a WordPress site." >&2
return 1
fi
echo "🚀 WordPress Plugin Performance Test 🚀"
echo "This script measures the execution time of 'wp plugin list --debug' under various conditions."
echo ""
echo "📋 Initial Baseline Measurements for 'wp plugin list --debug':"
printf " ⏳ Measuring time with NO themes loaded (--skip-themes)... "
time_no_themes_s=$(_get_wp_execution_time plugin list --skip-themes)
if [[ -n "$time_no_themes_s" ]]; then
echo "Time: $time_no_themes_s"
else
echo "⚠️ Could not measure (check 'wp plugin list --skip-themes --debug' manually)."
fi
printf " ⏳ Measuring time with NO plugins loaded (--skip-plugins)... "
time_no_plugins_s=$(_get_wp_execution_time plugin list --skip-plugins)
if [[ -n "$time_no_plugins_s" ]]; then
echo "Time: $time_no_plugins_s"
else
echo "⚠️ Could not measure (check 'wp plugin list --skip-plugins --debug' manually)."
fi
printf " ⏳ Measuring base time (ALL plugins & theme active)... "
base_time_s=$(_get_wp_execution_time plugin list)
if [[ -z "$base_time_s" ]]; then
echo "❌ Error: Could not measure base execution time (all plugins & theme active)." >&2
echo "Please ensure WP-CLI is working correctly. Try 'wp plugin list --debug' manually." >&2
return 1
fi
echo "Base time: $base_time_s"
echo ""
# Get a list of active plugin slugs.
# 'mapfile' reads lines into an array, which is robust.
mapfile -t active_plugins < <(command wp plugin list --field=name --status=active)
if [[ ${#active_plugins[@]} -eq 0 ]]; then
echo "ℹ️ No active plugins found to test for individual impact."
echo "✅ Test Complete (baselines reported above)."
return 0
fi
echo "📊 Measuring impact of individual plugins (compared to '$base_time_s' base time):"
echo "A larger positive 'Impact' suggests the plugin contributes more to the load time of this specific WP-CLI command."
echo "---------------------------------------------------------------------------------"
printf "%-40s | %-15s | %-15s\n" "Plugin Skipped" "Time w/ Skip" "Impact (Base-Skip)"
echo "---------------------------------------------------------------------------------"
for plugin in "${active_plugins[@]}"; do
time_with_skip_s=$(_get_wp_execution_time plugin list --skip-plugins="$plugin")
if [[ -n "$time_with_skip_s" ]]; then
# Remove 's' suffix for calculations
local base_num=${base_time_s%s}
local skip_num=${time_with_skip_s%s}
# Use awk for floating-point arithmetic
local diff_s
diff_s=$(awk -v base="$base_num" -v skip="$skip_num" 'BEGIN { printf "%.3f", base - skip }')
local impact_sign=""
# Check if diff is positive using a robust numeric comparison
if [[ $(awk -v diff="$diff_s" 'BEGIN { print (diff > 0) }') -eq 1 ]]; then
impact_sign="+"
fi
# Store results for sorting later
results+=("$(printf "%.3f" "$diff_s")|$plugin|$time_with_skip_s|${impact_sign}${diff_s}s")
else
# Store error result if time couldn't be measured
results+=("0.000|$plugin|Error|Error measuring")
fi
done
# Sort results by the numeric impact value (first field) in descending order
mapfile -t sorted_results < <(for res in "${results[@]}"; do echo "$res"; done | sort -t'|' -k1,1nr)
# Display sorted results
for result_line in "${sorted_results[@]}"; do
local p_name=$(echo "$result_line" | cut -d'|' -f2)
local t_skip=$(echo "$result_line" | cut -d'|' -f3)
local i_str=$(echo "$result_line" | cut -d'|' -f4)
printf "%-40s | %-15s | %-15s\n" "$p_name" "$t_skip" "$i_str"
done
echo "---------------------------------------------------------------------------------"
echo ""
echo "✅ Test Complete"
echo ""
echo "💡 Note: This script measures impact on 'wp plugin list --debug' command execution time."
echo "Actual impact on front-end or admin page load times may vary. For comprehensive"
echo "profiling, consider tools like Query Monitor (plugin) or Xdebug."
echo ""
}
find_slow_plugin
Run it across a fleet
Open the console in CaptainCore Manager, pick the target sites, choose this recipe from the Cookbook picker and press run. The CLI streams each site's output back to the dock.
About the console →