From 43e3c57c4938a450490c49e1f7ba68078678dd9e Mon Sep 17 00:00:00 2001 From: David Herrera Date: Wed, 7 Feb 2024 22:25:25 -0500 Subject: [PATCH] Use a feature class to represent standard feature behaviors --- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 11 ++ composer.json | 6 +- src/alley/wp/alleyvate/class-feature.php | 94 ++++++++++++++ .../wp/alleyvate/class-site-health-panel.php | 42 ++++++ .../features/class-clean-admin-bar.php | 2 +- .../class-disable-attachment-routing.php | 2 +- .../features/class-disable-comments.php | 2 +- .../class-disable-custom-fields-meta-box.php | 2 +- .../class-disable-dashboard-widgets.php | 2 +- ...s-disable-password-change-notification.php | 2 +- .../features/class-disable-sticky-posts.php | 2 +- .../features/class-disable-trackbacks.php | 2 +- .../features/class-disallow-file-edit.php | 2 +- .../alleyvate/features/class-login-nonce.php | 2 +- .../features/class-prevent-framing.php | 2 +- .../class-redirect-guess-shortcircuit.php | 2 +- .../alleyvate/features/class-site-health.php | 60 --------- .../class-user-enumeration-restrictions.php | 2 +- src/alley/wp/alleyvate/interface-feature.php | 23 ---- src/alley/wp/alleyvate/load.php | 120 +++++++++--------- .../concerns/trait-remove-meta-box.php | 2 +- .../alleyvate/features/test-site-health.php | 63 --------- wp-alleyvate.php | 7 +- 24 files changed, 228 insertions(+), 228 deletions(-) create mode 100644 src/alley/wp/alleyvate/class-feature.php create mode 100644 src/alley/wp/alleyvate/class-site-health-panel.php delete mode 100644 src/alley/wp/alleyvate/features/class-site-health.php delete mode 100644 src/alley/wp/alleyvate/interface-feature.php delete mode 100644 tests/alley/wp/alleyvate/features/test-site-health.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d2c2965e..f5908701 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: true matrix: - php: [ 8.0, 8.1, 8.2, 8.3 ] + php: [ 8.1, 8.2, 8.3 ] wp_version: [ "latest" ] multisite: [ false, true ] name: WordPress ${{ matrix.wp_version }} @ PHP ${{ matrix.php }} (WP_MULTISITE=${{ matrix.multisite }}) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b8a382..11cf4c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,14 @@ This library adheres to [Semantic Versioning](https://semver.org/) and [Keep a C ## Unreleased +### Added + +* `Alley\WP\Alleyvate\Feature` class implementing the `Alley\WP\Types\Feature` interface. + ### Changed +* The minimum PHP version is now 8.1. +* Feature classes now implement the `Alley\WP\Types\Feature` interface instead of `Alley\WP\Alleyvate\Feature`. * Unit tests: misc changes and fixes. * Unit tests: the `$feature` property uses the main feature class for better IDE intelephense support. * Unit tests: all test cases use `declare( strict_types=1 );`. @@ -13,6 +19,11 @@ This library adheres to [Semantic Versioning](https://semver.org/) and [Keep a C * Unit tests: support for `convertDeprecationsToExceptions="true"` added. Tests will fail if there are PHP deprecation warnings. +### Removed + +* `site_health`: Removed as a dedicated feature and now implemented directly in the plugin. +* `Alley\WP\Alleyvate\Feature` interface. + ## 2.4.0 ### Added diff --git a/composer.json b/composer.json index 85c920ef..9b313681 100644 --- a/composer.json +++ b/composer.json @@ -15,11 +15,13 @@ "composer/installers": true, "dealerdirect/phpcodesniffer-composer-installer": true }, - "lock": false + "lock": false, + "sort-packages": true }, "require": { - "php": "^8.0", + "php": "^8.1", "alleyinteractive/composer-wordpress-autoloader": "^1.0", + "alleyinteractive/wp-type-extensions": "^1.0", "composer/installers": "^1.0" }, "require-dev": { diff --git a/src/alley/wp/alleyvate/class-feature.php b/src/alley/wp/alleyvate/class-feature.php new file mode 100644 index 00000000..19fd0037 --- /dev/null +++ b/src/alley/wp/alleyvate/class-feature.php @@ -0,0 +1,94 @@ +handle ); + + /** + * Filters whether to load the given Alleyvate feature. + * + * The dynamic portion of the hook name, `$this->$this->handle`, refers to the + * machine name for the feature. + * + * @param bool $load Whether to load the feature. Default true. + */ + $load = apply_filters( "alleyvate_load_{$this->handle}", $load ); + + if ( $load ) { + $this->booted = true; + $this->origin->boot(); + } + } + + /** + * Add debug information to the Site Health screen. + * + * @param array $info Debug information. + * @return array + */ + public function add_debug_information( $info ): array { + if ( ! \is_array( $info ) ) { + $info = []; + } + + $info['wp-alleyvate']['fields'] ??= []; + $info['wp-alleyvate']['fields'][] = [ + 'label' => sprintf( + /* translators: %s: Feature name. */ + __( 'Feature: %s', 'alley' ), + $this->handle, + ), + 'value' => $this->booted ? __( 'Enabled', 'alley' ) : __( 'Disabled', 'alley' ), + ]; + + return $info; + } +} diff --git a/src/alley/wp/alleyvate/class-site-health-panel.php b/src/alley/wp/alleyvate/class-site-health-panel.php new file mode 100644 index 00000000..a08182ad --- /dev/null +++ b/src/alley/wp/alleyvate/class-site-health-panel.php @@ -0,0 +1,42 @@ + __( 'Alleyvate', 'alley' ), + 'description' => __( 'Diagnostic information about the Alleyvate plugin and which features are enabled.', 'alley' ), + 'fields' => [], + ]; + + return $info; + } +} diff --git a/src/alley/wp/alleyvate/features/class-clean-admin-bar.php b/src/alley/wp/alleyvate/features/class-clean-admin-bar.php index 24b7630a..84a4974c 100644 --- a/src/alley/wp/alleyvate/features/class-clean-admin-bar.php +++ b/src/alley/wp/alleyvate/features/class-clean-admin-bar.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Cleans admin bar. diff --git a/src/alley/wp/alleyvate/features/class-disable-attachment-routing.php b/src/alley/wp/alleyvate/features/class-disable-attachment-routing.php index e0415fb6..75d565c0 100644 --- a/src/alley/wp/alleyvate/features/class-disable-attachment-routing.php +++ b/src/alley/wp/alleyvate/features/class-disable-attachment-routing.php @@ -14,7 +14,7 @@ use WP_Admin_Bar; use WP_Query; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Disable attachment routing. diff --git a/src/alley/wp/alleyvate/features/class-disable-comments.php b/src/alley/wp/alleyvate/features/class-disable-comments.php index 06fd0aff..bf039ae1 100644 --- a/src/alley/wp/alleyvate/features/class-disable-comments.php +++ b/src/alley/wp/alleyvate/features/class-disable-comments.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Fully disables comments. diff --git a/src/alley/wp/alleyvate/features/class-disable-custom-fields-meta-box.php b/src/alley/wp/alleyvate/features/class-disable-custom-fields-meta-box.php index 7f752c36..71ec6e57 100644 --- a/src/alley/wp/alleyvate/features/class-disable-custom-fields-meta-box.php +++ b/src/alley/wp/alleyvate/features/class-disable-custom-fields-meta-box.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Disable the custom fields meta box. diff --git a/src/alley/wp/alleyvate/features/class-disable-dashboard-widgets.php b/src/alley/wp/alleyvate/features/class-disable-dashboard-widgets.php index 75cdac64..6f32a94d 100644 --- a/src/alley/wp/alleyvate/features/class-disable-dashboard-widgets.php +++ b/src/alley/wp/alleyvate/features/class-disable-dashboard-widgets.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Disable selected unpopular dashboard widgets. diff --git a/src/alley/wp/alleyvate/features/class-disable-password-change-notification.php b/src/alley/wp/alleyvate/features/class-disable-password-change-notification.php index f2e042ff..1985e1d7 100644 --- a/src/alley/wp/alleyvate/features/class-disable-password-change-notification.php +++ b/src/alley/wp/alleyvate/features/class-disable-password-change-notification.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Fully disables password change notifications. diff --git a/src/alley/wp/alleyvate/features/class-disable-sticky-posts.php b/src/alley/wp/alleyvate/features/class-disable-sticky-posts.php index 49385620..d0b0565f 100644 --- a/src/alley/wp/alleyvate/features/class-disable-sticky-posts.php +++ b/src/alley/wp/alleyvate/features/class-disable-sticky-posts.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Fully disables sticky posts. diff --git a/src/alley/wp/alleyvate/features/class-disable-trackbacks.php b/src/alley/wp/alleyvate/features/class-disable-trackbacks.php index ca05733e..9c51547d 100644 --- a/src/alley/wp/alleyvate/features/class-disable-trackbacks.php +++ b/src/alley/wp/alleyvate/features/class-disable-trackbacks.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Fully disables pingbacks and trackbacks. diff --git a/src/alley/wp/alleyvate/features/class-disallow-file-edit.php b/src/alley/wp/alleyvate/features/class-disallow-file-edit.php index 79da846b..92423a4a 100644 --- a/src/alley/wp/alleyvate/features/class-disallow-file-edit.php +++ b/src/alley/wp/alleyvate/features/class-disallow-file-edit.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Disallow theme/plugin editing in the filesystem to safeguard against unexpected code changes. diff --git a/src/alley/wp/alleyvate/features/class-login-nonce.php b/src/alley/wp/alleyvate/features/class-login-nonce.php index 0e462950..3c1f61b0 100644 --- a/src/alley/wp/alleyvate/features/class-login-nonce.php +++ b/src/alley/wp/alleyvate/features/class-login-nonce.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Adds a nonce field to the login form. diff --git a/src/alley/wp/alleyvate/features/class-prevent-framing.php b/src/alley/wp/alleyvate/features/class-prevent-framing.php index e328b338..cb36b44e 100644 --- a/src/alley/wp/alleyvate/features/class-prevent-framing.php +++ b/src/alley/wp/alleyvate/features/class-prevent-framing.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Headers to prevent iframe-ing of the site. diff --git a/src/alley/wp/alleyvate/features/class-redirect-guess-shortcircuit.php b/src/alley/wp/alleyvate/features/class-redirect-guess-shortcircuit.php index 36f9aedd..e9794052 100644 --- a/src/alley/wp/alleyvate/features/class-redirect-guess-shortcircuit.php +++ b/src/alley/wp/alleyvate/features/class-redirect-guess-shortcircuit.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; /** * Disable `redirect_guess_404_permalink()`, whose behavior often confuses clients diff --git a/src/alley/wp/alleyvate/features/class-site-health.php b/src/alley/wp/alleyvate/features/class-site-health.php deleted file mode 100644 index 2a23f43d..00000000 --- a/src/alley/wp/alleyvate/features/class-site-health.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * @package wp-alleyvate - */ - -namespace Alley\WP\Alleyvate\Features; - -use Alley\WP\Alleyvate\Feature; - -use function Alley\WP\Alleyvate\available_features; -use function Alley\WP\Alleyvate\should_load_feature; - -/** - * Site Health feature. - */ -final class Site_Health implements Feature { - /** - * Boot the feature. - */ - public function boot(): void { - add_filter( 'debug_information', [ $this, 'add_debug_information' ] ); - } - - /** - * Add debug information to the Site Health screen. - * - * @param array $info Debug information. - * @return array - */ - public function add_debug_information( $info ): array { - if ( ! \is_array( $info ) ) { - $info = []; - } - - $info['wp-alleyvate'] = [ - 'label' => __( 'Alleyvate', 'alley' ), - 'description' => __( 'Diagnostic information about the Alleyvate plugin and which features are enabled.', 'alley' ), - 'fields' => array_map( - fn ( string $handle ) => [ - 'label' => sprintf( - /* translators: %s: Feature name. */ - __( 'Feature: %s', 'alley' ), - $handle, - ), - 'value' => should_load_feature( $handle ) ? __( 'Enabled', 'alley' ) : __( 'Disabled', 'alley' ), - ], - array_keys( available_features() ), - ), - ]; - - return $info; - } -} diff --git a/src/alley/wp/alleyvate/features/class-user-enumeration-restrictions.php b/src/alley/wp/alleyvate/features/class-user-enumeration-restrictions.php index 5ae98544..a8f6c4ec 100644 --- a/src/alley/wp/alleyvate/features/class-user-enumeration-restrictions.php +++ b/src/alley/wp/alleyvate/features/class-user-enumeration-restrictions.php @@ -12,7 +12,7 @@ namespace Alley\WP\Alleyvate\Features; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; use WP_Error; use WP_HTTP_Response; use WP_REST_Request; diff --git a/src/alley/wp/alleyvate/interface-feature.php b/src/alley/wp/alleyvate/interface-feature.php deleted file mode 100644 index f3947054..00000000 --- a/src/alley/wp/alleyvate/interface-feature.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * @package wp-alleyvate - */ - -namespace Alley\WP\Alleyvate; - -/** - * Describes an Alleyvate feature. - */ -interface Feature { - /** - * Boot the feature. - */ - public function boot(): void; -} diff --git a/src/alley/wp/alleyvate/load.php b/src/alley/wp/alleyvate/load.php index 048773a3..b8b6c644 100644 --- a/src/alley/wp/alleyvate/load.php +++ b/src/alley/wp/alleyvate/load.php @@ -12,72 +12,70 @@ namespace Alley\WP\Alleyvate; -/** - * Get the available features to load. - * - * @return array - */ -function available_features(): array { - return [ - 'clean_admin_bar' => new Features\Clean_Admin_Bar(), - 'disable_attachment_routing' => new Features\Disable_Attachment_Routing(), - 'disable_comments' => new Features\Disable_Comments(), - 'disable_custom_fields_meta_box' => new Features\Disable_Custom_Fields_Meta_Box(), - 'disable_dashboard_widgets' => new Features\Disable_Dashboard_Widgets(), - 'disable_password_change_notification' => new Features\Disable_Password_Change_Notification(), - 'disable_sticky_posts' => new Features\Disable_Sticky_Posts(), - 'disable_trackbacks' => new Features\Disable_Trackbacks(), - 'disallow_file_edit' => new Features\Disallow_File_Edit(), - 'login_nonce' => new Features\Login_Nonce(), - 'prevent_framing' => new Features\Prevent_Framing(), - 'redirect_guess_shortcircuit' => new Features\Redirect_Guess_Shortcircuit(), - 'site_health' => new Features\Site_Health(), - 'user_enumeration_restrictions' => new Features\User_Enumeration_Restrictions(), - ]; -} - -/** - * Determine whether to load a feature. - * - * @param string $handle Feature handle. - * @return bool - */ -function should_load_feature( string $handle ): bool { - $load = true; - - /** - * Filters whether to load an Alleyvate feature. - * - * @param bool $load Whether to load the feature. Default true. - * @param string $handle Feature handle. - */ - $load = apply_filters( 'alleyvate_load_feature', $load, $handle ); - - /** - * Filters whether to load the given Alleyvate feature. - * - * The dynamic portion of the hook name, `$handle`, refers to the - * machine name for the feature. - * - * @param bool $load Whether to load the feature. Default true. - */ - $load = apply_filters( "alleyvate_load_{$handle}", $load ); - - return (bool) $load; -} - /** * Load plugin features. */ function load(): void { - // Bail if the Alleyvate feature interface isn't loaded to prevent a fatal error. - if ( ! interface_exists( Feature::class ) ) { + // Bail if the Alleyvate feature class isn't loaded to prevent a fatal error. + if ( ! class_exists( Feature::class ) ) { return; } - foreach ( available_features() as $handle => $feature ) { - if ( should_load_feature( $handle ) ) { - $feature->boot(); - } - } + $plugin = new \Alley\WP\Features\Features( + new Site_Health_Panel(), + new Feature( + 'clean_admin_bar', + new Features\Clean_Admin_Bar(), + ), + new Feature( + 'disable_attachment_routing', + new Features\Disable_Attachment_Routing(), + ), + new Feature( + 'disable_comments', + new Features\Disable_Comments(), + ), + new Feature( + 'disable_custom_fields_meta_box', + new Features\Disable_Custom_Fields_Meta_Box(), + ), + new Feature( + 'disable_dashboard_widgets', + new Features\Disable_Dashboard_Widgets(), + ), + new Feature( + 'disable_password_change_notification', + new Features\Disable_Password_Change_Notification(), + ), + new Feature( + 'disable_sticky_posts', + new Features\Disable_Sticky_Posts(), + ), + new Feature( + 'disable_trackbacks', + new Features\Disable_Trackbacks(), + ), + new Feature( + 'disallow_file_edit', + new Features\Disallow_File_Edit(), + ), + new Feature( + 'login_nonce', + new Features\Login_Nonce(), + ), + new Feature( + 'prevent_framing', + new Features\Prevent_Framing(), + ), + new Feature( + 'redirect_guess_shortcircuit', + new Features\Redirect_Guess_Shortcircuit(), + ), + new Feature( + 'user_enumeration_restrictions', + new Features\User_Enumeration_Restrictions(), + ), + ); + + $plugin->boot(); } diff --git a/tests/alley/wp/alleyvate/features/concerns/trait-remove-meta-box.php b/tests/alley/wp/alleyvate/features/concerns/trait-remove-meta-box.php index ecc7eaca..9329466e 100644 --- a/tests/alley/wp/alleyvate/features/concerns/trait-remove-meta-box.php +++ b/tests/alley/wp/alleyvate/features/concerns/trait-remove-meta-box.php @@ -16,7 +16,7 @@ namespace Alley\WP\Alleyvate\Features\Concerns; -use Alley\WP\Alleyvate\Feature; +use Alley\WP\Types\Feature; use Mantle\Testing\Concerns\Admin_Screen; /** diff --git a/tests/alley/wp/alleyvate/features/test-site-health.php b/tests/alley/wp/alleyvate/features/test-site-health.php deleted file mode 100644 index 8e730036..00000000 --- a/tests/alley/wp/alleyvate/features/test-site-health.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * @package wp-alleyvate - */ - -declare( strict_types=1 ); - -namespace Alley\WP\Alleyvate\Features; - -use Mantle\Testkit\Test_Case; - -use function Alley\WP\Alleyvate\available_features; - -/** - * Test for site health feature. - */ -final class Test_Site_Health extends Test_Case { - - /** - * Feature instance. - * - * @var Site_Health - */ - private Site_Health $feature; - - /** - * Set up. - */ - protected function setUp(): void { - parent::setUp(); - - $this->feature = new Site_Health(); - } - - /** - * Test the site health feature. - */ - public function test_site_health_feature(): void { - $features = available_features(); - - $this->expectApplied( 'alleyvate_load_feature' )->times( \count( $features ) ); - - foreach ( $features as $handle => $class ) { - $this->expectApplied( "alleyvate_load_{$handle}" )->once(); - } - - $this->feature->boot(); - - // Mock the Site Health screen. - $data = apply_filters( 'debug_information', [] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound - - $this->assertNotEmpty( $data['wp-alleyvate'] ?? null ); - $this->assertNotEmpty( $data['wp-alleyvate']['fields'] ?? null ); - $this->assertCount( \count( $features ), $data['wp-alleyvate']['fields'] ); - } -} diff --git a/wp-alleyvate.php b/wp-alleyvate.php index 76106aa9..aca7bcf8 100644 --- a/wp-alleyvate.php +++ b/wp-alleyvate.php @@ -25,12 +25,11 @@ } // Load Composer dependencies. -if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) { - require_once __DIR__ . '/vendor/autoload.php'; +if ( file_exists( __DIR__ . '/vendor/wordpress-autoload.php' ) ) { + require_once __DIR__ . '/vendor/wordpress-autoload.php'; } // Load the feature loader. require_once __DIR__ . '/src/alley/wp/alleyvate/load.php'; -// Alleyvate features load after all plugins and themes have had a chance to add filters. -add_action( 'after_setup_theme', 'Alley\WP\Alleyvate\load' ); +\Alley\WP\Alleyvate\load();