From d75ff328468f61a21bc4c01c1170d2bfa70d6d6f Mon Sep 17 00:00:00 2001 From: Isla Waters Date: Fri, 6 Dec 2024 13:12:24 -0500 Subject: [PATCH 01/10] Check WordPress and PHP requirements before installing a theme or plugin This matches the behavior of WordPress core which will refuse to install a plugin if the local copy of WordPress or PHP don't meet the minimum requirements for the most recent version as listed by the plugin authors. Unfortunately the api is limited and only provides these requirement details for the most recent version, so it isn't possible to find an older version that might work. As a compromise, this code doesn't check requirements if a user provides a specific --version since we can't know the requirements for anything other than the latest version and assume if somebody specifies a version they know it will work or want to try anyway. --- features/plugin-install.feature | 32 ++++++++++++++++++++++++++++++++ features/theme-install.feature | 32 ++++++++++++++++++++++++++++++++ src/Plugin_Command.php | 14 ++++++++++++++ src/Theme_Command.php | 14 ++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/features/plugin-install.feature b/features/plugin-install.feature index b490c7f3..f9281cca 100644 --- a/features/plugin-install.feature +++ b/features/plugin-install.feature @@ -237,3 +237,35 @@ Feature: Install WordPress plugins Plugin 'site-secrets' activated. Success: Plugin already installed. """ + + Scenario: Can't install plugin that requires a newer version of WordPress + Given a WP install + + When I run `wp core download --version=6.4 --force` + And I run `rm -r wp-content/themes/*` + + And I try `wp plugin install wp-super-cache` + Then STDERR should contain: + """ + Warning: wp-super-cache: This plugin does not work with your version of WordPress + """ + + And STDERR should contain: + """ + Error: No plugins installed. + """ + + @less-than-php-7.4 + Scenario: Can't install plugin that requires a newer version of PHP + Given a WP install + + And I try `wp plugin install contact-form-7` + Then STDERR should contain: + """ + Warning: contact-form-7: This plugin does not work with your version of PHP + """ + + And STDERR should contain: + """ + Error: No plugins installed. + """ diff --git a/features/theme-install.feature b/features/theme-install.feature index a49e7c23..2f4fece4 100644 --- a/features/theme-install.feature +++ b/features/theme-install.feature @@ -125,3 +125,35 @@ Feature: Install WordPress themes """ twentyeleven """ + + Scenario: Can't install theme that requires a newer version of WordPress + Given a WP install + + When I run `wp core download --version=6.4 --force` + And I run `rm -r wp-content/themes/*` + + And I try `wp theme install twentytwentyfive` + Then STDERR should contain: + """ + Warning: twentytwentyfive: This theme does not work with your version of WordPress. + """ + + And STDERR should contain: + """ + Error: No themes installed. + """ + + @less-than-php-7.4 + Scenario: Can't install theme that requires a newer version of PHP + Given a WP install + + And I try `wp theme install oceanwp` + Then STDERR should contain: + """ + Warning: oceanwp: This theme does not work with your version of PHP. + """ + + And STDERR should contain: + """ + Error: No themes installed. + """ diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index ca8c346a..bd221046 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -590,6 +590,20 @@ protected function install_from_repo( $slug, $assoc_args ) { if ( isset( $assoc_args['version'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); + } else { + $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; + $requires_wp = isset( $api->requires ) ? $api->requires : null; + + $compatible_php = is_php_version_compatible( $requires_php ); + $compatible_wp = is_wp_version_compatible( $requires_wp ); + + if ( ! $compatible_wp ) { + return new WP_Error( 'requirements_not_met', "This plugin does not work with your version of WordPress. Minimum WordPress requirement is $requires_wp" ); + } + + if ( ! $compatible_php ) { + return new WP_Error( 'requirements_not_met', "This plugin does not work with your version of PHP. Minimum PHP required is $compatible_php" ); + } } $status = install_plugin_install_status( $api ); diff --git a/src/Theme_Command.php b/src/Theme_Command.php index 19bf71b0..49715d10 100644 --- a/src/Theme_Command.php +++ b/src/Theme_Command.php @@ -403,6 +403,20 @@ protected function install_from_repo( $slug, $assoc_args ) { if ( isset( $assoc_args['version'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); + } else { + $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; + $requires_wp = isset( $api->requires ) ? $api->requires : null; + + $compatible_php = is_php_version_compatible( $requires_php ); + $compatible_wp = is_wp_version_compatible( $requires_wp ); + + if ( ! $compatible_wp ) { + return new WP_Error( 'requirements_not_met', "This theme does not work with your version of WordPress. Minimum WordPress requirement is $requires_wp" ); + } + + if ( ! $compatible_php ) { + return new WP_Error( 'requirements_not_met', "This theme does not work with your version of PHP. Minimum PHP required is $requires_php" ); + } } if ( ! Utils\get_flag_value( $assoc_args, 'force' ) ) { From e522b030ad17f25fcb9f86e21df50e504cb1723b Mon Sep 17 00:00:00 2001 From: Isla Waters Date: Fri, 6 Dec 2024 15:06:32 -0500 Subject: [PATCH 02/10] Use version_compare directly The built in wp functions for this are too new (Added in 5.2) --- src/Plugin_Command.php | 5 +++-- src/Theme_Command.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index bd221046..57d9f726 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -582,6 +582,7 @@ public function path( $args, $assoc_args ) { } protected function install_from_repo( $slug, $assoc_args ) { + global $wp_version; $api = plugins_api( 'plugin_information', array( 'slug' => $slug ) ); if ( is_wp_error( $api ) ) { @@ -594,8 +595,8 @@ protected function install_from_repo( $slug, $assoc_args ) { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; $requires_wp = isset( $api->requires ) ? $api->requires : null; - $compatible_php = is_php_version_compatible( $requires_php ); - $compatible_wp = is_wp_version_compatible( $requires_wp ); + $compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' ); + $compatible_wp = empty( $requires_wp ) || version_compare( $wp_version, $requires_wp, '>=' ); if ( ! $compatible_wp ) { return new WP_Error( 'requirements_not_met', "This plugin does not work with your version of WordPress. Minimum WordPress requirement is $requires_wp" ); diff --git a/src/Theme_Command.php b/src/Theme_Command.php index 49715d10..d0eb984a 100644 --- a/src/Theme_Command.php +++ b/src/Theme_Command.php @@ -395,6 +395,7 @@ public function path( $args, $assoc_args ) { } protected function install_from_repo( $slug, $assoc_args ) { + global $wp_version; $api = themes_api( 'theme_information', array( 'slug' => $slug ) ); if ( is_wp_error( $api ) ) { @@ -407,8 +408,8 @@ protected function install_from_repo( $slug, $assoc_args ) { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; $requires_wp = isset( $api->requires ) ? $api->requires : null; - $compatible_php = is_php_version_compatible( $requires_php ); - $compatible_wp = is_wp_version_compatible( $requires_wp ); + $compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' ); + $compatible_wp = empty( $requires_wp ) || version_compare( $wp_version, $requires_wp, '>=' ); if ( ! $compatible_wp ) { return new WP_Error( 'requirements_not_met', "This theme does not work with your version of WordPress. Minimum WordPress requirement is $requires_wp" ); From c9852acca46203d55d0584f7971426374423987a Mon Sep 17 00:00:00 2001 From: Isla Waters Date: Sat, 7 Dec 2024 11:46:06 -0500 Subject: [PATCH 03/10] Only use major wp version in version_compare Similar to what would happen in is_wp_version_compatible() --- src/Plugin_Command.php | 6 +++++- src/Theme_Command.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 57d9f726..2164afcc 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -583,6 +583,10 @@ public function path( $args, $assoc_args ) { protected function install_from_repo( $slug, $assoc_args ) { global $wp_version; + // Extract the major WordPress version (e.g., "6.3") from the full version string + list($wp_core_version) = explode( '-', $wp_version ); + $wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) ); + $api = plugins_api( 'plugin_information', array( 'slug' => $slug ) ); if ( is_wp_error( $api ) ) { @@ -596,7 +600,7 @@ protected function install_from_repo( $slug, $assoc_args ) { $requires_wp = isset( $api->requires ) ? $api->requires : null; $compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' ); - $compatible_wp = empty( $requires_wp ) || version_compare( $wp_version, $requires_wp, '>=' ); + $compatible_wp = empty( $requires_wp ) || version_compare( $wp_core_version, $requires_wp, '>=' ); if ( ! $compatible_wp ) { return new WP_Error( 'requirements_not_met', "This plugin does not work with your version of WordPress. Minimum WordPress requirement is $requires_wp" ); diff --git a/src/Theme_Command.php b/src/Theme_Command.php index d0eb984a..74af0a01 100644 --- a/src/Theme_Command.php +++ b/src/Theme_Command.php @@ -396,6 +396,10 @@ public function path( $args, $assoc_args ) { protected function install_from_repo( $slug, $assoc_args ) { global $wp_version; + // Extract the major WordPress version (e.g., "6.3") from the full version string + list($wp_core_version) = explode( '-', $wp_version ); + $wp_core_version = implode( '.', array_slice( explode( '.', $wp_core_version ), 0, 2 ) ); + $api = themes_api( 'theme_information', array( 'slug' => $slug ) ); if ( is_wp_error( $api ) ) { @@ -409,7 +413,7 @@ protected function install_from_repo( $slug, $assoc_args ) { $requires_wp = isset( $api->requires ) ? $api->requires : null; $compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' ); - $compatible_wp = empty( $requires_wp ) || version_compare( $wp_version, $requires_wp, '>=' ); + $compatible_wp = empty( $requires_wp ) || version_compare( $wp_core_version, $requires_wp, '>=' ); if ( ! $compatible_wp ) { return new WP_Error( 'requirements_not_met', "This theme does not work with your version of WordPress. Minimum WordPress requirement is $requires_wp" ); From e64abdcab617c954f5df31c7341b55a889b91a7d Mon Sep 17 00:00:00 2001 From: Isla Waters Date: Sat, 7 Dec 2024 12:14:31 -0500 Subject: [PATCH 04/10] Adjust test requirements Try and only test these with supported wp versions on unsupported php --- features/plugin-install.feature | 2 +- features/theme-install.feature | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/plugin-install.feature b/features/plugin-install.feature index f9281cca..22fead21 100644 --- a/features/plugin-install.feature +++ b/features/plugin-install.feature @@ -255,7 +255,7 @@ Feature: Install WordPress plugins Error: No plugins installed. """ - @less-than-php-7.4 + @less-than-php-7.4 @require-wp-6.6 Scenario: Can't install plugin that requires a newer version of PHP Given a WP install diff --git a/features/theme-install.feature b/features/theme-install.feature index 2f4fece4..8f9523b3 100644 --- a/features/theme-install.feature +++ b/features/theme-install.feature @@ -143,7 +143,7 @@ Feature: Install WordPress themes Error: No themes installed. """ - @less-than-php-7.4 + @less-than-php-7.4 @require-wp-5.6 Scenario: Can't install theme that requires a newer version of PHP Given a WP install From fcae1a082c4a4fb41b67c2f5bcc75f29a3d454f3 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 11 Dec 2024 09:46:56 +0100 Subject: [PATCH 05/10] Require PHP 7 for tests installing WP 6.4 WP 6.4 requires PHP 7+ --- features/plugin-install.feature | 1 + features/theme-install.feature | 1 + 2 files changed, 2 insertions(+) diff --git a/features/plugin-install.feature b/features/plugin-install.feature index 22fead21..cb5ef09e 100644 --- a/features/plugin-install.feature +++ b/features/plugin-install.feature @@ -238,6 +238,7 @@ Feature: Install WordPress plugins Success: Plugin already installed. """ + @require-php-7 Scenario: Can't install plugin that requires a newer version of WordPress Given a WP install diff --git a/features/theme-install.feature b/features/theme-install.feature index 8f9523b3..867cb225 100644 --- a/features/theme-install.feature +++ b/features/theme-install.feature @@ -126,6 +126,7 @@ Feature: Install WordPress themes twentyeleven """ + @require-php-7 Scenario: Can't install theme that requires a newer version of WordPress Given a WP install From 99c73807720700c7475494b9e3ac32ecd4feaab5 Mon Sep 17 00:00:00 2001 From: Isla Waters Date: Thu, 12 Dec 2024 12:45:03 -0500 Subject: [PATCH 06/10] Add new --ignore-requirements arg Per code review, also offer --ignore-requirements as a way to install themes and plugins that don't meet the listed requirements --- src/Plugin_Command.php | 5 ++++- src/Theme_Command.php | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 2164afcc..dd5ee425 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -593,7 +593,7 @@ protected function install_from_repo( $slug, $assoc_args ) { return $api; } - if ( isset( $assoc_args['version'] ) ) { + if ( isset( $assoc_args['version'] ) || isset( $assoc_args['ignore-requirements'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); } else { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; @@ -915,6 +915,9 @@ protected function filter_item_list( $items, $args ) { * : If set, the command will overwrite any installed version of the plugin, without prompting * for confirmation. * + * [--ignore-requirements] + * :If set, the command will install the plugin while ignoring any requirements specified by the plugin authors. + * * [--activate] * : If set, the plugin will be activated immediately after install. * diff --git a/src/Theme_Command.php b/src/Theme_Command.php index 74af0a01..47109b74 100644 --- a/src/Theme_Command.php +++ b/src/Theme_Command.php @@ -406,7 +406,7 @@ protected function install_from_repo( $slug, $assoc_args ) { return $api; } - if ( isset( $assoc_args['version'] ) ) { + if ( isset( $assoc_args['version'] ) || isset( $assoc_args['ignore-requirements'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); } else { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; @@ -481,6 +481,9 @@ protected function filter_item_list( $items, $args ) { * : If set, the command will overwrite any installed version of the theme, without prompting * for confirmation. * + * [--ignore-requirements] + * : If set, the command will install the theme while ignoring any requirements specified by the theme authors. + * * [--activate] * : If set, the theme will be activated immediately after install. * From 11afbb3514abd09a6e08877e59f9b397387b390a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 13 Dec 2024 11:40:55 +0100 Subject: [PATCH 07/10] Update tests --- features/plugin-auto-updates-disable.feature | 2 +- features/plugin-auto-updates-status.feature | 2 +- features/plugin-install.feature | 2 +- features/plugin.feature | 8 +++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/features/plugin-auto-updates-disable.feature b/features/plugin-auto-updates-disable.feature index 662c85f6..4b375d34 100644 --- a/features/plugin-auto-updates-disable.feature +++ b/features/plugin-auto-updates-disable.feature @@ -2,7 +2,7 @@ Feature: Disable auto-updates for WordPress plugins Background: Given a WP install - And I run `wp plugin install duplicate-post` + And I run `wp plugin install duplicate-post --ignore-requirements` And I run `wp plugin auto-updates enable --all` @require-wp-5.5 diff --git a/features/plugin-auto-updates-status.feature b/features/plugin-auto-updates-status.feature index 76843d08..07539da6 100644 --- a/features/plugin-auto-updates-status.feature +++ b/features/plugin-auto-updates-status.feature @@ -2,7 +2,7 @@ Feature: Show the status of auto-updates for WordPress plugins Background: Given a WP install - And I run `wp plugin install duplicate-post` + And I run `wp plugin install duplicate-post --ignore-requirements` @require-wp-5.5 Scenario: Show an error if required params are missing diff --git a/features/plugin-install.feature b/features/plugin-install.feature index cb5ef09e..c8fabf61 100644 --- a/features/plugin-install.feature +++ b/features/plugin-install.feature @@ -171,7 +171,7 @@ Feature: Install WordPress plugins When I run `rm wp-content/plugins/akismet/akismet.php` Then the return code should be 0 - When I try `wp plugin install akismet` + When I try `wp plugin install akismet --ignore-requirements` Then STDERR should contain: """ Warning: Destination folder already exists. "{WORKING_DIR}/wp-content/plugins/akismet/" diff --git a/features/plugin.feature b/features/plugin.feature index 73f910dd..b513af6c 100644 --- a/features/plugin.feature +++ b/features/plugin.feature @@ -329,7 +329,7 @@ Feature: Manage WordPress plugins When I run `wp plugin activate akismet hello` Then STDOUT should not be empty - When I run `wp plugin install wordpress-importer` + When I run `wp plugin install wordpress-importer --ignore-requirements` Then STDOUT should not be empty When I run `wp plugin activate network-only` @@ -373,6 +373,8 @@ Feature: Manage WordPress plugins | name | status | update | | wordpress-importer | inactive | none | + # WordPress Importer requires WP 5.2. + @require-wp-5.2 Scenario: Install a plugin when directory doesn't yet exist Given a WP install @@ -448,6 +450,8 @@ Feature: Manage WordPress plugins must-use """ + # WordPress Importer requires WP 5.2. + @require-wp-5.2 Scenario: Deactivate and uninstall a plugin, part one Given a WP install And these installed and active plugins: @@ -472,6 +476,8 @@ Feature: Manage WordPress plugins And STDOUT should be empty And the return code should be 1 + # WordPress Importer requires WP 5.2. + @require-wp-5.2 Scenario: Deactivate and uninstall a plugin, part two Given a WP install And these installed and active plugins: From c6ca68b199a0e0a11d1d9c0ff371c61280082848 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 13 Dec 2024 11:47:04 +0100 Subject: [PATCH 08/10] Use `get_flag_value` --- src/Plugin_Command.php | 5 +++-- src/Theme_Command.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index dd5ee425..bb868ad0 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -593,7 +593,7 @@ protected function install_from_repo( $slug, $assoc_args ) { return $api; } - if ( isset( $assoc_args['version'] ) || isset( $assoc_args['ignore-requirements'] ) ) { + if ( isset( $assoc_args['version'] ) || Utils\get_flag_value( $assoc_args, 'ignore-requirements' ) ) { self::alter_api_response( $api, $assoc_args['version'] ); } else { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; @@ -916,7 +916,8 @@ protected function filter_item_list( $items, $args ) { * for confirmation. * * [--ignore-requirements] - * :If set, the command will install the plugin while ignoring any requirements specified by the plugin authors. + * :If set, the command will install the plugin while ignoring any WordPress or PHP version requirements + * specified by the plugin authors. * * [--activate] * : If set, the plugin will be activated immediately after install. diff --git a/src/Theme_Command.php b/src/Theme_Command.php index 47109b74..90f9224e 100644 --- a/src/Theme_Command.php +++ b/src/Theme_Command.php @@ -406,7 +406,7 @@ protected function install_from_repo( $slug, $assoc_args ) { return $api; } - if ( isset( $assoc_args['version'] ) || isset( $assoc_args['ignore-requirements'] ) ) { + if ( isset( $assoc_args['version'] ) || Utils\get_flag_value( $assoc_args, 'ignore-requirements' ) ) { self::alter_api_response( $api, $assoc_args['version'] ); } else { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; @@ -482,7 +482,8 @@ protected function filter_item_list( $items, $args ) { * for confirmation. * * [--ignore-requirements] - * : If set, the command will install the theme while ignoring any requirements specified by the theme authors. + * : If set, the command will install the theme while ignoring any WordPress or PHP version requirements + * specified by the theme authors. * * [--activate] * : If set, the theme will be activated immediately after install. From 713f7bb8dd040f1cbd9396960f355df3829b5790 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 13 Dec 2024 12:41:42 +0100 Subject: [PATCH 09/10] Move to elseif --- src/Plugin_Command.php | 4 ++-- src/Theme_Command.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index bb868ad0..379e6c57 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -593,9 +593,9 @@ protected function install_from_repo( $slug, $assoc_args ) { return $api; } - if ( isset( $assoc_args['version'] ) || Utils\get_flag_value( $assoc_args, 'ignore-requirements' ) ) { + if ( isset( $assoc_args['version'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); - } else { + } elseif ( ! Utils\get_flag_value( $assoc_args, 'ignore-requirements', false ) ) { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; $requires_wp = isset( $api->requires ) ? $api->requires : null; diff --git a/src/Theme_Command.php b/src/Theme_Command.php index 90f9224e..50806cca 100644 --- a/src/Theme_Command.php +++ b/src/Theme_Command.php @@ -406,9 +406,9 @@ protected function install_from_repo( $slug, $assoc_args ) { return $api; } - if ( isset( $assoc_args['version'] ) || Utils\get_flag_value( $assoc_args, 'ignore-requirements' ) ) { + if ( isset( $assoc_args['version'] ) ) { self::alter_api_response( $api, $assoc_args['version'] ); - } else { + } elseif ( ! Utils\get_flag_value( $assoc_args, 'ignore-requirements', false ) ) { $requires_php = isset( $api->requires_php ) ? $api->requires_php : null; $requires_wp = isset( $api->requires ) ? $api->requires : null; From 8d9edf4b47fa2e4f67d4e326ea5217b8c442da21 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 13 Dec 2024 13:04:45 +0100 Subject: [PATCH 10/10] Ignore requirements in test --- features/plugin-auto-updates-enable.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/plugin-auto-updates-enable.feature b/features/plugin-auto-updates-enable.feature index 8646136a..9450576f 100644 --- a/features/plugin-auto-updates-enable.feature +++ b/features/plugin-auto-updates-enable.feature @@ -2,7 +2,7 @@ Feature: Enable auto-updates for WordPress plugins Background: Given a WP install - And I run `wp plugin install duplicate-post` + And I run `wp plugin install duplicate-post --ignore-requirements` @require-wp-5.5 Scenario: Show an error if required params are missing