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

Issue-91 [FEATURE] Removes preloading of blocks on edit post screen #92

Merged
merged 10 commits into from
May 14, 2024
2 changes: 1 addition & 1 deletion .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
'native_constant_invocation' => true,
'native_function_casing' => true,
'native_function_invocation' => true,
'native_function_type_declaration_casing' => true,
'native_type_declaration_casing' => true,
]
);
$config->setFinder( $finder );
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ This library adheres to [Semantic Versioning](https://semver.org/) and [Keep a C
* `disable_pantheon_constant_overrides`: Added a feature to disable forcing use of `WP_SITEURL` and `WP_HOME` on Pantheon environments.
* `force_two_factor_authentication`: Added a feature to force Two Factor Authentication for users with `edit_posts` permissions.
* `disable_deep_pagination`: Added a feature to restrict pagination to at most 100 pages, by default. This includes a filter `alleyvate_deep_pagination_max_pages` to override this limit, as well as a new `WP_Query` argument to override the limit: `__dangerously_set_max_pages`.
* `disable_block_editor_rest_api_preload_paths` Added a feature to disable preloading Synced Patterns (Reusable
Blocks) on the block edit screen to improve performance on sites with many patterns.

## 3.0.1

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ This feature removes selected nodes from the admin bar.

This feature disables WordPress attachment pages entirely from the front end of the site.

### `disable_block_editor_rest_api_preload_paths`

This feature enhances the stability and performance of the block edit screen by disabling the preloading of Synced
Patterns (Reusable Blocks). Typically, preloading triggers `the_content` filter for each block, along with
additional processing. This can lead to unexpected behavior and performance degradation, especially on sites with
hundreds of synced patterns. Notably, an error in a single block can propagate issues across all block edit screens.
Disabling preloading makes the system more resilient—less susceptible to cascading failures—thus improving overall
admin stability. For technical details on how WP core implements preloading, refer to
`wp-admin/edit-form-blocks.php.`

### `disable_comments`

This feature disables WordPress comments entirely, including the ability to post, view, edit, list, count, modify settings for, or access URLs that are related to comments completely.
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
],
"phpcbf": "phpcbf",
"phpcs": "phpcs",
"phpstan": "phpstan --memory-limit=512M",
"phpstan": "phpstan --memory-limit=768M",
"phpunit": "phpunit",
"test": [
"@lint",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Class file for Disable_Block_Editor_Rest_Api_Preload_Paths
*
* (c) Alley <[email protected]>
*
* 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 Alley\WP\Types\Feature;

/**
* Disables the preloading of the blocks which happens on all edit post pages.
*/
final class Disable_Block_Editor_Rest_Api_Preload_Paths implements Feature {
/**
* Boot the feature.
*/
public function boot(): void {
add_filter(
'block_editor_rest_api_preload_paths',
[ self::class, 'filter__block_editor_rest_api_preload_paths' ],
9999
);
}

/**
* Filter the block editor REST API preload paths.
*
* @param mixed[] $paths The paths to preload.
*
* @return mixed[] The filtered paths.
*/
public static function filter__block_editor_rest_api_preload_paths( $paths ) {
if ( ! \is_array( $paths ) ) {
return $paths;
}
return array_values(
array_filter(
$paths,
function ( $v ) {
// Remove the blocks preload path for performance reasons.
return ! \is_string( $v ) || ! str_starts_with( $v, '/wp/v2/blocks?context=edit' );
mslinnea marked this conversation as resolved.
Show resolved Hide resolved
},
)
);
}
}
4 changes: 4 additions & 0 deletions src/alley/wp/alleyvate/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ function load(): void {
'disable_deep_pagination',
new Features\Disable_Deep_Pagination(),
),
new Feature(
'disable_block_editor_rest_api_preload_paths',
new Features\Disable_Block_Editor_Rest_Api_Preload_Paths(),
),
);

$plugin->boot();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php
/**
* Class file for Test_Disable_Block_Editor_Rest_Api_Preload_Paths
*
* (c) Alley <[email protected]>
*
* 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;

/**
* Tests for disabling block editor REST API preload paths.
*/
final class Test_Disable_Block_Editor_Rest_Api_Preload_Paths extends Test_Case {

/**
* Feature instance.
*
* @var Disable_Block_Editor_Rest_Api_Preload_Paths
*/
private Disable_Block_Editor_Rest_Api_Preload_Paths $feature;

/**
* Set up.
*/
protected function setUp(): void {
parent::setUp();

$this->feature = new Disable_Block_Editor_Rest_Api_Preload_Paths();
}

/**
* Test that the feature short-circuits a redirect that would otherwise occur.
*/
public function test_disable_block_editor_rest_api_preload_paths(): void { // phpcs:ignore Generic.NamingConventions.ConstructorName.OldStyle

$post = self::factory()->post->create_and_get(
[
'post_title' => 'Testing REST API Preload Paths',
]
);

/**
* This code mimics the logic in wp-admin/edit-form-blocks.php to generate the preload paths.
*/
$rest_path = rest_get_route_for_post( $post );
$post_type = get_post_type( $post );
$preload_paths = [
'/wp/v2/types?context=view',
'/wp/v2/taxonomies?context=view',
add_query_arg(
[
'context' => 'edit',
'per_page' => - 1,
],
rest_get_route_for_post_type_items( 'wp_block' )
),
add_query_arg( 'context', 'edit', $rest_path ),
sprintf( '/wp/v2/types/%s?context=edit', $post_type ),
'/wp/v2/users/me',
[ rest_get_route_for_post_type_items( 'attachment' ), 'OPTIONS' ],
[ rest_get_route_for_post_type_items( 'page' ), 'OPTIONS' ],
[ rest_get_route_for_post_type_items( 'wp_block' ), 'OPTIONS' ],
[ rest_get_route_for_post_type_items( 'wp_template' ), 'OPTIONS' ],
sprintf( '%s/autosaves?context=edit', $rest_path ),
'/wp/v2/settings',
[ '/wp/v2/settings', 'OPTIONS' ],
];
mslinnea marked this conversation as resolved.
Show resolved Hide resolved

// Apply the filter that modifies the preload paths before the feature is activated.
$preload_paths = apply_filters( 'block_editor_rest_api_preload_paths', $preload_paths ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound

// Assert that the blocks rest path is in the preload paths.
$this->assertContains( '/wp/v2/blocks?context=edit&per_page=-1', $preload_paths );

// Check the other preload paths to ensure they are present.
$this->check_preloads_paths( $preload_paths, $rest_path, $post_type );

// Activate feature.
$this->feature->boot();

// Apply the filter that modifies the preload paths.
$preload_paths = apply_filters( 'block_editor_rest_api_preload_paths', $preload_paths ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound

// The blocks rest path should no longer be in the preload paths.
$this->assertNotContains( '/wp/v2/blocks?context=edit&per_page=-1', $preload_paths );
mslinnea marked this conversation as resolved.
Show resolved Hide resolved

// Check the other preload paths to ensure they are present.
$this->check_preloads_paths( $preload_paths, $rest_path, $post_type );
}

/**
* Check the preload paths.
*
* @param mixed $preload_paths The preload paths.
* @param string $rest_path The rest path.
* @param string $post_type The post type.
*
* @return void
*/
public function check_preloads_paths( mixed $preload_paths, string $rest_path, string $post_type ): void {
// The other preload paths should still be present.
$this->assertContains( '/wp/v2/types?context=view', $preload_paths );
$this->assertContains( '/wp/v2/taxonomies?context=view', $preload_paths );
$this->assertContains( add_query_arg( 'context', 'edit', $rest_path ), $preload_paths );
$this->assertContains( sprintf( '/wp/v2/types/%s?context=edit', $post_type ), $preload_paths );
$this->assertContains( '/wp/v2/users/me', $preload_paths );
$this->assertContains( [ rest_get_route_for_post_type_items( 'attachment' ), 'OPTIONS' ], $preload_paths );
$this->assertContains( [ rest_get_route_for_post_type_items( 'page' ), 'OPTIONS' ], $preload_paths );
$this->assertContains( [ rest_get_route_for_post_type_items( 'wp_block' ), 'OPTIONS' ], $preload_paths );
$this->assertContains( [ rest_get_route_for_post_type_items( 'wp_template' ), 'OPTIONS' ], $preload_paths );
$this->assertContains( sprintf( '%s/autosaves?context=edit', $rest_path ), $preload_paths );
$this->assertContains( '/wp/v2/settings', $preload_paths );
$this->assertContains( [ '/wp/v2/settings', 'OPTIONS' ], $preload_paths );
}
}
Loading