@@ -1017,6 +1017,9 @@ protected function filter_item_list( $items, $args ) {
10171017 * [--insecure]
10181018 * : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
10191019 *
1020+ * [--with-dependencies]
1021+ * : If set, the command will also install all required dependencies of the plugin as specified in the 'Requires Plugins' header.
1022+ *
10201023 * ## EXAMPLES
10211024 *
10221025 * # Install the latest version from wordpress.org and activate
@@ -1073,14 +1076,114 @@ protected function filter_item_list( $items, $args ) {
10731076 * Removing the old version of the plugin...
10741077 * Plugin updated successfully
10751078 * Success: Installed 1 of 1 plugins.
1079+ *
1080+ * # Install a plugin with all its dependencies
1081+ * $ wp plugin install my-plugin --with-dependencies
1082+ * Installing dependency: required-plugin-1 (1.2.3)
1083+ * Plugin installed successfully.
1084+ * Installing dependency: required-plugin-2 (2.0.0)
1085+ * Plugin installed successfully.
1086+ * Installing my-plugin (3.5.0)
1087+ * Plugin installed successfully.
1088+ * Success: Installed 3 of 3 plugins.
10761089 */
10771090 public function install ( $ args , $ assoc_args ) {
10781091
10791092 if ( ! is_dir ( WP_PLUGIN_DIR ) ) {
10801093 wp_mkdir_p ( WP_PLUGIN_DIR );
10811094 }
10821095
1083- parent ::install ( $ args , $ assoc_args );
1096+ // If --with-dependencies is set, we need to handle dependencies
1097+ if ( Utils \get_flag_value ( $ assoc_args , 'with-dependencies ' , false ) ) {
1098+ $ this ->install_with_dependencies ( $ args , $ assoc_args );
1099+ } else {
1100+ parent ::install ( $ args , $ assoc_args );
1101+ }
1102+ }
1103+
1104+ /**
1105+ * Installs plugins with their dependencies.
1106+ *
1107+ * @param array $args Plugin slugs to install.
1108+ * @param array $assoc_args Associative arguments.
1109+ */
1110+ private function install_with_dependencies ( $ args , $ assoc_args ) {
1111+ $ all_to_install = [];
1112+ $ installed_tracker = [];
1113+
1114+ // Remove with-dependencies from assoc_args to avoid infinite recursion
1115+ unset( $ assoc_args ['with-dependencies ' ] );
1116+
1117+ // Collect all plugins and their dependencies
1118+ foreach ( $ args as $ slug ) {
1119+ $ this ->collect_dependencies ( $ slug , $ all_to_install , $ installed_tracker );
1120+ }
1121+
1122+ if ( empty ( $ all_to_install ) ) {
1123+ WP_CLI ::success ( 'No plugins to install. ' );
1124+ return ;
1125+ }
1126+
1127+ // Install all collected plugins
1128+ parent ::install ( $ all_to_install , $ assoc_args );
1129+ }
1130+
1131+ /**
1132+ * Recursively collects all dependencies for a plugin.
1133+ *
1134+ * @param string $slug Plugin slug.
1135+ * @param array &$all_to_install Reference to array of all plugins to install.
1136+ * @param array &$installed_tracker Reference to array tracking what we've already processed.
1137+ */
1138+ private function collect_dependencies ( $ slug , &$ all_to_install , &$ installed_tracker ) {
1139+ // Skip if already processed
1140+ if ( isset ( $ installed_tracker [ $ slug ] ) ) {
1141+ return ;
1142+ }
1143+
1144+ $ installed_tracker [ $ slug ] = true ;
1145+
1146+ // Skip if it's a URL or zip file (can't get dependencies for those)
1147+ $ is_remote = false !== strpos ( $ slug , ':// ' );
1148+ if ( $ is_remote || ( pathinfo ( $ slug , PATHINFO_EXTENSION ) === 'zip ' && is_file ( $ slug ) ) ) {
1149+ $ all_to_install [] = $ slug ;
1150+ return ;
1151+ }
1152+
1153+ // Get plugin dependencies from WordPress.org API
1154+ $ dependencies = $ this ->get_plugin_dependencies ( $ slug );
1155+
1156+ // Recursively install dependencies first
1157+ if ( ! empty ( $ dependencies ) ) {
1158+ foreach ( $ dependencies as $ dependency_slug ) {
1159+ $ this ->collect_dependencies ( $ dependency_slug , $ all_to_install , $ installed_tracker );
1160+ }
1161+ }
1162+
1163+ // Add this plugin to the install list
1164+ $ all_to_install [] = $ slug ;
1165+ }
1166+
1167+ /**
1168+ * Gets the dependencies for a plugin from WordPress.org API.
1169+ *
1170+ * @param string $slug Plugin slug.
1171+ * @return array Array of dependency slugs.
1172+ */
1173+ private function get_plugin_dependencies ( $ slug ) {
1174+ $ api = plugins_api ( 'plugin_information ' , array ( 'slug ' => $ slug ) );
1175+
1176+ if ( is_wp_error ( $ api ) ) {
1177+ WP_CLI ::debug ( "Could not fetch information for plugin ' $ slug': " . $ api ->get_error_message () );
1178+ return [];
1179+ }
1180+
1181+ // Check if requires_plugins field exists and is not empty
1182+ if ( ! empty ( $ api ->requires_plugins ) && is_array ( $ api ->requires_plugins ) ) {
1183+ return $ api ->requires_plugins ;
1184+ }
1185+
1186+ return [];
10841187 }
10851188
10861189 /**
@@ -1377,6 +1480,66 @@ public function is_active( $args, $assoc_args ) {
13771480 $ this ->check_active ( $ plugin ->file , $ network_wide ) ? WP_CLI ::halt ( 0 ) : WP_CLI ::halt ( 1 );
13781481 }
13791482
1483+ /**
1484+ * Installs all dependencies of an installed plugin.
1485+ *
1486+ * This command is useful when you have a plugin installed that depends on other plugins,
1487+ * and you want to install those dependencies without activating the main plugin.
1488+ *
1489+ * ## OPTIONS
1490+ *
1491+ * <plugin>
1492+ * : The installed plugin to get dependencies for.
1493+ *
1494+ * [--activate]
1495+ * : If set, dependencies will be activated immediately after install.
1496+ *
1497+ * [--activate-network]
1498+ * : If set, dependencies will be network activated immediately after install.
1499+ *
1500+ * ## EXAMPLES
1501+ *
1502+ * # Install all dependencies of an installed plugin
1503+ * $ wp plugin install-dependencies my-plugin
1504+ * Installing dependency: required-plugin-1 (1.2.3)
1505+ * Plugin installed successfully.
1506+ * Installing dependency: required-plugin-2 (2.0.0)
1507+ * Plugin installed successfully.
1508+ * Success: Installed 2 dependencies.
1509+ *
1510+ * @subcommand install-dependencies
1511+ */
1512+ public function install_dependencies ( $ args , $ assoc_args ) {
1513+ $ plugin = $ this ->fetcher ->get_check ( $ args [0 ] );
1514+ $ file = $ plugin ->file ;
1515+
1516+ // Check if plugin is installed
1517+ if ( ! file_exists ( WP_PLUGIN_DIR . '/ ' . $ file ) ) {
1518+ WP_CLI ::error ( "Plugin ' {$ args [0 ]}' is not installed. " );
1519+ }
1520+
1521+ // Get dependencies from plugin header
1522+ $ plugin_data = get_plugin_data ( WP_PLUGIN_DIR . '/ ' . $ file , false , false );
1523+ $ dependencies = [];
1524+
1525+ if ( ! empty ( $ plugin_data ['RequiresPlugins ' ] ) ) {
1526+ // Parse the comma-separated list
1527+ $ dependencies = array_map ( 'trim ' , explode ( ', ' , $ plugin_data ['RequiresPlugins ' ] ) );
1528+ }
1529+
1530+ if ( empty ( $ dependencies ) ) {
1531+ WP_CLI ::success ( "Plugin ' {$ args [0 ]}' has no dependencies. " );
1532+ return ;
1533+ }
1534+
1535+ WP_CLI ::log ( sprintf ( "Installing %d %s for '%s'... " , count ( $ dependencies ), Utils \pluralize ( 'dependency ' , count ( $ dependencies ) ), $ args [0 ] ) );
1536+
1537+ // Install dependencies
1538+ $ this ->chained_command = true ;
1539+ $ this ->install ( $ dependencies , $ assoc_args );
1540+ $ this ->chained_command = false ;
1541+ }
1542+
13801543 /**
13811544 * Deletes plugin files without deactivating or uninstalling.
13821545 *
0 commit comments