diff --git a/src/Basic_Requirement_Checker.php b/src/Basic_Requirement_Checker.php index 6de82bf3692c243d10aa2ba5bd44ee414e86f1ea..942fe46f091473dec4ecc3996af141e441333161 100644 --- a/src/Basic_Requirement_Checker.php +++ b/src/Basic_Requirement_Checker.php @@ -1,6 +1,6 @@ <?php -if (!class_exists('WPDesk_Translable')) { +if ( ! class_exists( 'WPDesk_Translable' ) ) { require_once 'Translable.php'; } @@ -9,38 +9,31 @@ if (!class_exists('WPDesk_Translable')) { * have to be compatible with PHP 5.2.x */ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { + const EXTENSION_NAME_OPENSSL = 'openssl'; + const HOOK_ADMIN_NOTICES_ACTION = 'admin_notices'; /** @var string */ private $plugin_name = ''; - /** @var string */ private $plugin_file = ''; - /** @var string */ private $min_php_version; - /** @var string */ private $min_wp_version; - - /** @var string */ - private $min_wc_version; - + /** @var string|null */ + private $min_wc_version = null; + /** @var int|null */ + private $min_openssl_version = null; /** @var array */ private $plugin_require; - /** @var array */ private $module_require; - /** @var array */ private $setting_require; - /** @var array */ private $notices; - /** @var @string */ private $text_domain; - const EXTENSION_NAME_OPENSSL = 'openssl'; - /** * @param string $plugin_file * @param string $plugin_name @@ -56,14 +49,10 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { $this->set_min_php_require( $php_version ); $this->set_min_wp_require( $wp_version ); - $this->plugin_require = array(); - $this->module_require = array(); - $this->setting_require = array(); - $this->notices = array(); - } - - public function get_text_domain() { - return $this->text_domain; + $this->plugin_require = []; + $this->module_require = []; + $this->setting_require = []; + $this->notices = []; } /** @@ -99,6 +88,17 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { return $this; } + /** + * @param $version + * + * @return $this + */ + public function set_min_openssl_require( $version ) { + $this->min_openssl_version = $version; + + return $this; + } + /** * @param string $plugin_name * @param string $nice_plugin_name Nice plugin name for better looks in notice @@ -156,7 +156,7 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { * @return array */ private function prepare_requirement_notices() { - $notices = array(); + $notices = []; if ( ! $this->is_php_at_least( $this->min_php_version ) ) { $notices[] = $this->prepare_notice_message( sprintf( __( 'The “%s” plugin cannot run on PHP versions older than %s. Please contact your host and ask them to upgrade.', $this->get_text_domain() ), esc_html( $this->plugin_name ), $this->min_php_version ) ); @@ -166,8 +166,14 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { $this->get_text_domain() ), esc_html( $this->plugin_name ), $this->min_wp_version ) ); } if ( ! is_null( $this->min_wc_version ) && $this->can_check_plugin_version() && ! $this->is_wc_at_least( $this->min_wc_version ) ) { - $notices[] = $this->prepare_notice_message( sprintf( __( 'The “%s” plugin cannot run on WooCommerce versions older than %s. Please update WooCommerce.', $this->get_text_domain() ), esc_html( $this->plugin_name ), $this->min_wc_version ) ); + $notices[] = $this->prepare_notice_message( sprintf( __( 'The “%s” plugin cannot run on WooCommerce versions older than %s. Please update WooCommerce.', + $this->get_text_domain() ), esc_html( $this->plugin_name ), $this->min_wc_version ) ); } + if ( ! is_null( $this->min_openssl_version ) && ! $this->is_open_ssl_at_least( $this->min_openssl_version ) ) { + $notices[] = $this->prepare_notice_message( sprintf( __( 'The “%s” plugin cannot run without OpenSSL module version at least %s. Please update OpenSSL module.', + $this->get_text_domain() ), esc_html( $this->plugin_name ), '0x' . dechex( $this->min_openssl_version ) ) ); + } + $notices = $this->append_plugin_require_notices( $notices ); $notices = $this->append_module_require_notices( $notices ); $notices = $this->append_settings_require_notices( $notices ); @@ -175,13 +181,46 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { return $notices; } + /** + * @param $min_version + * + * @return mixed + */ + public static function is_php_at_least( $min_version ) { + return version_compare( phpversion(), $min_version, '>=' ); + } + + /** + * Prepares message in html format + * + * @param string $message + * + * @return string + */ + private function prepare_notice_message( $message ) { + return '<div class="error"><p>' . $message . '</p></div>'; + } + + public function get_text_domain() { + return $this->text_domain; + } + + /** + * @param string $min_version + * + * @return bool + */ + public static function is_wp_at_least( $min_version ) { + return version_compare( get_bloginfo( 'version' ), $min_version, '>=' ); + } + /** * Are plugins loaded so we can check the version * * @return bool */ private function can_check_plugin_version() { - return did_action('plugins_loaded') > 0; + return did_action( 'plugins_loaded' ) > 0; } /** @@ -192,10 +231,59 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { * @return bool */ public static function is_wc_at_least( $min_version ) { - return defined('WC_VERSION') && + return defined( 'WC_VERSION' ) && version_compare( WC_VERSION, $min_version, '>=' ); } + /** + * Checks if ssl version is valid + * + * @param int $required_version Version in hex. Version 9.6 is 0x000906000 + * + * @see https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_VERSION_NUMBER.html + * + * @return bool + */ + public static function is_open_ssl_at_least( $required_version ) { + return defined( 'OPENSSL_VERSION_NUMBER' ) && OPENSSL_VERSION_NUMBER > (int) $required_version; + } + + /** + * @param array $notices + * + * @return array + */ + private function append_plugin_require_notices( $notices ) { + if ( count( $this->plugin_require ) > 0 ) { + foreach ( $this->plugin_require as $plugin_name => $nice_plugin_name ) { + if ( ! $this->is_wp_plugin_active( $plugin_name ) ) { + $notices[] = $this->prepare_notice_message( sprintf( __( 'The “%s” plugin cannot run without %s active. Please install and activate %s plugin.', + $this->get_text_domain() ), esc_html( $this->plugin_name ), + esc_html( basename( $nice_plugin_name ) ), esc_html( basename( $nice_plugin_name ) ) ) ); + } + } + } + + return $notices; + } + + /** + * Checks if plugin is active. Needs to be enabled in deferred way. + * + * @param string $plugin_file + * + * @return bool + */ + public static function is_wp_plugin_active( $plugin_file ) { + $active_plugins = (array) get_option( 'active_plugins', [] ); + + if ( is_multisite() ) { + $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', [] ) ); + } + + return in_array( $plugin_file, $active_plugins ) || array_key_exists( $plugin_file, $active_plugins ); + } + /** * @param array $notices * @@ -215,6 +303,15 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { return $notices; } + /** + * @param string $name + * + * @return bool + */ + public static function is_module_active( $name ) { + return extension_loaded( $name ); + } + /** * @param array $notices * @@ -234,25 +331,6 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { return $notices; } - /** - * @param array $notices - * - * @return array - */ - private function append_plugin_require_notices( $notices ) { - if ( count( $this->plugin_require ) > 0 ) { - foreach ( $this->plugin_require as $plugin_name => $nice_plugin_name ) { - if ( ! $this->is_wp_plugin_active( $plugin_name ) ) { - $notices[] = $this->prepare_notice_message( sprintf( __( 'The “%s” plugin cannot run without %s active. Please install and activate %s plugin.', - $this->get_text_domain() ), esc_html( $this->plugin_name ), - esc_html( basename( $nice_plugin_name ) ), esc_html( basename( $nice_plugin_name ) ) ) ); - } - } - } - - return $notices; - } - /** * @param string $name * @param mixed $value @@ -263,21 +341,12 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { return ini_get( $name ) === strval( $value ); } - /** - * @param string $name - * - * @return bool - */ - public static function is_module_active( $name ) { - return extension_loaded( $name ); - } - /** * @return void */ public function disable_plugin_render_notice() { - add_action( 'admin_notices', array( $this, 'deactivate_action' ) ); - add_action( 'admin_notices', array( $this, 'render_notices_action' ) ); + add_action( self::HOOK_ADMIN_NOTICES_ACTION, [ $this, 'deactivate_action' ] ); + add_action( self::HOOK_ADMIN_NOTICES_ACTION, [ $this, 'render_notices_action' ] ); } /** @@ -291,17 +360,6 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { } } - /** - * Prepares message in html format - * - * @param string $message - * - * @return string - */ - private function prepare_notice_message( $message ) { - return '<div class="error"><p>' . $message . '</p></div>'; - } - /** * @return void */ @@ -311,51 +369,4 @@ class WPDesk_Basic_Requirement_Checker implements WPDesk_Translable { } } - /** - * @param $min_version - * - * @return mixed - */ - public static function is_php_at_least( $min_version ) { - return version_compare( phpversion(), $min_version, '>=' ); - } - - /** - * @param string $min_version - * - * @return bool - */ - public static function is_wp_at_least( $min_version ) { - return version_compare( get_bloginfo( 'version' ), $min_version, '>=' ); - } - - /** - * Checks if plugin is active. Needs to be enabled in deferred way. - * - * @param string $plugin_file - * - * @return bool - */ - public static function is_wp_plugin_active( $plugin_file ) { - $active_plugins = (array) get_option( 'active_plugins', array() ); - - if ( is_multisite() ) { - $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) ); - } - - return in_array( $plugin_file, $active_plugins ) || array_key_exists( $plugin_file, $active_plugins ); - } - - /** - * Checks if ssl version is valid - * - * @param int $required_version Version in hex. Version 9.6 is 0x000906000 - * @see https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_VERSION_NUMBER.html - * - * @return bool - */ - public static function is_open_ssl_at_least( $required_version ) { - return defined( 'OPENSSL_VERSION_NUMBER' ) && OPENSSL_VERSION_NUMBER > $required_version; - } - } diff --git a/tests/unit/Test_Basic_Requirement_Checker.php b/tests/unit/Test_Basic_Requirement_Checker.php index b2b66e39bb8413f213fbf69dfbeda543c8b9e32c..f7c2be96650ea921ece4cf39d9b9a398302bc544 100644 --- a/tests/unit/Test_Basic_Requirement_Checker.php +++ b/tests/unit/Test_Basic_Requirement_Checker.php @@ -9,8 +9,12 @@ class Test_Basic_Requirement_Checker extends PHPUnit\Framework\TestCase { const ALWAYS_VALID_PHP_VERSION = '5.2'; + const ALWAYS_NOT_VALID_PHP_VERSION = '100.100'; + const ALWAYS_VALID_WP_VERSION = '4.0'; + const HOOK_TYPE_ACTION = 'action'; + public function setUp() { WP_Mock::setUp(); @@ -22,17 +26,6 @@ class Test_Basic_Requirement_Checker extends PHPUnit\Framework\TestCase { WP_Mock::tearDown(); } - /** - * @param string $php - * @param string $wp - * - * @return WPDesk_Basic_Requirement_Checker - */ - public function create_requirements_for_php_wp( $php, $wp ) { - return new WPDesk_Basic_Requirement_Checker( self::RANDOM_PLUGIN_FILE, self::RANDOM_PLUGIN_NAME, - self::RANDOM_PLUGIN_TEXTDOMAIN, $php, $wp ); - } - public function test_php_version_check() { $known_PHP_versions = [ '7.3', '7.2', '7.1', '7.0', '5.6', '5.5', '5.4', '5.3', '5.2' ]; @@ -49,8 +42,22 @@ class Test_Basic_Requirement_Checker extends PHPUnit\Framework\TestCase { $this->assertFalse( $requirements->are_requirements_met(), 'Should fail because required PHP should be at least ' . $version ); } - } + $requirements->set_min_php_require( self::ALWAYS_NOT_VALID_PHP_VERSION ); + $requirements->are_requirements_met(); + $this->expectOutputRegex( "/PHP/" ); + $requirements->render_notices_action(); + } + + /** + * @param string $php + * @param string $wp + * + * @return WPDesk_Basic_Requirement_Checker + */ + public function create_requirements_for_php_wp( $php, $wp ) { + return new WPDesk_Basic_Requirement_Checker( self::RANDOM_PLUGIN_FILE, self::RANDOM_PLUGIN_NAME, + self::RANDOM_PLUGIN_TEXTDOMAIN, $php, $wp ); } public function test_wp_version_check() { @@ -64,6 +71,9 @@ class Test_Basic_Requirement_Checker extends PHPUnit\Framework\TestCase { $requirements->set_min_wp_require( $wp_version_fail ); $this->assertFalse( $requirements->are_requirements_met(), 'Should fail because required WP should be at least ' . $wp_version_fail ); + + $this->expectOutputRegex( "/WordPress/" ); + $requirements->render_notices_action(); } /** @@ -76,13 +86,16 @@ class Test_Basic_Requirement_Checker extends PHPUnit\Framework\TestCase { $requirements->add_php_module_require( 'curl' ); $this->assertTrue( $requirements->are_requirements_met(), 'Curl should exists' ); + + $this->expectOutputRegex( "/^$/" ); + $requirements->render_notices_action(); } public function test_plugin_check_with_multisite() { $multisite = true; $exising_plugin_name = 'WooCommerce'; $exising_multisite_plugin_name = 'Multisite'; - $not_existing_plugin_name = 'Whatever'; + $not_existing_plugin_name = 'Not exist'; WP_Mock::wpFunction( 'get_option' ) ->withArgs( [ 'active_plugins', [] ] ) @@ -107,5 +120,57 @@ class Test_Basic_Requirement_Checker extends PHPUnit\Framework\TestCase { $requirements->add_plugin_require( $not_existing_plugin_name ); $this->assertFalse( $requirements->are_requirements_met(), 'Plugin should not exists' ); + + $this->expectOutputRegex( "/$not_existing_plugin_name/" ); + $requirements->render_notices_action(); + } + + /** + * @requires extension openssl + */ + public function test_existing_openssl_requirement() { + $open_ssl_always_valid = 1; + $open_ssl_always_not_valid = 0x900905000; // 9.9.6 + + $requirements = $this->create_requirements_for_php_wp( self::ALWAYS_VALID_PHP_VERSION, + self::ALWAYS_VALID_WP_VERSION ); + + $this->assertTrue( $requirements->is_open_ssl_at_least( $open_ssl_always_valid ), + 'OpenSSL should have at least 0.1 version if exists' ); + + $this->assertFalse( $requirements->is_open_ssl_at_least( $open_ssl_always_not_valid ), + 'OpenSSL should fail for that high number' ); + + $requirements->set_min_openssl_require( $open_ssl_always_not_valid ); + + $this->assertFalse( $requirements->are_requirements_met(), + 'Requirement OpenSSL should fail for that high number' ); + + $this->expectOutputRegex( '/without OpenSSL module/' ); + $requirements->render_notices_action(); + } + + public function test_deactivate_plugin_notice() { + $requirements = $this->create_requirements_for_php_wp( self::ALWAYS_NOT_VALID_PHP_VERSION, + self::ALWAYS_VALID_WP_VERSION ); + + WP_Mock::expectActionAdded( WPDesk_Basic_Requirement_Checker::HOOK_ADMIN_NOTICES_ACTION, + [ $requirements, 'deactivate_action' ] ); + WP_Mock::expectActionAdded( WPDesk_Basic_Requirement_Checker::HOOK_ADMIN_NOTICES_ACTION, + [ $requirements, 'render_notices_action' ] ); + + $this->assertFalse( $requirements->are_requirements_met() ); + $requirements->disable_plugin_render_notice(); + + WP_Mock::wpFunction( 'deactivate_plugins' ) + ->once(); + + WP_Mock::wpFunction( 'plugin_basename' ) + ->once() + ->andReturn( 'whatever' ); + + $this->expectOutputRegex( '/cannot run on PHP/' ); + $requirements->deactivate_action(); + $requirements->render_notices_action(); } -} \ No newline at end of file +}