diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php index b1762344a..b09d27a86 100644 --- a/includes/Checker/Abstract_Check_Runner.php +++ b/includes/Checker/Abstract_Check_Runner.php @@ -55,7 +55,17 @@ abstract protected function get_check_slugs_to_run(); public function prepare() { if ( $this->requires_universal_preparations( $this->get_checks_to_run() ) ) { $preparation = new Universal_Runtime_Preparation( $this->get_checks_instance()->context() ); - return $preparation->prepare(); + $cleanup = $preparation->prepare(); + + // Set the database prefix to use the demo tables. + global $wpdb; + $old_prefix = $wpdb->set_prefix( 'wppc_' ); + + return function() use ( $old_prefix, $cleanup ) { + global $wpdb; + $wpdb->set_prefix( $old_prefix ); + $cleanup(); + }; } return function() {}; diff --git a/includes/Checker/Runtime_Environment_Setup.php b/includes/Checker/Runtime_Environment_Setup.php new file mode 100644 index 000000000..78acb4dc1 --- /dev/null +++ b/includes/Checker/Runtime_Environment_Setup.php @@ -0,0 +1,97 @@ +set_prefix( 'wppc_' ); + + // Create and populate the test database tables if they do not exist. + if ( 'wppc_posts' !== $wpdb->get_var( "SHOW TABLES LIKE 'wppc_posts'" ) ) { + wp_install( + 'Plugin Check', + 'plugincheck', + 'demo@plugincheck.test', + false + ); + } + + // Restore the old prefix. + $wpdb->set_prefix( $old_prefix ); + + // Return early if the plugin check object cache already exists. + if ( defined( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION' ) && WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION ) { + return; + } + + // Create the object-cache.php file. + if ( $wp_filesystem || WP_Filesystem() ) { + // Do not replace the object-cache.php file if it already exists. + if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) { + $wp_filesystem->copy( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'object-cache.copy.php', WP_CONTENT_DIR . '/object-cache.php' ); + } + } + } + + /** + * Cleans up the runtime environment setup. + * + * @since n.e.x.t + */ + public function cleanup() { + global $wpdb, $wp_filesystem; + + require_once ABSPATH . '/wp-admin/includes/upgrade.php'; + + $old_prefix = $wpdb->set_prefix( 'wppc_' ); + $tables = $wpdb->tables(); + + foreach ( $tables as $table ) { + $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + } + + // Restore the old prefix. + $wpdb->set_prefix( $old_prefix ); + + // Return early if the plugin check object cache does not exist. + if ( ! defined( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION' ) || ! WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION ) { + return; + } + + // Remove the object-cache.php file. + if ( $wp_filesystem || WP_Filesystem() ) { + if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) { + return; + } + + // Check the drop-in file matches the copy. + $original_content = $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ); + $copy_content = $wp_filesystem->get_contents( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'object-cache.copy.php' ); + + if ( $original_content && $original_content === $copy_content ) { + $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' ); + } + } + } +} diff --git a/includes/Utilities/Plugin_Request_Utility.php b/includes/Utilities/Plugin_Request_Utility.php index e06162ba4..10c9b5721 100644 --- a/includes/Utilities/Plugin_Request_Utility.php +++ b/includes/Utilities/Plugin_Request_Utility.php @@ -8,6 +8,8 @@ namespace WordPress\Plugin_Check\Utilities; use Exception; +use WordPress\Plugin_Check\Checker\CLI_Runner; +use WordPress\Plugin_Check\Checker\AJAX_Runner; /** * Class providing utility methods to return plugin information based on the request. @@ -16,6 +18,22 @@ */ class Plugin_Request_Utility { + /** + * Instance of the current runner based on the request. + * + * @since n.e.x.t + * @var Abstract_Check_Runner + */ + protected static $runner; + + /** + * The universal runtime preparation cleanups if applied. + * + * @since n.e.x.t + * @var callable + */ + protected static $cleanup; + /** * Returns the plugin basename based on the input provided. * @@ -60,4 +78,40 @@ public static function get_plugin_basename_from_input( $plugin_slug ) { ) ); } + + /** + * Initializes the runner classes. + * + * @since n.e.x.t + */ + public static function initialize_runner() { + $runners = array( + new CLI_Runner(), + new AJAX_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; + break; + } + } + } + + /** + * Get the Runner class for the current request. + * + * @since n.e.x.t + * + * @return Abstract_Check_Runner|null The Runner class for the request or null. + */ + public static function get_runner() { + if ( isset( static::$runner ) ) { + return static::$runner; + } + + return null; + } } diff --git a/object-cache.copy.php b/object-cache.copy.php new file mode 100644 index 000000000..3684c7b1e --- /dev/null +++ b/object-cache.copy.php @@ -0,0 +1,43 @@ + +set_prefix( $table_prefix ); + parent::tear_down(); + } + public function test_is_plugin_check_returns_true() { // Mock the AJAX request. add_filter( 'wp_doing_ajax', '__return_true' ); diff --git a/tests/Checker/CLI_Runner_Tests.php b/tests/Checker/CLI_Runner_Tests.php index 278f3f211..4b416ac16 100644 --- a/tests/Checker/CLI_Runner_Tests.php +++ b/tests/Checker/CLI_Runner_Tests.php @@ -10,6 +10,13 @@ class CLI_Runner_Tests extends WP_UnitTestCase { + public function tear_down() { + // Force reset the database prefix after runner prepare method called. + global $wpdb, $table_prefix; + $wpdb->set_prefix( $table_prefix ); + parent::tear_down(); + } + public function test_is_plugin_check_returns_true() { $_SERVER['argv'] = array( 'wp', diff --git a/tests/Checker/Runtime_Environment_Setup_Tests.php b/tests/Checker/Runtime_Environment_Setup_Tests.php new file mode 100644 index 000000000..7f44fddc3 --- /dev/null +++ b/tests/Checker/Runtime_Environment_Setup_Tests.php @@ -0,0 +1,98 @@ +set_up_mock_filesystem(); + + $runtime_setup = new Runtime_Environment_Setup(); + $runtime_setup->setup(); + + $this->assertTrue( 0 <= strpos( $wpdb->last_query, 'wppc_' ) ); + $this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ); + $this->assertSame( file_get_contents( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'object-cache.copy.php' ), $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) ); + } + + public function test_cleanup() { + global $wp_filesystem, $wpdb; + + $this->set_up_mock_filesystem(); + + $runtime_setup = new Runtime_Environment_Setup(); + $runtime_setup->setup(); + + // Simulate file exists by setting constant found in object-cache.php. + define( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION', 1 ); + + $runtime_setup->cleanup(); + + $this->assertTrue( 0 <= strpos( $wpdb->last_query, 'wppc_' ) ); + $this->assertFalse( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ); + } + + public function test_setup_with_existing_object_cache() { + global $wp_filesystem, $wpdb; + + $this->set_up_mock_filesystem(); + + // Simulate a different object-cache.php. + $dummy_file_content = 'put_contents( WP_CONTENT_DIR . '/object-cache.php', $dummy_file_content ); + + $runtime_setup = new Runtime_Environment_Setup(); + $runtime_setup->setup(); + + $this->assertTrue( 0 <= strpos( $wpdb->last_query, 'wppc_' ) ); + $this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ); + $this->assertSame( $dummy_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) ); + } + + public function test_cleanup_with_existing_object_cache() { + global $wp_filesystem, $wpdb; + + $this->set_up_mock_filesystem(); + + // Simulate a different object-cache.php. + $dummy_file_content = 'put_contents( WP_CONTENT_DIR . '/object-cache.php', $dummy_file_content ); + + $runtime_setup = new Runtime_Environment_Setup(); + $runtime_setup->setup(); + $runtime_setup->cleanup(); + + $this->assertTrue( 0 <= strpos( $wpdb->last_query, 'wppc_' ) ); + $this->assertTrue( $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ); + $this->assertSame( $dummy_file_content, $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' ) ); + } + + private function set_up_mock_filesystem() { + global $wp_filesystem; + + add_filter( + 'filesystem_method_file', + function() { + return __DIR__ . '/../testdata/Filesystem/WP_Filesystem_MockFilesystem.php'; + } + ); + add_filter( + 'filesystem_method', + function() { + return 'MockFilesystem'; + } + ); + + WP_Filesystem(); + + // Simulate that the original object-cache.copy.php file exists. + $wp_filesystem->put_contents( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'object-cache.copy.php', file_get_contents( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'object-cache.copy.php' ) ); + } +} diff --git a/tests/Utilities/Plugin_Request_Utility_Tests.php b/tests/Utilities/Plugin_Request_Utility_Tests.php index 47b677f57..a0ea70f0e 100644 --- a/tests/Utilities/Plugin_Request_Utility_Tests.php +++ b/tests/Utilities/Plugin_Request_Utility_Tests.php @@ -6,6 +6,8 @@ */ use WordPress\Plugin_Check\Utilities\Plugin_Request_Utility; +use WordPress\Plugin_Check\Checker\CLI_Runner; +use WordPress\Plugin_Check\Checker\AJAX_Runner; class Plugin_Request_Utility_Tests extends WP_UnitTestCase { @@ -28,4 +30,31 @@ public function test_get_plugin_basename_from_input_with_invalid_input() { Plugin_Request_Utility::get_plugin_basename_from_input( 'invalid' ); } + + public function test_initialize_runner_with_cli() { + $_SERVER['argv'] = array( + 'wp', + 'plugin', + 'check', + 'plugin-check', + ); + + Plugin_Request_Utility::initialize_runner(); + $runner = Plugin_Request_Utility::get_runner(); + + unset( $_SERVER['argv'] ); + + $this->assertInstanceOf( CLI_Runner::class, $runner ); + } + + public function test_initialize_runner_with_ajax() { + add_filter( 'wp_doing_ajax', '__return_true' ); + $_REQUEST['action'] = 'plugin_check_run_checks'; + $_REQUEST['plugin'] = 'plugin-check'; + + Plugin_Request_Utility::initialize_runner(); + $runner = Plugin_Request_Utility::get_runner(); + + $this->assertInstanceOf( AJAX_Runner::class, $runner ); + } } diff --git a/tests/testdata/Filesystem/WP_Filesystem_MockFilesystem.php b/tests/testdata/Filesystem/WP_Filesystem_MockFilesystem.php new file mode 100644 index 000000000..8729c2062 --- /dev/null +++ b/tests/testdata/Filesystem/WP_Filesystem_MockFilesystem.php @@ -0,0 +1,141 @@ +file_contents[ $file ] ) ) { + return $this->file_contents[ $file ]; + } + return false; + } + + public function get_contents_array( $file ) { + if ( isset( $this->file_contents[ $file ] ) ) { + return array( $this->file_contents[ $file ] ); + } + return false; + } + + public function put_contents( $file, $contents, $mode = false ) { + $this->file_contents[ $file ] = $contents; + return true; + } + + public function cwd() { + return false; + } + + public function chdir( $dir ) { + return false; + } + + public function chgrp( $file, $group, $recursive = false ) { + return false; + } + + public function chmod( $file, $mode = false, $recursive = false ) { + return false; + } + + public function owner( $file ) { + return false; + } + + public function group( $file ) { + return false; + } + + public function copy( $source, $destination, $overwrite = false, $mode = false ) { + if ( ! isset( $this->file_contents[ $source ] ) ) { + return false; + } + if ( ! $overwrite && isset( $this->file_contents[ $destination ] ) ) { + return false; + } + $this->file_contents[ $destination ] = $this->file_contents[ $source ]; + return true; + } + + public function move( $source, $destination, $overwrite = false ) { + if ( $this->copy( $source, $destination, $overwrite, false ) ) { + return $this->delete( $source ); + } + return false; + } + + public function delete( $file, $recursive = false, $type = false ) { + if ( isset( $this->file_contents[ $file ] ) ) { + unset( $this->file_contents[ $file ] ); + } + return true; + } + + public function exists( $path ) { + return isset( $this->file_contents[ $path ] ); + } + + public function is_file( $file ) { + return isset( $this->file_contents[ $file ] ); + } + + public function is_dir( $path ) { + return false; + } + + public function is_readable( $file ) { + return isset( $this->file_contents[ $file ] ); + } + + public function is_writable( $path ) { + return true; + } + + public function atime( $file ) { + return false; + } + + public function mtime( $file ) { + return false; + } + + public function size( $file ) { + if ( isset( $this->file_contents[ $file ] ) ) { + return strlen( $this->file_contents[ $file ] ); + } + return false; + } + + public function touch( $file, $time = 0, $atime = 0 ) { + if ( ! isset( $this->file_contents[ $file ] ) ) { + $this->file_contents[ $file ] = ''; + } + return true; + } + + public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) { + return false; + } + + public function rmdir( $path, $recursive = false ) { + return false; + } + + public function dirlist( $path, $include_hidden = true, $recursive = false ) { + return false; + } +}