Skip to content

Commit

Permalink
Merge branch 'trunk' into add/cli-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
swissspidy authored Jul 13, 2023
2 parents 78e49f2 + a19dcac commit 041b3ab
Show file tree
Hide file tree
Showing 57 changed files with 1,197 additions and 64 deletions.
65 changes: 63 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,66 @@
# Plugin Check

A repository for the new Plugin Check plugin from the [WordPress Performance Team](https://make.wordpress.org/performance/).
This repository is for the WordPress plugin checker, a tool for plugin developers to analyze their plugin code and flag any violations or concerns around plugin development best practices, from basic requirements like correct usage of internationalization functions to accessibility, performance, and security best practices.

If you would like to contribute please see the [CONTRIBUTING.md guide here](/CONTRIBUTING.md).
The WordPress plugin checker was [first proposed in summer 2022](https://make.wordpress.org/plugins/2022/07/05/proposal-for-a-wordpress-plugin-checker/) and is now at an early MVP stage.

## Features

### For end users

* Allows analyzing any installed plugin using either a WP Admin screen or a WP-CLI command.
* Supports two kinds of checks:
* Static checks, which analyze the code, either using PHPCodeSniffer sniffs or custom logic e.g. using regular expressions.
* Runtime checks, which actually execute certain parts of the code, such as running specific WordPress hooks with the plugin active.
* Allows customizing which checks are run, either via a list of individual check identifiers, or specific check categories.
* Comes with an ever-growing list of checks for various plugin development requirements and best practices. Please see the [`Abstract_Check_Runner::register_checks()` method](/includes/Checker/Abstract_Check_Runner.php#L358) for a quick overview of currently available checks.

### For developers

* Facilitates efficient yet flexible authoring of new checks, either using a base class for common check patterns, or implementing an interface for more specific checks.
* Every check has to implement either the [`Static_Check`](/includes/Checker/Static_Check.php) or the [`Runtime_Check`](/includes/Checker/Runtime_Check.php) interface.
* Most checks will benefit from extending either the [`Abstract_File_Check`](/includes/Checker/Checks/Abstract_File_Check.php), the [`Abstract_PHPCodeSniffer_Check`](/includes/Checker/Checks/Abstract_PHP_CodeSniffer_Check.php), or the [`Abstract_Runtime_Check`](/includes/Checker/Checks/Abstract_Runtime_Check.php) class.
* Comes with comprehensive unit test coverage.

## How to use

The WordPress plugin checker is a WordPress plugin itself, which can be installed on any WordPress site. While it is implemented in a way that should avoid any disruptions on the site that it is being used on, it is still **advised not to use the plugin checker in a production environment**.

Currently, the only way to install the plugin checker is to download it from this GitHub repository. Please see the [contributing section below](#contributing) for further instructions. Once a first beta version is available, it will be distributed in a standalone ZIP file, e.g. via the wordpress.org plugin repository.

After having the plugin activated, you can analyze any other plugin installed on the same site, either using the WP Admin user interface or WP-CLI:

* To check a plugin using WP Admin, please navigate to the _Tools > Plugin Check_ menu. You need to be able to manage plugins on your site in order to access that screen.
* To check a plugin using WP-CLI, please use the `wp plugin check` command. For example, to check the "Hello Dolly" plugin: `wp plugin check hello.php`
* Note that by default when using WP-CLI, only static checks can be executed. In order to also include runtime checks, a workaround is currently necessary using the `--require` argument of WP-CLI, to manually load the `cli.php` file within the plugin checker directory before WordPress is loaded. For example: `wp plugin check hello.php --require=./wp-content/plugins/plugin-check/cli.php`

## Contributing

To set up the repository locally, you will need to clone this GitHub repository (or a fork of it) and then install the relevant dependencies:

```
git clone https://github.com/10up/plugin-check.git wp-content/plugins/plugin-check
cd wp-content/plugins/plugin-check
composer install
npm install
```

### Built-in development environment (optional)

With the above commands, you can use the plugin in any development environment as you like. The recommended way is to use the built-in development environment, which is based on the [`@wordpress/env` package](https://www.npmjs.com/package/@wordpress/env), as that will allow you to use the preconfigured commands to e.g. run unit tests, linting etc. You will need to have Docker installed to use this environment.

You can start the built-in environment as follows:
```
npm run wp-env start
```

If you want to stop the environment again, you can use:
```
npm run wp-env stop
```

For further information on contributing, please see the [contributing guide](/CONTRIBUTING.md).

## License

The WordPress plugin checker is free software, and is released under the terms of the GNU General Public License version 2 or (at your option) any later version. See [LICENSE](/LICENSE) for complete license.
2 changes: 1 addition & 1 deletion cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
function() {
if ( CLI_Runner::is_plugin_check() ) {
if ( ! file_exists( ABSPATH . 'wp-content/object-cache.php' ) ) {
if ( ! copy( __DIR__ . '/object-cache.copy.php', ABSPATH . 'wp-content/object-cache.php' ) ) {
if ( ! copy( __DIR__ . '/drop-ins/object-cache.copy.php', ABSPATH . 'wp-content/object-cache.php' ) ) {
WP_CLI::error( 'Unable to copy object-cache.php file.' );
}
}
Expand Down
File renamed without changes.
14 changes: 12 additions & 2 deletions includes/Admin/Admin_Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function __construct( Admin_AJAX $admin_ajax ) {
*/
public function add_hooks() {
add_action( 'admin_menu', array( $this, 'add_and_initialize_page' ) );
add_filter( 'plugin_action_links', array( $this, 'filter_plugin_action_links' ), 10, 2 );
add_filter( 'plugin_action_links', array( $this, 'filter_plugin_action_links' ), 10, 4 );

$this->admin_ajax->add_hooks();
}
Expand Down Expand Up @@ -166,9 +166,19 @@ public function render_page() {
*
* @param array $actions List of actions.
* @param string $plugin_file Plugin main file.
* @param array $plugin_data An array of plugin data.
* @param string $context The plugin context. By default this can include 'all',
* 'active', 'inactive', 'recently_activated', 'upgrade',
* 'mustuse', 'dropins', and 'search'.
* @return array The modified list of actions.
*/
public function filter_plugin_action_links( $actions, $plugin_file ) {
public function filter_plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {

$plugin_check_base_name = plugin_basename( WP_PLUGIN_CHECK_MAIN_FILE );
if ( in_array( $context, array( 'mustuse', 'dropins' ), true ) || $plugin_check_base_name === $plugin_file ) {
return $actions;
}

if ( current_user_can( 'activate_plugins' ) ) {
$actions[] = sprintf(
'<a href="%1$s">%2$s</a>',
Expand Down
12 changes: 8 additions & 4 deletions includes/CLI/Plugin_Check_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public function __construct( Plugin_Context $plugin_context ) {
* [--ignore-errors]
* : Limit displayed results to exclude errors.
*
* [--include-experimental]
* : Include experimental checks.
*
* ## EXAMPLES
*
Expand Down Expand Up @@ -124,6 +126,7 @@ public function check( $args, $assoc_args ) {

$checks_to_run = array();
try {
$runner->set_experimental_flag( $options['include-experimental'] );
$runner->set_check_slugs( $checks );
$runner->set_plugin( $plugin );

Expand Down Expand Up @@ -204,10 +207,11 @@ public function check( $args, $assoc_args ) {
*/
private function get_options( $assoc_args ) {
$defaults = array(
'checks' => '',
'format' => 'table',
'ignore-warnings' => false,
'ignore-errors' => false,
'checks' => '',
'format' => 'table',
'ignore-warnings' => false,
'ignore-errors' => false,
'include-experimental' => false,
);

$options = wp_parse_args( $assoc_args, $defaults );
Expand Down
11 changes: 11 additions & 0 deletions includes/Checker/AJAX_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,15 @@ protected function get_check_slugs_param() {

return $checks;
}

/**
* Returns the include experimental paramater based on the request.
*
* @since n.e.x.t
*
* @return bool Returns true to include experimental checks else false.
*/
protected function get_include_experimental_param() {
return false;
}
}
58 changes: 58 additions & 0 deletions includes/Checker/Abstract_Check_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ abstract class Abstract_Check_Runner implements Check_Runner {
*/
protected $runtime_environment;

/**
* Whether to include experimental checks.
*
* @since n.e.x.t
* @var bool
*/
protected $include_experimental;

/**
* Determines if the current request is intended for the plugin checker.
*
Expand Down Expand Up @@ -101,6 +109,15 @@ abstract protected function get_plugin_param();
*/
abstract protected function get_check_slugs_param();

/**
* Returns the include experimental paramater based on the request.
*
* @since n.e.x.t
*
* @return bool Returns true to include experimental checks else false.
*/
abstract protected function get_include_experimental_param();

/**
* Sets whether the runner class was initialized early.
*
Expand Down Expand Up @@ -156,6 +173,27 @@ final public function set_plugin( $plugin ) {
$this->plugin = $plugin;
}

/**
* Sets whether to include experimental checks in the process.
*
* @since n.e.x.t
*
* @param bool $include_experimental True to include experimental checks. False to exclude.
*
* @throws Exception Thrown if the flag set does not match the original request parameter.
*/
final public function set_experimental_flag( $include_experimental ) {
if ( $this->initialized_early ) {
if ( $include_experimental !== $this->get_include_experimental_param() ) {
throw new Exception(
__( 'Invalid flag: The include-experimental flag does not match the original request parameter.', 'plugin-check' )
);
}
}

$this->include_experimental = $include_experimental;
}

/**
* Prepares the environment for running the requested checks.
*
Expand Down Expand Up @@ -287,6 +325,11 @@ final public function get_checks_to_run() {
$check_flags = Check_Repository::TYPE_ALL;
}

// Check whether to include experimental checks.
if ( $this->get_include_experimental() ) {
$check_flags = $check_flags | Check_Repository::INCLUDE_EXPERIMENTAL;
}

return $this->check_repository->get_checks( $check_flags, $check_slugs );
}

Expand Down Expand Up @@ -340,6 +383,21 @@ final public function get_plugin_basename() {
return $this->plugin_basename;
}

/**
* Returns the value for the include experimental flag.
*
* @since n.e.x.t
*
* @return bool True if experimental checks are included. False if not.
*/
final public function get_include_experimental() {
if ( null !== $this->include_experimental ) {
return $this->include_experimental;
}

return $this->get_include_experimental_param();
}

/** Gets the Check_Context for the plugin.
*
* @since n.e.x.t
Expand Down
15 changes: 15 additions & 0 deletions includes/Checker/CLI_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,19 @@ protected function get_check_slugs_param() {

return $checks;
}

/**
* Returns the include experimental paramater based on the request.
*
* @since n.e.x.t
*
* @return bool Returns true to include experimental checks else false.
*/
protected function get_include_experimental_param() {
if ( in_array( '--include-experimental', $_SERVER['argv'], true ) ) {
return true;
}

return false;
}
}
35 changes: 35 additions & 0 deletions includes/Checker/Check.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,30 @@
* @since n.e.x.t
*/
interface Check {
/**
* Stability value for stable checks.
*
* @since n.e.x.t
* @var string
*/
const STABILITY_STABLE = 'STABLE';

/**
* Stability value for experimental checks.
*
* @since n.e.x.t
* @var string
*/
const STABILITY_EXPERIMENTAL = 'EXPERIMENTAL';

/**
* Returns the check's stability.
*
* @since n.e.x.t
*
* @return string One of the check stability constant values.
*/
public function get_stability();

/**
* Amends the given result by running the check on the associated plugin.
Expand All @@ -27,4 +51,15 @@ interface Check {
* the check).
*/
public function run( Check_Result $result );

/**
* Gets the categories for the check.
*
* Every check must have at least one category.
*
* @since n.e.x.t
*
* @return array The categories for the check.
*/
public function get_categories();
}
73 changes: 73 additions & 0 deletions includes/Checker/Check_Categories.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* Class WordPress\Plugin_Check\Checker\Check_Categories
*
* @package plugin-check
*/

namespace WordPress\Plugin_Check\Checker;

/**
* Abstract Check Runner class.
*
* @since n.e.x.t
*/
class Check_Categories {

// Constants for available categories.
const CATEGORY_GENERAL = 'general';
const CATEGORY_PLUGIN_REPO = 'plugin_repo';
const CATEGORY_SECURITY = 'security';
const CATEGORY_PERFORMANCE = 'performance';
const CATEGORY_ACCESSIBILITY = 'accessibility';

/**
* Returns an array of available categories.
*
* @since n.e.x.t
*
* @return array An array of available categories.
*/
public static function get_categories() {
static $categories = '';
if ( ! $categories ) {
$constants = ( new \ReflectionClass( __CLASS__ ) )->getConstants();

/**
* List of categories.
*
* @var string[] $categories
*/
$categories = array_values(
array_filter(
$constants,
static function( $key ) {
return strpos( $key, 'CATEGORY_' ) === 0;
},
ARRAY_FILTER_USE_KEY
)
);
}

return $categories;
}

/**
* Returns an array of checks.
*
* @since n.e.x.t
*
* @param array $checks An array of Check instances.
* @param array $categories An array of categories to filter by.
* @return array Filtered $checks list.
*/
public static function filter_checks_by_categories( array $checks, array $categories ) {
return array_filter(
$checks,
static function( $check ) use ( $categories ) {
// Return true if at least one of the check categories is among the filter categories.
return (bool) array_intersect( $check->get_categories(), $categories );
}
);
}
}
Loading

0 comments on commit 041b3ab

Please sign in to comment.