@automattic/newspack-blocks 4.2.1 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [4.2.2](https://github.com/Automattic/newspack-blocks/compare/v4.2.1...v4.2.2) (2024-10-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **block-caching:** only serve from cache if all blocks are cached ([#1899](https://github.com/Automattic/newspack-blocks/issues/1899)) ([fb64d0c](https://github.com/Automattic/newspack-blocks/commit/fb64d0ca4ffc2f16870bcc51db83f090054a5d26))
7
+
1
8
  ## [4.2.1](https://github.com/Automattic/newspack-blocks/compare/v4.2.0...v4.2.1) (2024-10-09)
2
9
 
3
10
 
@@ -12,11 +12,31 @@ class Newspack_Blocks_Caching {
12
12
 
13
13
  const CACHE_GROUP = 'newspack_blocks';
14
14
 
15
+ /**
16
+ * Store the cache status for all blocks for this request.
17
+ *
18
+ * @var bool
19
+ */
20
+ private static $can_serve_all_blocks_from_cache = true;
21
+
22
+ /**
23
+ * Store the current block index. This will be incremented with each cache reading,
24
+ * in order to add specificity to the cache key. The cache key consists of the
25
+ * hashed block attributes – which may be duplicated on a page – and a unique index.
26
+ * With index only, replacing the block would *not* invalidate cache, which is undesired.
27
+ * With hashed block attributes only, duplicated block configurations would result in
28
+ * duplication of rendered posts.
29
+ *
30
+ * @var int
31
+ */
32
+ private static $current_block_index = 0;
33
+
15
34
  /**
16
35
  * Add hooks and filters.
17
36
  */
18
37
  public static function init() {
19
38
  if ( defined( 'NEWSPACK_BLOCKS_CACHE_BLOCKS' ) && NEWSPACK_BLOCKS_CACHE_BLOCKS ) {
39
+ add_action( 'template_redirect', [ __CLASS__, 'check_all_blocks_cache_status' ] );
20
40
  add_filter( 'pre_render_block', [ __CLASS__, 'maybe_serve_cached_block' ], 10, 2 );
21
41
  add_filter( 'render_block', [ __CLASS__, 'maybe_cache_block' ], 9999, 2 );
22
42
 
@@ -27,18 +47,65 @@ class Newspack_Blocks_Caching {
27
47
  }
28
48
 
29
49
  /**
30
- * Determine whether a block should be cached.
50
+ * Traverse all blocks on a page to check their cache status.
51
+ * This has to happen before any blocks are rendered.
52
+ */
53
+ public static function check_all_blocks_cache_status() {
54
+ if ( is_singular() ) {
55
+ $post = get_post();
56
+ if ( $post && property_exists( $post, 'post_content' ) ) {
57
+ self::check_block_cache_status( parse_blocks( $post->post_content ) );
58
+ // Reset the index after initial checks.
59
+ self::$current_block_index = 0;
60
+ }
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Check if a block can be cached, recursively.
31
66
  *
32
- * @param array $block_data Parsed block data.
33
- * @return bool True if block should be cached. False otherwise.
67
+ * @param array $blocks Array of block data.
34
68
  */
35
- protected static function should_cache_block( $block_data ) {
69
+ private static function check_block_cache_status( $blocks ) {
70
+ $cacheable_block_names = self::get_cacheable_blocks_names();
71
+ foreach ( $blocks as $block_data ) {
72
+ // Special treatment for reusable blocks, which are blocks stored in the posts table.
73
+ if ( $block_data['blockName'] === 'core/block' ) {
74
+ $reusable_block_post = get_post( $block_data['attrs']['ref'] );
75
+ if ( $reusable_block_post && property_exists( $reusable_block_post, 'post_content' ) ) {
76
+ self::check_block_cache_status( parse_blocks( $reusable_block_post->post_content ) );
77
+ }
78
+ }
79
+ if ( in_array( $block_data['blockName'], $cacheable_block_names, true ) ) {
80
+ if ( ! self::get_cached_block_data( $block_data ) ) {
81
+ self::$can_serve_all_blocks_from_cache = false;
82
+ }
83
+ }
84
+ if ( ! empty( $block_data['innerBlocks'] ) ) {
85
+ self::check_block_cache_status( $block_data['innerBlocks'] );
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Get cacheable blocks' names.
92
+ */
93
+ public static function get_cacheable_blocks_names() {
36
94
  $cacheable_blocks = [
37
95
  'newspack-blocks/homepage-articles',
38
96
  'newspack-blocks/carousel',
39
97
  ];
40
- $cacheable_blocks = apply_filters( 'newspack_blocks_cacheable_blocks', $cacheable_blocks );
41
- return in_array( $block_data['blockName'], $cacheable_blocks, true );
98
+ return apply_filters( 'newspack_blocks_cacheable_blocks', $cacheable_blocks );
99
+ }
100
+
101
+ /**
102
+ * Determine whether a block should be cached.
103
+ *
104
+ * @param array $block_data Parsed block data.
105
+ * @return bool True if block should be cached. False otherwise.
106
+ */
107
+ protected static function should_cache_block( $block_data ) {
108
+ return in_array( $block_data['blockName'], self::get_cacheable_blocks_names(), true );
42
109
  }
43
110
 
44
111
  /**
@@ -49,7 +116,8 @@ class Newspack_Blocks_Caching {
49
116
  */
50
117
  protected static function get_cache_key( $block_data ) {
51
118
  $block_attributes = $block_data['attrs'];
52
- $cache_key = 'np_cached_block_' . md5( wp_json_encode( $block_attributes ) );
119
+ $cache_key = 'np_cached_block_' . md5( wp_json_encode( $block_attributes ) ) . '_' . self::$current_block_index;
120
+ self::$current_block_index++;
53
121
  return $cache_key;
54
122
  }
55
123
 
@@ -92,15 +160,13 @@ class Newspack_Blocks_Caching {
92
160
  }
93
161
 
94
162
  /**
95
- * Serve a cached block if a valid one exists.
163
+ * Is the block available in the cache?
96
164
  *
97
- * @param string|null $block_html Block HTML. If you return something non-null here it will short-circuit block rendering.
98
- * @param array $block_data Parsed block data.
99
- * @return string|null Block markup if served from cache. Default (usually null), otherwise.
165
+ * @param array $block_data Parsed block data.
100
166
  */
101
- public static function maybe_serve_cached_block( $block_html, $block_data ) {
167
+ public static function get_cached_block_data( $block_data ) {
102
168
  if ( ! self::should_cache_block( $block_data ) ) {
103
- return $block_html;
169
+ return false;
104
170
  }
105
171
 
106
172
  $cache_key = self::get_cache_key( $block_data );
@@ -110,7 +176,7 @@ class Newspack_Blocks_Caching {
110
176
  $cached_data = wp_cache_get( $cache_key, $cache_group );
111
177
  if ( ! is_array( $cached_data ) || ! isset( $cached_data['timestamp_generated'], $cached_data['cached_content'] ) || empty( $cached_data['cached_content'] ) ) {
112
178
  self::debug_log( sprintf( 'Cached data not found for item %s in group %s', $cache_key, $cache_group ) );
113
- return $block_html;
179
+ return false;
114
180
  }
115
181
 
116
182
  // Double-check to make sure cached data is still valid.
@@ -119,10 +185,30 @@ class Newspack_Blocks_Caching {
119
185
  Newspack\Logger::log( sprintf( 'Flushing cache for item %s in group %s because it expired', $cache_key, $cache_group ) );
120
186
  }
121
187
  wp_cache_delete( $cache_key, $cache_group );
122
- return $block_html;
188
+ return false;
123
189
  }
190
+ self::debug_log( sprintf( 'Found cached block: item %s in group %s', $cache_key, $cache_group ) );
191
+ return $cached_data;
192
+ }
124
193
 
125
- self::debug_log( sprintf( 'Serving cached block: item %s in group %s', $cache_key, $cache_group ) );
194
+ /**
195
+ * Serve a cached block if a valid one exists.
196
+ *
197
+ * @param string|null $block_html Block HTML. If you return something non-null here it will short-circuit block rendering.
198
+ * @param array $block_data Parsed block data.
199
+ * @return string|null Block markup if served from cache. Default (usually null), otherwise.
200
+ */
201
+ public static function maybe_serve_cached_block( $block_html, $block_data ) {
202
+ if ( ! self::should_cache_block( $block_data ) ) {
203
+ return $block_html;
204
+ }
205
+ if ( ! self::$can_serve_all_blocks_from_cache ) {
206
+ return $block_html;
207
+ }
208
+ $cached_data = self::get_cached_block_data( $block_data );
209
+ if ( ! $cached_data ) {
210
+ return $block_html;
211
+ }
126
212
 
127
213
  Newspack_Blocks::enqueue_view_assets( 'homepage-articles' );
128
214
 
@@ -7,7 +7,7 @@
7
7
  * Author URI: https://newspack.com/
8
8
  * Text Domain: newspack-blocks
9
9
  * Domain Path: /languages
10
- * Version: 4.2.1
10
+ * Version: 4.2.2
11
11
  *
12
12
  * @package Newspack_Blocks
13
13
  */
@@ -15,7 +15,7 @@
15
15
  define( 'NEWSPACK_BLOCKS__PLUGIN_FILE', __FILE__ );
16
16
  define( 'NEWSPACK_BLOCKS__BLOCKS_DIRECTORY', 'dist/' );
17
17
  define( 'NEWSPACK_BLOCKS__PLUGIN_DIR', plugin_dir_path( NEWSPACK_BLOCKS__PLUGIN_FILE ) );
18
- define( 'NEWSPACK_BLOCKS__VERSION', '4.2.1' );
18
+ define( 'NEWSPACK_BLOCKS__VERSION', '4.2.2' );
19
19
 
20
20
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks.php';
21
21
  require_once NEWSPACK_BLOCKS__PLUGIN_DIR . 'includes/class-newspack-blocks-api.php';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/newspack-blocks",
3
- "version": "4.2.1",
3
+ "version": "4.2.2",
4
4
  "author": "Automattic",
5
5
  "devDependencies": {
6
6
  "@rushstack/eslint-patch": "^1.10.4",
@@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
22
22
 
23
23
  require_once __DIR__ . '/composer/autoload_real.php';
24
24
 
25
- return ComposerAutoloaderInit8b6b14905fc0a9c5eacbb4c428bdc56e::getLoader();
25
+ return ComposerAutoloaderInit0bb7139426d46ba9e60aab1249f589e1::getLoader();
@@ -2,7 +2,7 @@
2
2
 
3
3
  // autoload_real.php @generated by Composer
4
4
 
5
- class ComposerAutoloaderInit8b6b14905fc0a9c5eacbb4c428bdc56e
5
+ class ComposerAutoloaderInit0bb7139426d46ba9e60aab1249f589e1
6
6
  {
7
7
  private static $loader;
8
8
 
@@ -22,12 +22,12 @@ class ComposerAutoloaderInit8b6b14905fc0a9c5eacbb4c428bdc56e
22
22
  return self::$loader;
23
23
  }
24
24
 
25
- spl_autoload_register(array('ComposerAutoloaderInit8b6b14905fc0a9c5eacbb4c428bdc56e', 'loadClassLoader'), true, true);
25
+ spl_autoload_register(array('ComposerAutoloaderInit0bb7139426d46ba9e60aab1249f589e1', 'loadClassLoader'), true, true);
26
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
27
- spl_autoload_unregister(array('ComposerAutoloaderInit8b6b14905fc0a9c5eacbb4c428bdc56e', 'loadClassLoader'));
27
+ spl_autoload_unregister(array('ComposerAutoloaderInit0bb7139426d46ba9e60aab1249f589e1', 'loadClassLoader'));
28
28
 
29
29
  require __DIR__ . '/autoload_static.php';
30
- call_user_func(\Composer\Autoload\ComposerStaticInit8b6b14905fc0a9c5eacbb4c428bdc56e::getInitializer($loader));
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit0bb7139426d46ba9e60aab1249f589e1::getInitializer($loader));
31
31
 
32
32
  $loader->register(true);
33
33
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  namespace Composer\Autoload;
6
6
 
7
- class ComposerStaticInit8b6b14905fc0a9c5eacbb4c428bdc56e
7
+ class ComposerStaticInit0bb7139426d46ba9e60aab1249f589e1
8
8
  {
9
9
  public static $classMap = array (
10
10
  'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
@@ -13,7 +13,7 @@ class ComposerStaticInit8b6b14905fc0a9c5eacbb4c428bdc56e
13
13
  public static function getInitializer(ClassLoader $loader)
14
14
  {
15
15
  return \Closure::bind(function () use ($loader) {
16
- $loader->classMap = ComposerStaticInit8b6b14905fc0a9c5eacbb4c428bdc56e::$classMap;
16
+ $loader->classMap = ComposerStaticInit0bb7139426d46ba9e60aab1249f589e1::$classMap;
17
17
 
18
18
  }, null, ClassLoader::class);
19
19
  }
@@ -3,7 +3,7 @@
3
3
  'name' => 'automattic/newspack-blocks',
4
4
  'pretty_version' => 'dev-trunk',
5
5
  'version' => 'dev-trunk',
6
- 'reference' => 'd7d934ab198a89d9e299daabb4cdf51d0f4bec2d',
6
+ 'reference' => 'fb64d0ca4ffc2f16870bcc51db83f090054a5d26',
7
7
  'type' => 'wordpress-plugin',
8
8
  'install_path' => __DIR__ . '/../../',
9
9
  'aliases' => array(),
@@ -13,7 +13,7 @@
13
13
  'automattic/newspack-blocks' => array(
14
14
  'pretty_version' => 'dev-trunk',
15
15
  'version' => 'dev-trunk',
16
- 'reference' => 'd7d934ab198a89d9e299daabb4cdf51d0f4bec2d',
16
+ 'reference' => 'fb64d0ca4ffc2f16870bcc51db83f090054a5d26',
17
17
  'type' => 'wordpress-plugin',
18
18
  'install_path' => __DIR__ . '/../../',
19
19
  'aliases' => array(),