diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js index 22ac0bdd1..9c2a3f13f 100644 --- a/assets/js/plugin-check-admin.js +++ b/assets/js/plugin-check-admin.js @@ -12,7 +12,9 @@ e.preventDefault(); getChecksToRun() + .then( setUpEnvironment ) .then( runChecks ) + .then( cleanUpEnvironment ) .then( ( data ) => { console.log( data.message ); @@ -25,17 +27,95 @@ ); } ); + /** + * Setup the runtime environment if needed. + * + * @since n.e.x.t + */ + function setUpEnvironment( data ) { + const pluginCheckData = new FormData(); + pluginCheckData.append( 'nonce', pluginCheck.nonce ); + pluginCheckData.append( 'plugin', data.plugin ); + pluginCheckData.append( 'action', 'plugin_check_set_up_environment' ); + + for (var i = 0; i < data.checks.length; i++) { + pluginCheckData.append( 'checks[]', data.checks[ i ] ); + } + + return fetch( + ajaxurl, + { + method: 'POST', + credentials: 'same-origin', + body: pluginCheckData + } + ) + .then( + ( response ) => { + return response.json(); + } + ) + .then( handleDataErrors ) + .then( + ( data ) => { + if ( ! data.data || ! data.data.message ) { + throw new Error( 'Response contains no data.' ); + } + + console.log( data.data.message ); + + return data.data; + } + ); + } + + /** + * Cleanup the runtime environment. + * + * @since n.e.x.t + */ + function cleanUpEnvironment( data ) { + const pluginCheckData = new FormData(); + pluginCheckData.append( 'nonce', pluginCheck.nonce ); + pluginCheckData.append( 'action', 'plugin_check_clean_up_environment' ); + + return fetch( + ajaxurl, + { + method: 'POST', + credentials: 'same-origin', + body: pluginCheckData + } + ) + .then( + ( response ) => { + return response.json(); + } + ) + .then( handleDataErrors ) + .then( + ( data ) => { + if ( ! data.data || ! data.data.message ) { + throw new Error( 'Response contains no data.' ); + } + + console.log( data.data.message ); + + return data.data; + } + ); + } + + /** * Get the Checks to run. * * @since n.e.x.t */ function getChecksToRun() { - // Collect the data to pass along for generating a check results. const pluginCheckData = new FormData(); pluginCheckData.append( 'nonce', pluginCheck.nonce ); pluginCheckData.append( 'plugin', pluginsList.value ); - pluginCheckData.append( 'checks', [] ); pluginCheckData.append( 'action', 'plugin_check_get_checks_to_run' ); return fetch( @@ -73,9 +153,12 @@ const pluginCheckData = new FormData(); pluginCheckData.append( 'nonce', pluginCheck.nonce ); pluginCheckData.append( 'plugin', data.plugin ); - pluginCheckData.append( 'checks', data.checks ); pluginCheckData.append( 'action', 'plugin_check_run_checks' ); + for (var i = 0; i < data.checks.length; i++) { + pluginCheckData.append( 'checks[]', data.checks[ i ] ); + } + return fetch( ajaxurl, { diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php index a9b537ab8..1299c6973 100644 --- a/includes/Admin/Admin_AJAX.php +++ b/includes/Admin/Admin_AJAX.php @@ -11,7 +11,9 @@ use Exception; use WordPress\Plugin_Check\Checker\AJAX_Runner; use WordPress\Plugin_Check\Checker\Runtime_Check; +use WordPress\Plugin_Check\Checker\Runtime_Environment_Setup; use WordPress\Plugin_Check\Utilities\Plugin_Request_Utility; + /** * Class to handle the Admin AJAX requests. * @@ -33,6 +35,8 @@ class Admin_AJAX { * @since n.e.x.t */ public function add_hooks() { + add_action( 'wp_ajax_plugin_check_clean_up_environment', array( $this, 'clean_up_environment' ) ); + add_action( 'wp_ajax_plugin_check_set_up_environment', array( $this, 'set_up_environment' ) ); add_action( 'wp_ajax_plugin_check_get_checks_to_run', array( $this, 'get_checks_to_run' ) ); add_action( 'wp_ajax_plugin_check_run_checks', array( $this, 'run_checks' ) ); } @@ -46,6 +50,100 @@ public function get_nonce() { return wp_create_nonce( self::NONCE_KEY ); } + /** + * Handles the AJAX request to setup the runtime environment if needed. + * + * @since n.e.x.t + */ + public function set_up_environment() { + // Verify the nonce before continuing. + $valid_nonce = $this->verify_nonce( filter_input( INPUT_POST, 'nonce', FILTER_SANITIZE_STRING ) ); + + if ( is_wp_error( $valid_nonce ) ) { + wp_send_json_error( $valid_nonce, 403 ); + } + $runner = Plugin_Request_Utility::get_runner(); + + if ( is_null( $runner ) ) { + $runner = new AJAX_Runner(); + } + + // Make sure we are using the correct runner instance. + if ( ! ( $runner instanceof AJAX_Runner ) ) { + wp_send_json_error( + new WP_Error( 'invalid-runner', __( 'AJAX Runner was not initialized correctly.', 'plugin-check' ) ), + 500 + ); + } + + $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY ); + $plugin = filter_input( INPUT_POST, 'plugin', FILTER_SANITIZE_STRING ); + + try { + $runner->set_check_slugs( $checks ); + $runner->set_plugin( $plugin ); + $checks_to_run = $runner->get_checks_to_run(); + } catch ( Exception $error ) { + wp_send_json_error( + new WP_Error( 'invalid-request', $error->getMessage() ), + 400 + ); + } + + $message = __( 'No runtime checks, runtime environment was not setup.', 'plugin-check' ); + + if ( $this->has_runtime_check( $checks_to_run ) ) { + $runtime = new Runtime_Environment_Setup(); + $runtime->setup(); + $message = __( 'Runtime environment setup successful.', 'plugin-check' ); + } + + wp_send_json_success( + array( + 'message' => $message, + 'plugin' => $plugin, + 'checks' => $checks, + ) + ); + } + + /** + * Handles the AJAX request to cleanup the runtime environment. + * + * @since n.e.x.t + */ + public function clean_up_environment() { + global $wpdb; + + // Verify the nonce before continuing. + $valid_nonce = $this->verify_nonce( filter_input( INPUT_POST, 'nonce', FILTER_SANITIZE_STRING ) ); + + if ( is_wp_error( $valid_nonce ) ) { + wp_send_json_error( $valid_nonce, 403 ); + } + + // Set the new prefix. + $old_prefix = $wpdb->set_prefix( 'wppc_' ); + + $message = __( 'Runtime environment was not prepared, cleanup was not run.', 'plugin-check' ); + + // Test if the runtime environment tables exist. + if ( 'wppc_posts' === $wpdb->get_var( "SHOW TABLES LIKE 'wppc_posts'" ) || defined( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION' ) ) { + $runtime = new Runtime_Environment_Setup(); + $runtime->cleanup(); + $message = __( 'Runtime environment cleanup successful.', 'plugin-check' ); + } + + // Restore the old prefix. + $wpdb->set_prefix( $old_prefix ); + + wp_send_json_success( + array( + 'message' => $message, + ) + ); + } + /** * Handles the AJAX request that returns the checks to run. * @@ -59,9 +157,9 @@ public function get_checks_to_run() { wp_send_json_error( $valid_nonce, 403 ); } - $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); + $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY ); + $checks = is_null( $checks ) ? array() : $checks; $plugin = filter_input( INPUT_POST, 'plugin', FILTER_SANITIZE_STRING ); - $runner = Plugin_Request_Utility::get_runner(); if ( is_null( $runner ) ) { diff --git a/includes/Checker/AJAX_Runner.php b/includes/Checker/AJAX_Runner.php index 77409b324..7ebd3b8fe 100644 --- a/includes/Checker/AJAX_Runner.php +++ b/includes/Checker/AJAX_Runner.php @@ -70,13 +70,6 @@ protected function get_plugin_param() { * @return array An array of Check slugs to run. */ protected function get_check_slugs_param() { - $checks = array(); - - if ( isset( $_REQUEST['checks'] ) ) { - // Checks are passed as a comma separated string. - $checks = wp_parse_list( $_REQUEST['checks'] ); - } - - return $checks; + return filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY ); } } diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php index d2b46c662..6c9cea18c 100644 --- a/includes/Checker/Abstract_Check_Runner.php +++ b/includes/Checker/Abstract_Check_Runner.php @@ -195,12 +195,12 @@ public function run() { } /** - * Determines if any of the checks contains at least one runtime check. + * Determines if any of the checks are a runtime check. * * @since n.e.x.t * * @param array $checks An array of check instances to run. - * @return bool Returns true if one or more checks contains at least one runtime check. + * @return bool Returns true if one or more checks is a runtime check. */ protected function has_runtime_check( array $checks ) { foreach ( $checks as $check ) { diff --git a/includes/Checker/Preparations/Force_Single_Plugin_Preparation.php b/includes/Checker/Preparations/Force_Single_Plugin_Preparation.php index 5041b283d..dbc50fc5d 100644 --- a/includes/Checker/Preparations/Force_Single_Plugin_Preparation.php +++ b/includes/Checker/Preparations/Force_Single_Plugin_Preparation.php @@ -82,7 +82,14 @@ public function prepare() { public function filter_active_plugins( $active_plugins ) { if ( is_array( $active_plugins ) && in_array( $this->plugin_basename, $active_plugins, true ) ) { - $plugin_base_file = plugin_basename( WP_PLUGIN_CHECK_MAIN_FILE ); + if ( defined( 'WP_PLUGIN_CHECK_MAIN_FILE' ) ) { + $plugin_check_file = WP_PLUGIN_CHECK_MAIN_FILE; + } else { + $plugins_dir = defined( 'WP_PLUGIN_DIR' ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins'; + $plugin_check_file = $plugins_dir . '/plugin-check/plugin-check.php'; + } + + $plugin_base_file = plugin_basename( $plugin_check_file ); // If the plugin-check is the only available plugin then return that one only. if ( $this->plugin_basename === $plugin_base_file ) { diff --git a/includes/Checker/Preparations/Universal_Runtime_Preparation.php b/includes/Checker/Preparations/Universal_Runtime_Preparation.php index cd4ab8793..633cc93fd 100644 --- a/includes/Checker/Preparations/Universal_Runtime_Preparation.php +++ b/includes/Checker/Preparations/Universal_Runtime_Preparation.php @@ -54,7 +54,14 @@ public function prepare() { $cleanup_functions = array(); - $use_minimal_theme_preparation = new Use_Minimal_Theme_Preparation( 'wp-empty-theme', WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . '/test-content/themes' ); + if ( ! defined( 'WP_PLUGIN_CHECK_PLUGIN_DIR_PATH' ) ) { + $plugins_dir = defined( 'WP_PLUGIN_DIR' ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins'; + $theme_folder = $plugins_dir . '/plugin-check/test-content/themes'; + } else { + $theme_folder = WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'test-content/themes'; + } + + $use_minimal_theme_preparation = new Use_Minimal_Theme_Preparation( 'wp-empty-theme', $theme_folder ); $cleanup_functions[] = $use_minimal_theme_preparation->prepare(); $force_single_plugin_preparation = new Force_Single_Plugin_Preparation( $this->check_context->basename() ); diff --git a/includes/Utilities/Plugin_Request_Utility.php b/includes/Utilities/Plugin_Request_Utility.php index 0949f27ff..06c4f04a3 100644 --- a/includes/Utilities/Plugin_Request_Utility.php +++ b/includes/Utilities/Plugin_Request_Utility.php @@ -93,9 +93,14 @@ public static function initialize_runner() { foreach ( $runners as $runner ) { if ( $runner->is_plugin_check() ) { - // @TODO: Handle the cleanup function in later issue with shutdown action or method that returns cleanup functions. - static::$cleanup = $runner->prepare(); - static::$runner = $runner; + add_action( + 'muplugins_loaded', + function() use ( $runner ) { + static::$cleanup = $runner->prepare(); + static::$runner = $runner; + } + ); + break; } } diff --git a/tests/Checker/AJAX_Runner_Tests.php b/tests/Checker/AJAX_Runner_Tests.php index 4e4f75bb9..f4bd41bcb 100644 --- a/tests/Checker/AJAX_Runner_Tests.php +++ b/tests/Checker/AJAX_Runner_Tests.php @@ -52,7 +52,7 @@ public function test_prepare_with_runtime_check() { add_filter( 'wp_doing_ajax', '__return_true' ); $_REQUEST['action'] = 'plugin_check_run_checks'; $_REQUEST['plugin'] = 'plugin-check'; - $_REQUEST['checks'] = 'runtime_check'; + $_REQUEST['checks'] = array( 'runtime_check' ); add_filter( 'wp_plugin_check_checks', @@ -89,7 +89,7 @@ public function test_prepare_with_static_check() { add_filter( 'wp_doing_ajax', '__return_true' ); $_REQUEST['action'] = 'plugin_check_run_checks'; $_REQUEST['plugin'] = 'plugin-check'; - $_REQUEST['checks'] = 'empty_check'; + $_REQUEST['checks'] = array( 'empty_check' ); add_filter( 'wp_plugin_check_checks', @@ -121,7 +121,7 @@ public function test_run() { add_filter( 'wp_doing_ajax', '__return_true' ); $_REQUEST['action'] = 'plugin_check_run_checks'; $_REQUEST['plugin'] = 'plugin-check'; - $_REQUEST['checks'] = 'empty_check'; + $_REQUEST['checks'] = array( 'empty_check' ); add_filter( 'wp_plugin_check_checks', @@ -145,7 +145,7 @@ public function test_run_with_errors() { add_filter( 'wp_doing_ajax', '__return_true' ); $_REQUEST['action'] = 'plugin_check_run_checks'; $_REQUEST['plugin'] = 'plugin-check'; - $_REQUEST['checks'] = 'error_check'; + $_REQUEST['checks'] = array( 'error_check' ); add_filter( 'wp_plugin_check_checks', @@ -174,7 +174,7 @@ public function test_runner_initialized_early_throws_plugin_basename_exception() add_filter( 'wp_doing_ajax', '__return_true' ); $_REQUEST['action'] = 'plugin_check_run_checks'; $_REQUEST['plugin'] = 'plugin-check'; - $_REQUEST['checks'] = 'empty_check'; + $_REQUEST['checks'] = array( 'empty_check' ); $muplugins_loaded = $wp_actions['muplugins_loaded']; unset( $wp_actions['muplugins_loaded'] ); @@ -198,7 +198,7 @@ public function test_runner_initialized_early_throws_checks_exception() { add_filter( 'wp_doing_ajax', '__return_true' ); $_REQUEST['action'] = 'plugin_check_run_checks'; $_REQUEST['plugin'] = 'plugin-check'; - $_REQUEST['checks'] = 'empty_check'; + $_REQUEST['checks'] = array( 'empty_check' ); $muplugins_loaded = $wp_actions['muplugins_loaded']; unset( $wp_actions['muplugins_loaded'] ); diff --git a/tests/Utilities/Plugin_Request_Utility_Tests.php b/tests/Utilities/Plugin_Request_Utility_Tests.php index c790a4d38..6ef368959 100644 --- a/tests/Utilities/Plugin_Request_Utility_Tests.php +++ b/tests/Utilities/Plugin_Request_Utility_Tests.php @@ -47,6 +47,9 @@ public function test_initialize_runner_with_cli() { ); Plugin_Request_Utility::initialize_runner(); + + do_action( 'muplugins_loaded' ); + $runner = Plugin_Request_Utility::get_runner(); unset( $_SERVER['argv'] ); @@ -60,6 +63,9 @@ public function test_initialize_runner_with_ajax() { $_REQUEST['plugin'] = 'plugin-check'; Plugin_Request_Utility::initialize_runner(); + + do_action( 'muplugins_loaded' ); + $runner = Plugin_Request_Utility::get_runner(); $this->assertInstanceOf( AJAX_Runner::class, $runner ); @@ -90,6 +96,8 @@ function( $checks ) { Plugin_Request_Utility::initialize_runner(); + do_action( 'muplugins_loaded' ); + // Determine if one of the Universal_Runtume_Preparation was run. $prepared = has_filter( 'option_active_plugins' ); @@ -114,7 +122,7 @@ public function test_destroy_runner_with_ajax() { add_filter( 'wp_doing_ajax', '__return_true' ); $_REQUEST['action'] = 'plugin_check_run_checks'; $_REQUEST['plugin'] = 'plugin-check'; - $_REQUEST['checks'] = 'runtime_check'; + $_REQUEST['checks'] = array( 'runtime_check' ); add_filter( 'wp_plugin_check_checks', @@ -130,6 +138,8 @@ function( $checks ) { Plugin_Request_Utility::initialize_runner(); + do_action( 'muplugins_loaded' ); + // Determine if one of the Universal_Runtume_Preparation was run. $prepared = has_filter( 'option_active_plugins' );