@@ -778,9 +778,15 @@ protected function install_from_repo( $slug, $assoc_args ) {
778778
779779 $ status = install_plugin_install_status ( $ api );
780780
781- if ( ! Utils \get_flag_value ( $ assoc_args , 'force ' ) && 'install ' !== $ status ['status ' ] ) {
782- // We know this will fail, so avoid a needless download of the package.
783- return new WP_Error ( 'already_installed ' , 'Plugin already installed. ' );
781+ if ( ! Utils \get_flag_value ( $ assoc_args , 'force ' ) ) {
782+ $ is_installed = 'install ' !== $ status ['status ' ]
783+ || $ this ->is_plugin_installed ( $ slug )
784+ || $ this ->is_plugin_installed ( $ api ->slug );
785+
786+ if ( $ is_installed ) {
787+ // We know this will fail, so avoid a needless download of the package.
788+ return new WP_Error ( 'already_installed ' , 'Plugin already installed. ' );
789+ }
784790 }
785791
786792 WP_CLI ::log ( sprintf ( 'Installing %s (%s) ' , html_entity_decode ( $ api ->name , ENT_QUOTES ), $ api ->version ) );
@@ -798,6 +804,30 @@ protected function install_from_repo( $slug, $assoc_args ) {
798804 return $ result ;
799805 }
800806
807+ /**
808+ * Checks whether a plugin with the given slug is already installed.
809+ *
810+ * `install_plugin_install_status()` reports an 'install' status for a plugin
811+ * that is already installed when the WordPress.org API does not provide any
812+ * update information for the installed version. This happens for example for
813+ * the Akismet plugin that is bundled with WordPress, whenever the bundled
814+ * version is outdated but no update is being offered for it.
815+ *
816+ * Checking the file system as well makes sure such a plugin is reported as
817+ * being already installed instead of the installation failing with a
818+ * "Destination folder already exists" error.
819+ *
820+ * @param string $slug Plugin slug.
821+ * @return bool Whether the plugin folder holds an installed plugin.
822+ */
823+ private function is_plugin_installed ( $ slug ) {
824+ if ( ! is_dir ( WP_PLUGIN_DIR . '/ ' . $ slug ) ) {
825+ return false ;
826+ }
827+
828+ return count ( get_plugins ( '/ ' . $ slug ) ) > 0 ;
829+ }
830+
801831 /**
802832 * Updates one or more plugins.
803833 *
0 commit comments