Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI: Add --exclude-checks support #318

Merged
merged 10 commits into from
Nov 7, 2023
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
4 changes: 4 additions & 0 deletions includes/CLI/Plugin_Check_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public function __construct( Plugin_Context $plugin_context ) {
* [--checks=<checks>]
* : Only runs checks provided as an argument in comma-separated values, e.g. i18n_usage, late_escaping. Otherwise runs all checks.
*
* [--exclude-checks=<checks>]
Copy link
Member

@felixarntz felixarntz Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You call this ignore-checks / ignore-slugs everywhere else in this PR. I don't feel strongly about either way, but we should go with a consistent term throughout. It makes sense that the parameter uses "checks" while the code uses "slugs", but I mean we should only use the term "ignore" or "exclude" throughout.

I personally prefer exclude-checks I think, but either way WFM.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, let's settle on exclude

* : Exclude checks provided as an argument in comma-separated values, e.g. i18n_usage, late_escaping.
* Applies after evaluating `--checks`.
*
* [--format=<format>]
* : Format to display the results. Options are table, csv, and json. The default will be a table.
* ---
Expand Down
14 changes: 14 additions & 0 deletions includes/Checker/AJAX_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ protected function get_check_slugs_param() {
return $checks;
}

/**
* Returns an array of Check slugs to exclude based on the request.
*
* @since n.e.x.t
*
* @return array An array of Check slugs to exclude.
*/
protected function get_check_exclude_slugs_param() {
$checks = filter_input( INPUT_POST, 'exclude-checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
$checks = is_null( $checks ) ? array() : $checks;

return $checks;
}

/**
* Returns the include experimental parameter based on the request.
*
Expand Down
67 changes: 62 additions & 5 deletions includes/Checker/Abstract_Check_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ abstract class Abstract_Check_Runner implements Check_Runner {
*/
protected $check_slugs;

/**
* The check slugs to exclude.
*
* @since n.e.x.t
* @var array
*/
protected $check_exclude_slugs;

/**
* The plugin parameter.
*
Expand Down Expand Up @@ -110,6 +118,15 @@ abstract protected function get_plugin_param();
*/
abstract protected function get_check_slugs_param();

/**
* Returns an array of Check slugs to exclude based on the request.
*
* @since n.e.x.t
*
* @return array An array of Check slugs.
*/
abstract protected function get_check_exclude_slugs_param();

/**
* Returns the include experimental parameter based on the request.
*
Expand Down Expand Up @@ -161,6 +178,28 @@ final public function set_check_slugs( array $check_slugs ) {
$this->check_slugs = $check_slugs;
}

/**
* Sets the check slugs to be excluded.
*
* @since n.e.x.t
*
* @param array $check_slugs An array of check slugs to be excluded.
*
* @throws Exception Thrown if the checks do not match those in the original request.
*/
final public function set_check_exclude_slugs( array $check_slugs ) {
if ( $this->initialized_early ) {
// Compare the check slugs to see if there was an error.
if ( $check_slugs !== $this->get_check_exclude_slugs_param() ) {
throw new Exception(
__( 'Invalid checks: The checks to exclude do not match the original request.', 'plugin-check' )
);
}
}

$this->check_exclude_slugs = $check_slugs;
}

/**
* Sets the plugin slug or basename to be checked.
*
Expand Down Expand Up @@ -368,18 +407,21 @@ final public function get_checks_to_run() {
$check_flags = $check_flags | Check_Repository::INCLUDE_EXPERIMENTAL;
}

$checks = $this->check_repository->get_checks( $check_flags )
->require( $check_slugs ) // Ensures all of the given slugs are valid.
$excluded_checks = $this->get_check_exclude_slugs();

$collection = $this->check_repository->get_checks( $check_flags )
->include( $check_slugs ) // Ensures only the checks with the given slugs are included.
->to_map();
->exclude( $excluded_checks ); // Exclude provided checks from list.

// Filters the checks by specific categories.
$categories = $this->get_categories();
if ( $categories ) {
$checks = Check_Categories::filter_checks_by_categories( $checks, $categories );
$collection = Check_Categories::filter_checks_by_categories( $collection, $categories );
}

return $checks;
return $collection
->require( $check_slugs ) // Ensures all of the given slugs are valid.
->to_map();
}

/**
Expand Down Expand Up @@ -416,6 +458,21 @@ private function get_check_slugs() {
return $this->get_check_slugs_param();
}

/**
* Returns the check slugs to exclude.
*
* @since n.e.x.t
*
* @return array An array of check slugs to exclude.
*/
private function get_check_exclude_slugs() {
if ( null !== $this->check_exclude_slugs ) {
return $this->check_exclude_slugs;
}

return $this->get_check_exclude_slugs_param();
}

/**
* Returns the plugin basename.
*
Expand Down
20 changes: 20 additions & 0 deletions includes/Checker/CLI_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,26 @@ protected function get_check_slugs_param() {
return $checks;
}

/**
* Returns an array of Check slugs to exclude based on the request.
*
* @since n.e.x.t
*
* @return array An array of Check slugs to run.
*/
protected function get_check_exclude_slugs_param() {
$checks = array();

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

return $checks;
}

/**
* Returns the include experimental parameter based on the request.
*
Expand Down
11 changes: 5 additions & 6 deletions includes/Checker/Check_Categories.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,12 @@ static function ( $key ) {
*
* @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.
* @param Check_Collection $collection Check collection.
* @param array $categories An array of categories to filter by.
* @return Check_Collection Filtered check collection.
*/
public static function filter_checks_by_categories( array $checks, array $categories ) {
return array_filter(
$checks,
public static function filter_checks_by_categories( Check_Collection $collection, array $categories ): Check_Collection {
return $collection->filter(
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 );
Expand Down
18 changes: 16 additions & 2 deletions includes/Checker/Check_Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ public function to_map(): array;
*
* @since n.e.x.t
*
* @param callable $filter_fn Filter function that accepts a single check object and should return a boolean for
* whether to include the check in the new collection.
* @phpstan-param callable(Check,string): bool $filter_fn
*
* @param callable $filter_fn Filter function that accepts a Check object and a Check slug and
* should return a boolean for whether to include the check in the new collection.
* @return Check_Collection New check collection, effectively a subset of this one.
*/
public function filter( callable $filter_fn ): Check_Collection;
Expand All @@ -60,6 +62,18 @@ public function filter( callable $filter_fn ): Check_Collection;
*/
public function include( array $check_slugs ): Check_Collection;

/**
* Returns a new check collection excluding the provided checks.
*
* If the given list is empty, the same collection will be returned without any change.
*
* @since n.e.x.t
*
* @param array $check_slugs List of slugs to exclude. If empty, the same collection is returned.
* @return Check_Collection New check collection, effectively a subset of this one.
*/
public function exclude( array $check_slugs ): Check_Collection;

/**
* Throws an exception if any of the given check slugs are not present, or returns the same collection otherwise.
*
Expand Down
32 changes: 29 additions & 3 deletions includes/Checker/Default_Check_Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,18 @@ public function to_map(): array {
*
* @since n.e.x.t
*
* @param callable $filter_fn Filter function that accepts a single check object and should return a boolean for
* whether to include the check in the new collection.
* @phpstan-param callable(Check,string): bool $filter_fn
*
* @param callable $filter_fn Filter function that accepts a Check object and a Check slug and
* should return a boolean for whether to include the check in the new collection.
* @return Check_Collection New check collection, effectively a subset of this one.
*/
public function filter( callable $filter_fn ): Check_Collection {
return new self(
array_filter(
$this->checks,
$filter_fn
$filter_fn,
ARRAY_FILTER_USE_BOTH
)
);
}
Expand Down Expand Up @@ -116,6 +119,29 @@ public function include( array $check_slugs ): Check_Collection {
return new self( $checks );
}

/**
* Returns a new check collection excluding the provided checks.
*
* If the given list is empty, the same collection will be returned without any change.
*
* @since n.e.x.t
*
* @param array $check_slugs List of slugs to exclude. If empty, the same collection is returned.
* @return Check_Collection New check collection, effectively a subset of this one.
*/
public function exclude( array $check_slugs ): Check_Collection {
// Return unmodified collection if no check slugs to exclude are given.
if ( ! $check_slugs ) {
return $this;
}

return $this->filter(
static function ( Check $check, $slug ) use( $check_slugs ) {
return ! in_array( $slug, $check_slugs, true );
}
);
}

/**
* Throws an exception if any of the given check slugs are not present, or returns the same collection otherwise.
*
Expand Down
2 changes: 1 addition & 1 deletion includes/Checker/Default_Check_Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public function get_checks( $flags = self::TYPE_ALL ) {

// Remove experimental checks before returning.
return ( new Default_Check_Collection( $checks ) )->filter(
static function ( $check ) {
static function ( Check $check ) {
return $check->get_stability() !== Check::STABILITY_EXPERIMENTAL;
}
);
Expand Down
5 changes: 2 additions & 3 deletions tests/phpunit/Checker/Check_Categories_Tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,12 @@ public function test_filter_checks_by_categories( array $categories, array $all_
$this->repository->register_check( $check[0], $check[1] );
}

$checks = $this->repository->get_checks()
->to_map();
$checks = $this->repository->get_checks();

$check_categories = new Check_Categories();
$filtered_checks = $check_categories->filter_checks_by_categories( $checks, $categories );

$this->assertEquals( $expected_filtered_checks, $filtered_checks );
$this->assertEquals( $expected_filtered_checks, $filtered_checks->to_map() );
}

public function data_checks_by_categories() {
Expand Down
21 changes: 21 additions & 0 deletions tests/phpunit/Checker/Default_Check_Collection_Tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,25 @@ public function test_require_with_invalid() {

$this->collection->require( array( 'static_check', 'invalid_check' ) );
}

public function test_exclude() {
$this->assertSame(
array( $this->checks['runtime_check'] ),
$this->collection->exclude( array( 'static_check' ) )->to_array()
);
}

public function test_exclude_with_empty() {
$this->assertSame(
array_values( $this->checks ),
$this->collection->exclude( array() )->to_array()
);
}

public function test_exclude_with_invalid() {
$this->assertSame(
array( $this->checks['static_check'], $this->checks['runtime_check'] ),
$this->collection->exclude( array( 'invalid_check' ) )->to_array()
);
}
}