From f1fc869437c0cb71891bd255402a67b18c5ccfc1 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 6 Nov 2023 17:11:23 +0100 Subject: [PATCH] =?UTF-8?q?CLI:=20Add=20`=E2=80=94exclude-checks`=20suppor?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- includes/CLI/Plugin_Check_Command.php | 4 ++ includes/Checker/AJAX_Runner.php | 14 ++++ includes/Checker/Abstract_Check_Runner.php | 66 +++++++++++++++++-- includes/Checker/CLI_Runner.php | 20 ++++++ includes/Checker/Check_Categories.php | 11 ++-- includes/Checker/Check_Collection.php | 18 ++++- includes/Checker/Default_Check_Collection.php | 32 ++++++++- includes/Checker/Default_Check_Repository.php | 2 +- 8 files changed, 150 insertions(+), 17 deletions(-) diff --git a/includes/CLI/Plugin_Check_Command.php b/includes/CLI/Plugin_Check_Command.php index 282c422f3..4e1e28d32 100644 --- a/includes/CLI/Plugin_Check_Command.php +++ b/includes/CLI/Plugin_Check_Command.php @@ -62,6 +62,10 @@ public function __construct( Plugin_Context $plugin_context ) { * [--checks=] * : Only runs checks provided as an argument in comma-separated values, e.g. i18n_usage, late_escaping. Otherwise runs all checks. * + * [--exclude-checks=] + * : Exclude checks provided as an argument in comma-separated values, e.g. i18n_usage, late_escaping. + * Applies after evaluating `--checks`. + * * [--format=] * : Format to display the results. Options are table, csv, and json. The default will be a table. * --- diff --git a/includes/Checker/AJAX_Runner.php b/includes/Checker/AJAX_Runner.php index 381c46e1d..585f077a4 100644 --- a/includes/Checker/AJAX_Runner.php +++ b/includes/Checker/AJAX_Runner.php @@ -82,6 +82,20 @@ protected function get_check_slugs_param() { return $checks; } + /** + * Returns an array of Check slugs to ignore based on the request. + * + * @since n.e.x.t + * + * @return array An array of Check slugs to ignore. + */ + protected function get_check_ignore_slugs_param() { + $checks = filter_input( INPUT_POST, 'ignore-checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY ); + $checks = is_null( $checks ) ? array() : $checks; + + return $checks; + } + /** * Returns the include experimental parameter based on the request. * diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php index 59138e1bd..137c4de7a 100644 --- a/includes/Checker/Abstract_Check_Runner.php +++ b/includes/Checker/Abstract_Check_Runner.php @@ -36,6 +36,14 @@ abstract class Abstract_Check_Runner implements Check_Runner { */ protected $check_slugs; + /** + * The check slugs to ignore. + * + * @since n.e.x.t + * @var array + */ + protected $check_ignore_slugs; + /** * The plugin parameter. * @@ -109,6 +117,14 @@ abstract protected function get_plugin_param(); * @return array An array of Check slugs. */ abstract protected function get_check_slugs_param(); + /** + * Returns an array of Check slugs to ignore based on the request. + * + * @since n.e.x.t + * + * @return array An array of Check slugs. + */ + abstract protected function get_check_ignore_slugs_param(); /** * Returns the include experimental parameter based on the request. @@ -161,6 +177,28 @@ final public function set_check_slugs( array $check_slugs ) { $this->check_slugs = $check_slugs; } + /** + * Sets the check slugs to be ignored. + * + * @since n.e.x.t + * + * @param array $check_slugs An array of check slugs to be ignored. + * + * @throws Exception Thrown if the checks do not match those in the original request. + */ + final public function set_check_ignore_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_ignore_slugs_param() ) { + throw new Exception( + __( 'Invalid checks: The checks to ignore do not match the original request.', 'plugin-check' ) + ); + } + } + + $this->check_ignore_slugs = $check_slugs; + } + /** * Sets the plugin slug or basename to be checked. * @@ -368,18 +406,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_ignore_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(); } /** @@ -416,6 +457,21 @@ private function get_check_slugs() { return $this->get_check_slugs_param(); } + /** + * Returns the check slugs to ignore. + * + * @since n.e.x.t + * + * @return array An array of check slugs to ignore. + */ + private function get_check_ignore_slugs() { + if ( null !== $this->check_ignore_slugs ) { + return $this->check_ignore_slugs; + } + + return $this->get_check_ignore_slugs_param(); + } + /** * Returns the plugin basename. * diff --git a/includes/Checker/CLI_Runner.php b/includes/Checker/CLI_Runner.php index 03e0ecd09..b68970e62 100644 --- a/includes/Checker/CLI_Runner.php +++ b/includes/Checker/CLI_Runner.php @@ -89,6 +89,26 @@ protected function get_check_slugs_param() { return $checks; } + /** + * Returns an array of Check slugs to ignore based on the request. + * + * @since n.e.x.t + * + * @return array An array of Check slugs to run. + */ + protected function get_check_ignore_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. * diff --git a/includes/Checker/Check_Categories.php b/includes/Checker/Check_Categories.php index 2b694ed49..cf4d17101 100644 --- a/includes/Checker/Check_Categories.php +++ b/includes/Checker/Check_Categories.php @@ -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 ); diff --git a/includes/Checker/Check_Collection.php b/includes/Checker/Check_Collection.php index c5698cbe3..0296b84c3 100644 --- a/includes/Checker/Check_Collection.php +++ b/includes/Checker/Check_Collection.php @@ -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; @@ -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. * diff --git a/includes/Checker/Default_Check_Collection.php b/includes/Checker/Default_Check_Collection.php index de81e85b9..c35fc8af3 100644 --- a/includes/Checker/Default_Check_Collection.php +++ b/includes/Checker/Default_Check_Collection.php @@ -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 ) ); } @@ -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 ignore 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. * diff --git a/includes/Checker/Default_Check_Repository.php b/includes/Checker/Default_Check_Repository.php index aa1670c14..3199d41fa 100644 --- a/includes/Checker/Default_Check_Repository.php +++ b/includes/Checker/Default_Check_Repository.php @@ -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; } );