Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions includes/Checker/Abstract_Check_Runner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php
/**
* Class WordPress\Plugin_Check\Checker\Abstract_Check_runner
*
* @package plugin-check
*/

namespace WordPress\Plugin_Check\Checker;

use WordPress\Plugin_Check\Checker\Preparations\Universal_Runtime_Preparation;
Comment thread
jjgrainger marked this conversation as resolved.

/**
* Abstract Check Runner class.
*
* @since n.e.x.t
*/
abstract class Abstract_Check_Runner implements Check_Runner {

/**
* Determines if the current request is intended for the plugin checker.
*
* @since n.e.x.t
*
* @return bool Returns true if the check is for plugin else false.
*/
abstract public function is_plugin_check();

/**
* Creates and returns an instance of the Checks class based on the request.
*
* @since n.e.x.t
*
* @return Checks An instances of the Checks class.
*/
abstract protected function get_checks_instance();

/**
* Returns an array of Check slugs to run based on the request.
*
* @since n.e.x.t
*
* @return array An array of Check slugs.
*/
Comment thread
jjgrainger marked this conversation as resolved.
abstract protected function get_check_slugs_to_run();

/**
* Prepares the environment for running the requested checks.
*
* @since n.e.x.t
*
* @return callable Cleanup function to revert any changes made here.
*
* @throws Exception Thrown exception when preparation fails.
*/
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();
}

return function() {};
}

/**
* Runs the checks against the plugin.
*
* @since n.e.x.t
*
* @return Check_Result An object containing all check results.
*/
public function run() {
$checks = $this->get_checks_to_run();
$preparations = $this->get_shared_preparations( $checks );
$cleanups = array();

foreach ( $preparations as $preparation ) {
$instance = new $preparation['class']( ...$preparation['args'] );
$cleanups[] = $instance->prepare();
}

$results = $this->get_checks_instance()->run_checks( $checks );

if ( ! empty( $cleanups ) ) {
foreach ( $cleanups as $cleanup ) {
$cleanup();
}
}

return $results;
}

/**
* Determines if any of the checks requires the universal runtime preparation.
*
* @since n.e.x.t
*
* @param array $checks An array of check instances to run.
* @return bool Returns true if one or more checks requires the universal runtime preparation.
*/
protected function requires_universal_preparations( array $checks ) {
foreach ( $checks as $check ) {
if ( $check instanceof Runtime_Check ) {
return true;
}
}

return false;
}

/**
* Returns all shared preparations used by the checks to run.
*
* @since n.e.x.t
*
* @param array $checks An array of Check instances to run.
* @return array An array of Preparations to run where each item is an array with keys `class` and `args`.
*/
private function get_shared_preparations( array $checks ) {
$shared_preparations = array();

foreach ( $checks as $check ) {
if ( ! $check instanceof With_Shared_Preparations ) {
continue;
}

$preparations = $check->get_shared_preparations();

foreach ( $preparations as $class => $args ) {
$key = $class . '::' . md5( json_encode( $args ) );

if ( ! isset( $shared_preparations[ $key ] ) ) {
$shared_preparations[ $key ] = array(
'class' => $class,
'args' => $args,
);
}
}
}

return array_values( $shared_preparations );
}

/**
* Returns the Check instances to run.
*
* @since n.e.x.t
*
* @return array An array map of check slugs to Check instances.
*/
protected function get_checks_to_run() {
$check_slugs = $this->get_check_slugs_to_run();
$all_checks = $this->get_checks_instance()->get_checks();

if ( empty( $check_slugs ) ) {
return $all_checks;
}

return array_intersect_key( $all_checks, array_flip( $check_slugs ) );
}
}
90 changes: 90 additions & 0 deletions includes/Checker/CLI_Runner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php
/**
* Class WordPress\Plugin_Check\Checker\CLI_Runner
*
* @package plugin-check
*/

namespace WordPress\Plugin_Check\Checker;

use WordPress\Plugin_Check\Utilities\Plugin_Request_Utility;

/**
* CLI Runner class.
*
* @since n.e.x.t
*/
class CLI_Runner extends Abstract_Check_Runner {

/**
* An instances of the Checks class.
*
* @since n.e.x.t
* @var Checks
*/
protected $checks;

/**
* Checks if the current request is a CLI request for the Plugin Checker.
*
* @since n.e.x.t
*
* @return bool
*/
public function is_plugin_check() {
if ( empty( $_SERVER['argv'] ) || 3 > count( $_SERVER['argv'] ) ) {
return false;
}

if (
'wp' === $_SERVER['argv'][0] &&
'plugin' === $_SERVER['argv'][1] &&
'check' === $_SERVER['argv'][2]
Comment thread
jjgrainger marked this conversation as resolved.
) {
return true;
}

return false;
}

/**
* Retruns an instance of the Checks class.
*
* @since n.e.x.t
*
* @return Checks
*
* @throws Exception Thrown if the plugin main file cannot be found based on the CLI input.
*/
protected function get_checks_instance() {
if ( ! isset( $this->checks ) ) {
// Get the plugin name from the command line arguments.
$plugin_slug = isset( $_SERVER['argv'][3] ) ? $_SERVER['argv'][3] : '';
$plugin_file = Plugin_Request_Utility::get_plugin_basename_from_input( $plugin_slug );

$this->checks = new Checks( WP_PLUGIN_DIR . '/' . $plugin_file );
}

return $this->checks;
}
Comment thread
jjgrainger marked this conversation as resolved.

/**
* Returns an array of Check instances to run.
*
* @since n.e.x.t
*
* @return array An array of Check instances to run.
*/
protected function get_check_slugs_to_run() {
$checks = array();

foreach ( $_SERVER['argv'] as $value ) {
if ( false !== strpos( $value, '--checks=' ) ) {
$checks = explode( ',', str_replace( '--checks=', '', $value ) );
break;
}
}

return $checks;
}
}
49 changes: 37 additions & 12 deletions includes/Checker/Checks.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
*/
class Checks {

/**
* Array of all available Checks.
*
* @since n.e.x.t
* @var array
*/
protected $checks;

/**
* Context for the plugin to check.
*
Expand All @@ -36,6 +44,17 @@ public function __construct( $plugin_main_file ) {
$this->check_context = new Check_Context( $plugin_main_file );
}

/**
* Returns the Check Context.
*
* @since n.e.x.t
*
* @return Check_Context The plugin context that is being checked.
*/
public function context() {
return $this->check_context;
}

/**
* Runs checks against the plugin.
*
Expand Down Expand Up @@ -105,19 +124,25 @@ protected function run_check_with_result( Check $check, Check_Result $result ) {
*
* @since n.e.x.t
*
* @return array List of plugin check class instances implementing the Check interface.
* @return array An array map of check slugs to Check instances.
*/
public function get_checks() {
// TODO: Add checks once implemented.
$checks = array();

/**
* Filters the available plugin check classes.
*
* @since n.e.x.t
*
* @param array $checks List of plugin check class instances implementing the Check interface.
*/
return apply_filters( 'wp_plugin_check_checks', $checks );
if ( ! isset( $this->checks ) ) {
// TODO: Add checks once implemented.
$checks = array(
'i18n_usage' => new Checks\I18n_Usage_Check(),
);

/**
* Filters the available plugin check classes.
*
* @since n.e.x.t
*
* @param array $checks An array map of check slugs to Check instances.
*/
$this->checks = apply_filters( 'wp_plugin_check_checks', $checks );
}

return $this->checks;
}
}
63 changes: 63 additions & 0 deletions includes/Utilities/Plugin_Request_Utility.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
/**
* Class WordPress\Plugin_Check\Utilities
*
* @package plugin-check
*/

namespace WordPress\Plugin_Check\Utilities;

use Exception;

/**
* Class providing utility methods to return plugin information based on the request.
*
* @since n.e.x.t
*/
class Plugin_Request_Utility {

/**
* Returns the plugin basename based on the input provided.
*
* @param string $plugin_slug The plugin slug or basename.
* @return string The plugin basename.
*
* @throws Exception Thrown if an invalid basename or plugin slug is provided.
*/
public static function get_plugin_basename_from_input( $plugin_slug ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';

if ( empty( $plugin_slug ) ) {
throw new Exception( 'Invalid plugin slug: Plugin slug must not be empty.' );
}

$plugins = get_plugins();

// Is the provided value is a full plugin basename?
if ( isset( $plugins[ $plugin_slug ] ) ) {
return $plugin_slug;
}

if ( strpos( $plugin_slug, '/' ) ) {
throw new Exception(
sprintf(
'Invalid plugin basename: Plugin with basename %s is not installed.',
$plugin_slug
)
);
}

foreach ( $plugins as $plugin_basename => $plugin_data ) {
if ( strpos( $plugin_basename, $plugin_slug . '/' ) === 0 ) {
return $plugin_basename;
}
}

throw new Exception(
sprintf(
'Invalid plugin slug: Plugin with slug %s is not installed.',
$plugin_slug
)
);
}
}
Loading