diff --git a/features/plugin-is-active.feature b/features/plugin-is-active.feature new file mode 100644 index 00000000..7113b1a1 --- /dev/null +++ b/features/plugin-is-active.feature @@ -0,0 +1,86 @@ +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 activate akismet` + Then STDOUT should contain: + """ + Success: + """ + + 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 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 diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index ef5b061f..4dd7517f 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1348,6 +1348,10 @@ 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 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 * * @@ -1371,6 +1375,55 @@ 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]; + // 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 + if ( ! is_array( $active_plugins ) ) { + $active_plugins = []; + } + + // 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 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 + if ( ! is_string( $plugin_file ) ) { + continue; + } + + // Check if the input matches the plugin file in various ways + // This mirrors the logic in WP_CLI\Fetchers\Plugin::get() + 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; + } + } + + 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 ); + if ( is_wp_error( $validation ) ) { + WP_CLI::warning( "Plugin '{$input_name}' is marked as active but the plugin file does not exist." ); + } + } + WP_CLI::halt( 1 ); }