extension-command icon indicating copy to clipboard operation
extension-command copied to clipboard

Add warning in `wp plugin is-active` for missing plugin files still in active_plugins

Open Copilot opened this issue 2 months ago • 3 comments

Description

When a plugin directory is manually removed (not via WP-CLI), the plugin remains in WordPress's active_plugins option. This orphaned entry can break dependency checks and compatibility features in other plugins and core. wp plugin is-active previously returned "not active" without indicating this inconsistent state.

Changes

  • Modified Plugin_Command::is_active() to check active_plugins / active_sitewide_plugins options when plugin file is not found
  • Uses WordPress's validate_plugin() to confirm file absence
  • Emits warning: Plugin 'X' is marked as active but the plugin file does not exist.
  • Returns exit code 1 (not active) as before - no behavioral change
  • Handles both single-site and multisite scenarios

Example behavior:

$ wp plugin activate akismet
Success: Activated 1 of 1 plugins.

$ rm -rf wp-content/plugins/akismet

$ wp plugin is-active akismet
Warning: Plugin 'akismet' is marked as active but the plugin file does not exist.
$ echo $?
1

Tests

  • Added features/plugin-is-active.feature with scenarios covering active, inactive, missing, and orphaned plugins
  • Includes test for network-activated plugins with missing files to verify --network flag behavior
  • Fixed test scenario to properly activate plugin before deactivating to ensure consistent test behavior

Checklist

  • [x] Functional tests included
  • [x] Follows WP-CLI coding standards (lint, phpcs, phpstan pass)
  • [x] Documentation updated (method docblock)
Original prompt

This section details on the original issue you should resolve

<issue_title>is-active/deactivate should also check the "active_plugins" option</issue_title> <issue_description>## Bug Report

Describe the current, buggy behavior

When a plugin file/directory is removed by the user (and not by WP), the "active_plugins" option will still contain that plugin. Various plugins (and WP core?) use this "active_plugins" option for dependency checks, loading of compatibitility data,...

However wp plugin is-active and wp plugin deactivate will report that the plugin isn't active/the plugin could not be found.

WP core has a function https://developer.wordpress.org/reference/functions/validate_active_plugins/ to update this option (and deactivate all those not found ones), however it will deactivate ALL of those plugins, not necessarily only the one passed to CLI. However it's trivial copy and port that to CLI.

Describe how other contributors can replicate this bug

wp plugin activate whatever
rm -rf wp-content/plugins/whatever
wp plugin deactivate whatever
wp eval "echo array_values( preg_grep( '#^whatever#', get_option( 'active_plugins' ) ) )[0];"

Describe what you would expect as the correct outcome

is-active and deactivate should deactivate the plugin if it's still in active_plugins but the directory does not exist.

Provide a possible solution

See validate_active_plugins()</issue_description>

<agent_instructions>wp plugin is-active should emit a warning if the plugin does not exist but is still in the active_plugins option. This can be done by using the validate_plugin option provided by WordPress</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@swissspidy Updating `wp plugin deactivate` sounds reasonable at first glance, but we shouldn't change `is-active` to make destructive actions. That sounds very unexpected when you just want to check the status. @swissspidy What about `wp plugin is-active` emitting a warning if the plugin does not exist but is still in the `active_plugins` option?
  • Fixes wp-cli/extension-command#458

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot avatar Dec 14 '25 20:12 Copilot

@copilot One failing test:

001 Scenario: Check if an inactive plugin is not active # features/plugin-is-active.feature:16
      When I run `wp plugin deactivate akismet`         # features/plugin-is-active.feature:17
        $ wp plugin deactivate akismet
        Success: Plugin already deactivated.
        
        Warning: Plugin 'akismet' isn't active.

swissspidy avatar Dec 14 '25 21:12 swissspidy

@copilot One failing test:

...

Fixed in commit 76ee8dc. The test scenario now activates the plugin before deactivating it, since each scenario starts with a fresh WP install where akismet is inactive by default.

Copilot avatar Dec 14 '25 21:12 Copilot

Codecov Report

:x: Patch coverage is 88.88889% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Plugin_Command.php 88.88% 3 Missing :warning:

:loudspeaker: Thoughts on this report? Let us know!

codecov[bot] avatar Dec 14 '25 22:12 codecov[bot]

/gemini review

swissspidy avatar Jan 20 '26 20:01 swissspidy

@copilot These ! empty( $input_name ) checks appear to be redundant. The <plugin> argument is required for this command, so $input_name (from $args[0]) should always be a non-empty string. Removing these checks will simplify the condition.

On a related note, this logic for finding a plugin by name is duplicated from WP_CLI\Fetchers\Plugin::get(). For future refactoring, it would be beneficial to extract this into a shared helper to improve maintainability.

					"$input_name.php" === $plugin_file ||
					$plugin_file === $input_name ||
					( dirname( $plugin_file ) === $input_name && '.' !== $input_name )

swissspidy avatar Feb 05 '26 10:02 swissspidy

@copilot These ! empty( $input_name ) checks appear to be redundant. The <plugin> argument is required for this command, so $input_name (from $args[0]) should always be a non-empty string. Rem...

Removed the redundant ! empty( $input_name ) checks in commit 0ed14ea. Since <plugin> is a required argument, $input_name will always be set.

Copilot avatar Feb 05 '26 10:02 Copilot