From 5ec22781e9db9eefa9a2e9f2b23d6776acf1c30e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 20:51:21 +0000 Subject: [PATCH 01/10] Initial plan From 9649a815b54b95c60525976ab3fe208dd96ed335 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:00:02 +0000 Subject: [PATCH 02/10] Add warning when plugin is in active_plugins but file doesn't exist Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/plugin-is-active.feature | 53 +++++++++++++++++++++++++++++++ src/Plugin_Command.php | 43 +++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 features/plugin-is-active.feature diff --git a/features/plugin-is-active.feature b/features/plugin-is-active.feature new file mode 100644 index 00000000..52351fd0 --- /dev/null +++ b/features/plugin-is-active.feature @@ -0,0 +1,53 @@ +Feature: Check if a WordPress plugin is active + + Background: + Given a WP install + + Scenario: Check if an active plugin is active + When I run `wp plugin activate akismet` + Then STDOUT should contain: + """ + Success: + """ + + When I run `wp plugin is-active akismet` + Then the return code should be 0 + + Scenario: Check if an inactive plugin is not active + When I run `wp plugin deactivate akismet` + Then STDOUT should contain: + """ + Success: + """ + + When I try `wp plugin is-active akismet` + Then the return code should be 1 + + Scenario: Check if a non-existent plugin is not active + When I try `wp plugin is-active non-existent-plugin` + Then the return code should be 1 + + Scenario: Warn when plugin is in active_plugins but file does not exist + When I run `wp plugin activate akismet` + Then STDOUT should contain: + """ + Success: + """ + + When I run `wp plugin is-active akismet` + Then the return code should be 0 + + # Remove the plugin directory + When I run `wp plugin path akismet --dir` + Then save STDOUT as {PLUGIN_PATH} + + When I run `rm -rf {PLUGIN_PATH}` + Then the return code should be 0 + + # Now the plugin file is gone but still in active_plugins + When I try `wp plugin is-active akismet` + Then STDERR should contain: + """ + Warning: Plugin 'akismet' is in the active_plugins option but the plugin file does not exist. + """ + And the return code should be 1 diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index ef5b061f..4f1d8336 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1371,6 +1371,49 @@ public function is_active( $args, $assoc_args ) { $plugin = $this->fetcher->get( $args[0] ); if ( ! $plugin ) { + // Plugin not found via fetcher, but it might still be in active_plugins option + // Check if it's in the active_plugins list + $input_name = $args[0]; + $active_plugins = $network_wide ? get_site_option( 'active_sitewide_plugins', [] ) : get_option( 'active_plugins', [] ); + + // Ensure we have an array to work with + if ( ! is_array( $active_plugins ) ) { + $active_plugins = []; + } + + // For network-wide plugins, the array is keyed differently + if ( $network_wide ) { + $active_plugin_files = array_keys( $active_plugins ); + } else { + $active_plugin_files = $active_plugins; + } + + // Try to find a matching plugin file in active_plugins + $found_in_active = false; + foreach ( $active_plugin_files as $plugin_file ) { + // Ensure plugin_file is a string + if ( ! is_string( $plugin_file ) ) { + continue; + } + + // Check if the input matches the plugin file in various ways + if ( "$input_name.php" === $plugin_file || + $plugin_file === $input_name || + ( dirname( $plugin_file ) === $input_name && '.' !== $input_name ) ) { + $found_in_active = $plugin_file; + break; + } + } + + if ( is_string( $found_in_active ) && $found_in_active ) { + // Plugin is in active_plugins but file doesn't exist + // Use validate_plugin to confirm the file is missing + $validation = validate_plugin( $found_in_active ); + if ( is_wp_error( $validation ) ) { + WP_CLI::warning( "Plugin '{$input_name}' is in the active_plugins option but the plugin file does not exist." ); + } + } + WP_CLI::halt( 1 ); } From b0c8d9d5b49748bdc026b286ec08f44a5d37a498 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:01:27 +0000 Subject: [PATCH 03/10] Add documentation for is-active warning behavior Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/Plugin_Command.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 4f1d8336..55ca9772 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1348,6 +1348,9 @@ public function is_installed( $args, $assoc_args ) { * * Returns exit code 0 when active, 1 when not active. * + * If the plugin does not exist but is still in the active_plugins option, + * a warning will be emitted. + * * ## OPTIONS * * From b892b0320f019da77b395a07bcf8703df6b509b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:03:15 +0000 Subject: [PATCH 04/10] Fix type inconsistency in is_active method Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/Plugin_Command.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 55ca9772..3b9ac9a0 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1392,7 +1392,7 @@ public function is_active( $args, $assoc_args ) { } // Try to find a matching plugin file in active_plugins - $found_in_active = false; + $found_in_active = ''; foreach ( $active_plugin_files as $plugin_file ) { // Ensure plugin_file is a string if ( ! is_string( $plugin_file ) ) { @@ -1408,7 +1408,7 @@ public function is_active( $args, $assoc_args ) { } } - if ( is_string( $found_in_active ) && $found_in_active ) { + if ( $found_in_active ) { // Plugin is in active_plugins but file doesn't exist // Use validate_plugin to confirm the file is missing $validation = validate_plugin( $found_in_active ); From 81943ba8402bac99c707c300489b3c7033cb385c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:06:27 +0000 Subject: [PATCH 05/10] Add detailed comments explaining active_plugins structure Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/Plugin_Command.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 3b9ac9a0..ee925df3 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1376,7 +1376,9 @@ public function is_active( $args, $assoc_args ) { if ( ! $plugin ) { // Plugin not found via fetcher, but it might still be in active_plugins option // Check if it's in the active_plugins list - $input_name = $args[0]; + $input_name = $args[0]; + // For network plugins: active_sitewide_plugins is an array where keys are plugin files and values are timestamps + // For regular plugins: active_plugins is an array of plugin file paths $active_plugins = $network_wide ? get_site_option( 'active_sitewide_plugins', [] ) : get_option( 'active_plugins', [] ); // Ensure we have an array to work with @@ -1384,14 +1386,15 @@ public function is_active( $args, $assoc_args ) { $active_plugins = []; } - // For network-wide plugins, the array is keyed differently + // For network-wide plugins, extract the plugin files from the keys if ( $network_wide ) { $active_plugin_files = array_keys( $active_plugins ); } else { $active_plugin_files = $active_plugins; } - // Try to find a matching plugin file in active_plugins + // Try to find a matching plugin file in active_plugins using the same logic as the fetcher + // This matches: exact file name, "name.php", or directory name $found_in_active = ''; foreach ( $active_plugin_files as $plugin_file ) { // Ensure plugin_file is a string @@ -1400,6 +1403,7 @@ public function is_active( $args, $assoc_args ) { } // Check if the input matches the plugin file in various ways + // This mirrors the logic in WP_CLI\Fetchers\Plugin::get() if ( "$input_name.php" === $plugin_file || $plugin_file === $input_name || ( dirname( $plugin_file ) === $input_name && '.' !== $input_name ) ) { From 76ee8dcf6739665f71d3596e202c54e7f9bb0332 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:22:20 +0000 Subject: [PATCH 06/10] Fix test: activate plugin before deactivating in test scenario Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/plugin-is-active.feature | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/features/plugin-is-active.feature b/features/plugin-is-active.feature index 52351fd0..b080cedb 100644 --- a/features/plugin-is-active.feature +++ b/features/plugin-is-active.feature @@ -14,6 +14,12 @@ Feature: Check if a WordPress plugin is active Then the return code should be 0 Scenario: Check if an inactive plugin is not active + When I run `wp plugin activate akismet` + Then STDOUT should contain: + """ + Success: + """ + When I run `wp plugin deactivate akismet` Then STDOUT should contain: """ From b99a9be8ae27bafc4a917291fa06dff513bce277 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 14 Dec 2025 22:37:57 +0100 Subject: [PATCH 07/10] Update src/Plugin_Command.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Plugin_Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index ee925df3..6bce0c3e 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1417,7 +1417,7 @@ public function is_active( $args, $assoc_args ) { // Use validate_plugin to confirm the file is missing $validation = validate_plugin( $found_in_active ); if ( is_wp_error( $validation ) ) { - WP_CLI::warning( "Plugin '{$input_name}' is in the active_plugins option but the plugin file does not exist." ); + WP_CLI::warning( "Plugin '{$input_name}' is marked as active but the plugin file does not exist." ); } } From 920cefbceca6e57bde2ed0160f4d800c17760e03 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 14 Dec 2025 22:38:07 +0100 Subject: [PATCH 08/10] Update src/Plugin_Command.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Plugin_Command.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 6bce0c3e..5d88ec96 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1348,7 +1348,8 @@ public function is_installed( $args, $assoc_args ) { * * Returns exit code 0 when active, 1 when not active. * - * If the plugin does not exist but is still in the active_plugins option, + * If the plugin does not exist but is still in WordPress's active plugins storage + * (such as the active plugins option or the sitewide plugins option for network-activated plugins), * a warning will be emitted. * * ## OPTIONS From d1ccde9eded5de639dfd6c22ec7a1611c71e2028 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 14 Dec 2025 22:38:14 +0100 Subject: [PATCH 09/10] Update src/Plugin_Command.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Plugin_Command.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 5d88ec96..4dd7517f 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1405,9 +1405,11 @@ public function is_active( $args, $assoc_args ) { // Check if the input matches the plugin file in various ways // This mirrors the logic in WP_CLI\Fetchers\Plugin::get() - if ( "$input_name.php" === $plugin_file || - $plugin_file === $input_name || - ( dirname( $plugin_file ) === $input_name && '.' !== $input_name ) ) { + if ( + ( ! empty( $input_name ) && "$input_name.php" === $plugin_file ) || + ( ! empty( $input_name ) && $plugin_file === $input_name ) || + ( ! empty( $input_name ) && dirname( $plugin_file ) === $input_name && '.' !== $input_name ) + ) { $found_in_active = $plugin_file; break; } From 4f2f884ba8fb12ae0129a0087c915cb3a71ffa7f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:40:56 +0000 Subject: [PATCH 10/10] Add test for network-activated plugins with missing files Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/plugin-is-active.feature | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/features/plugin-is-active.feature b/features/plugin-is-active.feature index b080cedb..7113b1a1 100644 --- a/features/plugin-is-active.feature +++ b/features/plugin-is-active.feature @@ -54,6 +54,33 @@ Feature: Check if a WordPress plugin is active When I try `wp plugin is-active akismet` Then STDERR should contain: """ - Warning: Plugin 'akismet' is in the active_plugins option but the plugin file does not exist. + Warning: Plugin 'akismet' is marked as active but the plugin file does not exist. + """ + And the return code should be 1 + + Scenario: Warn when network-activated plugin is in active_sitewide_plugins but file does not exist + Given a WP multisite install + + When I run `wp plugin activate akismet --network` + Then STDOUT should contain: + """ + Success: + """ + + When I run `wp plugin is-active akismet --network` + Then the return code should be 0 + + # Remove the plugin directory + When I run `wp plugin path akismet --dir` + Then save STDOUT as {PLUGIN_PATH} + + When I run `rm -rf {PLUGIN_PATH}` + Then the return code should be 0 + + # Now the plugin file is gone but still in active_sitewide_plugins + When I try `wp plugin is-active akismet --network` + Then STDERR should contain: + """ + Warning: Plugin 'akismet' is marked as active but the plugin file does not exist. """ And the return code should be 1