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

Implement Check_Collection to separate concerns for check filtering from Check_Repository #315

Merged
merged 9 commits into from
Nov 6, 2023
5 changes: 4 additions & 1 deletion includes/Checker/Abstract_Check_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,10 @@ final public function get_checks_to_run() {
$check_flags = $check_flags | Check_Repository::INCLUDE_EXPERIMENTAL;
}

$checks = $this->check_repository->get_checks( $check_flags, $check_slugs );
$checks = $this->check_repository->get_checks( $check_flags )
->require( $check_slugs ) // Ensures all of the given slugs are valid.
->include( $check_slugs ) // Ensures only the checks with the given slugs are included.
->to_map();

// Filters the checks by specific categories.
$categories = $this->get_categories();
Expand Down
74 changes: 74 additions & 0 deletions includes/Checker/Check_Collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* Class WordPress\Plugin_Check\Checker\Check_Collection
*
* @package plugin-check
*/

namespace WordPress\Plugin_Check\Checker;

use ArrayAccess;
use Countable;
use Exception;
use IteratorAggregate;

/**
* Check Collection interface.
*
* @since n.e.x.t
*/
interface Check_Collection extends ArrayAccess, Countable, IteratorAggregate {

/**
* Returns the raw indexed array representation of this collection.
*
* @since n.e.x.t
*
* @return array The indexed array of check objects.
*/
public function to_array(): array;

/**
* Returns the raw map of check slugs and their check objects as a representation of this collection.
*
* @since n.e.x.t
*
* @return array Map of `$check_slug => $check_obj` pairs.
*/
public function to_map(): array;

/**
* Returns a new check collection containing the subset of checks based on the given check filter function.
*
* @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.
* @return Check_Collection New check collection, effectively a subset of this one.
*/
public function filter( callable $filter_fn ): Check_Collection;

/**
* Returns a new check collection containing the subset of checks based on the given check slugs.
*
* 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 limit to only those. If empty, the same collection is returned.
* @return Check_Collection New check collection, effectively a subset of this one.
*/
public function include( array $check_slugs ): Check_Collection;

/**
* Throws an exception if any of the given check slugs are not present, or returns the same collection otherwise.
*
* @since n.e.x.t
*
* @param array $check_slugs List of slugs to limit to only those. If empty, the same collection is returned.
* @return Check_Collection The unchanged check collection.
*
* @throws Exception Thrown when any of the given check slugs is not present in the collection.
*/
public function require( array $check_slugs ): Check_Collection;
}
7 changes: 3 additions & 4 deletions includes/Checker/Check_Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ public function register_check( $slug, Check $check );
*
* @since n.e.x.t
*
* @param int $flags The check type flag.
* @param array $check_slugs An array of check slugs to return.
* @return array An indexed array of check instances.
* @param int $flags The check type flag.
* @return Check_Collection Check collection providing an indexed array of check instances.
*/
public function get_checks( $flags = self::TYPE_ALL, array $check_slugs = array() );
public function get_checks( $flags = self::TYPE_ALL );
}
232 changes: 232 additions & 0 deletions includes/Checker/Default_Check_Collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
<?php
/**
* Class WordPress\Plugin_Check\Checker\Default_Check_Collection
*
* @package plugin-check
*/

namespace WordPress\Plugin_Check\Checker;

use ArrayIterator;
use Exception;
use Traversable;

/**
* Default Check Collection class.
*
* @since n.e.x.t
*/
class Default_Check_Collection implements Check_Collection {

/**
* Map of `$check_slug => $check_obj` pairs.
*
* @since n.e.x.t
* @var array
*/
private $checks;

/**
* List of check slugs, in the same order as `$checks` - effectively the keys of that array.
*
* @since n.e.x.t
* @var array
*/
private $slugs;

/**
* Constructor.
*
* @since n.e.x.t
*
* @param array $checks Map of `$check_slug => $check_obj` pairs for the collection.
*/
public function __construct( array $checks ) {
$this->checks = $checks;
$this->slugs = array_keys( $this->checks );
}

/**
* Returns the raw indexed array representation of this collection.
*
* @since n.e.x.t
*
* @return array The indexed array of check objects.
*/
public function to_array(): array {
return array_values( $this->checks );
}

/**
* Returns the raw map of check slugs and their check objects as a representation of this collection.
*
* @since n.e.x.t
*
* @return array Map of `$check_slug => $check_obj` pairs.
*/
public function to_map(): array {
return $this->checks;
}

/**
* Returns a new check collection containing the subset of checks based on the given check filter function.
*
* @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.
* @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
)
);
}

/**
* Returns a new check collection containing the subset of checks based on the given check slugs.
*
* 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 limit to only those. If empty, the same collection is returned.
* @return Check_Collection New check collection, effectively a subset of this one.
*/
public function include( array $check_slugs ): Check_Collection {
// Return unmodified collection if no check slugs to limit to are given.
if ( ! $check_slugs ) {
return $this;
}

$check_slugs = array_flip( $check_slugs );

$checks = array();
foreach ( $this->checks as $slug => $check ) {
if ( ! isset( $check_slugs[ $slug ] ) ) {
continue;
}

$checks[ $slug ] = $check;
}

return new self( $checks );
}

/**
* Throws an exception if any of the given check slugs are not present, or returns the same collection otherwise.
*
* @since n.e.x.t
*
* @param array $check_slugs List of slugs to limit to only those. If empty, the same collection is returned.
* @return Check_Collection The unchanged check collection.
*
* @throws Exception Thrown when any of the given check slugs is not present in the collection.
*/
public function require( array $check_slugs ): Check_Collection {
foreach ( $check_slugs as $slug ) {
if ( ! isset( $this->checks[ $slug ] ) ) {
throw new Exception(
sprintf(
/* translators: %s: The Check slug. */
__( 'Check with the slug "%s" does not exist.', 'plugin-check' ),
$slug
)
);
}
}

return $this;
}

/**
* Counts the checks in the collection.
*
* @since n.e.x.t
*
* @return int Number of checks in the collection.
*/
public function count(): int {
return count( $this->checks );
}

/**
* Returns an iterator for the checks in the collection.
*
* @since n.e.x.t
*
* @return Traversable Checks iterator.
*/
public function getIterator(): Traversable {
return new ArrayIterator( $this->checks );
}

/**
* Checks whether a check exists with the given slug or index.
*
* @since n.e.x.t
*
* @param string|int $offset Either a check slug (string) or index (integer).
* @return bool True if a check exists at the given slug or index, false otherwise.
*/
public function offsetExists( $offset ) {
if ( is_string( $offset ) ) {
return isset( $this->checks[ $offset ] );
}

return isset( $this->slugs[ $offset ] );
}

/**
* Retrieves the check with the given slug or index.
*
* @since n.e.x.t
*
* @param string|int $offset Either a check slug (string) or index (integer).
* @return Check|null Check with the given slug or index, or null if it does not exist.
*/
public function offsetGet( $offset ) {
if ( is_string( $offset ) ) {
if ( isset( $this->checks[ $offset ] ) ) {
return $this->checks[ $offset ];
}
return null;
}

if ( isset( $this->slugs[ $offset ] ) ) {
return $this->checks[ $this->slugs[ $offset ] ];
}

return null;
}

/**
* Sets a check in the collection.
*
* This method does nothing as the collection is read-only.
*
* @since n.e.x.t
*
* @param string|int $offset Either a check slug (string) or index (integer).
* @param mixed $value Value to set.
*/
public function offsetSet( $offset, $value ) {
// Not implemented as this is a read-only collection.
}

/**
* Removes a check from the collection.
*
* This method does nothing as the collection is read-only.
*
* @since n.e.x.t
*
* @param string|int $offset Either a check slug (string) or index (integer).
*/
public function offsetUnset( $offset ) {
// Not implemented as this is a read-only collection.
}
}
34 changes: 5 additions & 29 deletions includes/Checker/Default_Check_Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,10 @@ public function register_check( $slug, Check $check ) {
*
* @since n.e.x.t
*
* @param int $flags The check type flag.
* @param array $check_slugs An array of check slugs to return.
* @return array An array of check instances.
*
* @throws Exception Thrown when invalid flag is passed, or Check slug does not exist.
* @param int $flags The check type flag.
* @return Check_Collection Check collection providing an indexed array of check instances.
*/
public function get_checks( $flags = self::TYPE_ALL, array $check_slugs = array() ) {
public function get_checks( $flags = self::TYPE_ALL ) {
$checks = array();

if ( $flags & self::TYPE_STATIC ) {
Expand All @@ -99,34 +96,13 @@ public function get_checks( $flags = self::TYPE_ALL, array $check_slugs = array(
$checks += $this->runtime_checks;
}

// Filter out the specific check slugs requested.
if ( ! empty( $check_slugs ) ) {
$checks = array_map(
function ( $slug ) use ( $checks ) {
if ( ! isset( $checks[ $slug ] ) ) {
throw new Exception(
mukeshpanchal27 marked this conversation as resolved.
Show resolved Hide resolved
sprintf(
/* translators: %s: The Check slug. */
__( 'Check with the slug "%s" does not exist.', 'plugin-check' ),
$slug
)
);
}

return $checks[ $slug ];
},
$check_slugs
);
}

// Return all checks, including experimental if requested.
if ( $flags & self::INCLUDE_EXPERIMENTAL ) {
return $checks;
return new Default_Check_Collection( $checks );
}

// Remove experimental checks before returning.
return array_filter(
$checks,
return ( new Default_Check_Collection( $checks ) )->filter(
static function ( $check ) {
return $check->get_stability() !== Check::STABILITY_EXPERIMENTAL;
}
Expand Down
Loading