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 caching for get_blocks #27

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
65 changes: 56 additions & 9 deletions src/data.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Data layer to process to block data.
*
* @package WP_REST_Blocks.
* @phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
*/

namespace WP_REST_Blocks\Data;
Expand All @@ -19,6 +20,27 @@
* @return array
*/
function get_blocks( $content, $post_id = 0 ) {
$do_cache = apply_filters( 'rest_api_blocks_cache', true );

if ( $do_cache ) {
$cache_key = 'rest_api_blocks_' . md5( $content );
if ( 0 !== $post_id ) {
$cache_key .= '_' . md5( serialize( get_post_meta( $post_id ) ) );
}
$multisite_cache = is_multisite() && apply_filters( 'rest_api_blocks_multisite_cache', true );

if ( $multisite_cache ) {
$output = get_site_transient( $cache_key );
} else {
$output = get_transient( $cache_key );
}

if ( ! empty( $output ) && is_array( $output ) ) {
/** This filter is documented at the end of this function */
return apply_filters( 'rest_api_blocks_output', $output, $content, $post_id, true );
}
}

$output = [];
$blocks = parse_blocks( $content );

Expand All @@ -29,7 +51,25 @@ function get_blocks( $content, $post_id = 0 ) {
}
}

return $output;
if ( $do_cache ) {
$cache_expiration = apply_filters( 'rest_api_blocks_expiration', 0 );

if ( $multisite_cache ) {
set_site_transient( $cache_key, $output, $cache_expiration );
} else {
set_transient( $cache_key, $output, $cache_expiration );
}
}

/**
* Filter to allow plugins to change the parsed blocks.
*
* @param array $output The parsed blocks.
* @param string $content The content that is parsed.
* @param int $post_id The post id. Defaults to 0 if not parsing a post.
* @param bool $cached True if output is cached.
*/
return apply_filters( 'rest_api_blocks_output', $output, $content, $post_id, false );
}

/**
Expand All @@ -51,13 +91,13 @@ function handle_do_block( array $block, $post_id = 0 ) {
$attributes = $block_object->block_type->attributes;
$supports = $block_object->block_type->supports;
if ( $supports && isset( $supports['anchor'] ) && $supports['anchor'] ) {
$attributes['anchor'] = [
'type' => 'string',
'source' => 'attribute',
'attribute' => 'id',
'selector' => '*',
'default' => '',
];
$attributes['anchor'] = [
'type' => 'string',
'source' => 'attribute',
'attribute' => 'id',
'selector' => '*',
'default' => '',
];
}

if ( $attributes ) {
Expand All @@ -80,7 +120,14 @@ function handle_do_block( array $block, $post_id = 0 ) {
}
}

return $block;
/**
* Filter to allow plugins to change the parsed block.
*
* @param array $block The parsed block.
* @param int $post_id The post id. Defaults to 0 if not parsing a post.
* @param WP_Block $block_object The block object.
*/
return apply_filters( 'rest_api_handle_block', $block, $post_id, $block_object );
}

/**
Expand Down
204 changes: 204 additions & 0 deletions tests/test-cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
<?php
/**
* Class CacheTest
*
* @package WP_REST_Blocks
* @phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
*/

namespace WP_REST_Blocks\Tests;

use WP_REST_Blocks\Posts;
use WP_REST_Blocks\Widgets;
use Yoast\WPTestUtils\WPIntegration\TestCase;

/**
* Class CacheTest
*
* @package WP_REST_Blocks\Tests
* @phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
*/
class CacheTest extends TestCase {
/**
* Static variable for post object.
*
* @var int $post_id Post id.
*/
protected static $post_ids = [];

/**
* @var array
*/
protected static $block_types;

/**
*
*/
public static function wpSetUpBeforeClass() {
self::register_block_type(
'fake/testcache',
[
'icon' => 'text',
'attributes' => [
'test_meta' => [
'source' => 'meta',
'meta' => 'test_meta',
'type' => 'number',
],
],
]
);
}

/**
*
*/
public static function wpTearDownAfterClass() {
foreach ( self::$post_ids as $post_id ) {
wp_delete_post( $post_id, true );
}
foreach ( self::$block_types as $block_type ) {
unregister_block_type( $block_type );
}
}

/**
*
*/
public function test_posts_cache() {
$mixed_post_content = '<!-- wp:fake/testcache {"align":"right"} --><!-- /wp:fake/testcache -->';

self::$post_ids['cached'] = $this->factory->post->create(
[
'post_content' => $mixed_post_content,
]
);

update_post_meta( self::$post_ids['cached'], 'test_meta', 99 );

$object = $this->get_object( self::$post_ids['cached'] );

$data = Posts\blocks_get_callback( $object );
$this->assertArrayHasKey( 'test_meta', $data[0]['attrs'] );
$this->assertEquals( 99, $data[0]['attrs']['test_meta'] );

$cached = get_transient( 'rest_api_blocks_' . md5( $object['content']['raw'] ) . '_' . md5( serialize( get_post_meta( self::$post_ids['cached'] ) ) ) );
$this->assertEquals( $data, $cached );

// Test cache invalidation after post content update
$post = get_post( self::$post_ids['cached'] );
$post->post_content .= '<!-- wp:core/separator -->';
wp_update_post( $post );

$object = $this->get_object( self::$post_ids['cached'] );
$data = Posts\blocks_get_callback( $object );
$this->assertArrayHasKey( 'test_meta', $data[0]['attrs'] );
$this->assertEquals( 99, $data[0]['attrs']['test_meta'] );

$cached = get_transient( 'rest_api_blocks_' . md5( $object['content']['raw'] ) . '_' . md5( serialize( get_post_meta( self::$post_ids['cached'] ) ) ) );
$this->assertEquals( $data, $cached );

// Test cache invalidation after post meta update
update_post_meta( self::$post_ids['cached'], 'test_meta', 100 );

$object = $this->get_object( self::$post_ids['cached'] );
$data = Posts\blocks_get_callback( $object );
$this->assertArrayHasKey( 'test_meta', $data[0]['attrs'] );
$this->assertEquals( 100, $data[0]['attrs']['test_meta'] );

$cached = get_transient( 'rest_api_blocks_' . md5( $object['content']['raw'] ) . '_' . md5( serialize( get_post_meta( self::$post_ids['cached'] ) ) ) );
$this->assertEquals( $data, $cached );
}

public function test_widget_cache() {
register_sidebar(
[
'name' => 'Test Sidebar',
'id' => 'test_sidebar',
'before_widget' => '',
'after_widget' => '',
]
);

$widget_id = 7;

wp_set_sidebars_widgets(
[
'wp_inactive_widgets' => [],
'test_sidebar' => [
"block-$widget_id",
],
'array_version' => 3,
]
);

$object = [
'id' => "block-$widget_id",
'id_base' => 'block',
'content' => '<!-- wp:paragraph -->
<p>sidebar</p>
<!-- /wp:paragraph -->',
];

update_option(
'widget_block',
[
$widget_id => [
'content' => $object['content'],
],
'_multiwidget' => 1,
]
);

$data = Widgets\blocks_widget_get_callback( $object );
$this->assertEquals( 'sidebar', $data[0]['attrs']['content'] );

$cached = get_transient( 'rest_api_blocks_' . md5( $object['content'] ) );
$this->assertEquals( $data, $cached );

// Test cache invalidation after widget content update
$object['content'] .= '<!-- wp:paragraph -->
<p>second paragraph</p>
<!-- /wp:paragraph -->';

update_option(
'widget_block',
[
$widget_id => [
'content' => $object['content'],
],
'_multiwidget' => 1,
]
);

$data = Widgets\blocks_widget_get_callback( $object );
$this->assertEquals( 'sidebar', $data[0]['attrs']['content'] );
$this->assertEquals( 'second paragraph', $data[1]['attrs']['content'] );

$cached = get_transient( 'rest_api_blocks_' . md5( $object['content'] ) );
$this->assertEquals( $data, $cached );
}

/**
* @param $id
*
* @return array
*/
protected function get_object( $id ) {
$object = [];
$post = get_post( $id );
if ( $post ) {
$object = [
'id' => $id,
'content' => [ 'raw' => $post->post_content ],
];
}

return $object;
}

protected static function register_block_type( $name, $settings ) {
self::$block_types[] = $name;
register_block_type( $name, $settings );
}
}