Finding all your sites Gutenberg Blocks

WordPress

Want to know something? Sure you do, a secret only you and me know….
I use Gutenberg on this site! There I have said it, half the WordPress world gasped in horror and do you know what I don’t dislike it.

There are also lot’s of plugins now that support Gutenberg blocks, like Atomic Blocks and Otter Block and My Mates got a Block. A Block directory is coming it’s so well blocky.

However there is a problem with all these blocks, people they are going to use them!

For example on this site I had 4 separate block libraries mostly doing the same things and I don’t need all of them and surely can’t be using all of them but how to check.

Guttenberg Blocks

WordPress has a built-in Blocks Parser, which if you give it content (like post contents) it will return you blocks within it for example:

parse_blocks( $post->post_content );

returns an array container items something like:

array(5) {
    ["blockName"]=>
    string(14) "core/paragraph"
    ["attrs"]=>
    array(1) {
      ["fontSize"]=>
      string(5) "small"
    }
    ["innerBlocks"]=>
    array(0) {
    }
    ["innerHTML"]=>
    string(113) "
<p class="has-small-font-size">Illustrations used in the article by <a href="https://undraw.co/">Undraw</a></p>
"
    ["innerContent"]=>
    array(1) {
      [0]=>
      string(113) "
<p class="has-small-font-size">Illustrations used in the article by <a href="https://undraw.co/">Undraw</a></p>
"
    }

So to get the list of blocks for your site is easy it’s just something like:

$args = [
    'numberposts'   => -1,
    'post_type'     => ['post','page']
];
$postc = get_posts($args);
$site_blocks = [];
foreach($postc as $post){
  if ( has_blocks( $post->post_content ) ) {
    $blocks = parse_blocks( $post->post_content );
    foreach($blocks as $block)
    {
     	if(!in_array($block['blockName'], $site_blocks)) $site_blocks[] = $block['blockName'];
    }
  }
}
var_dump($site_blocks);

If we run this on the CLI with:

% wp eval-file list_blocks.php                                                                                                                               ~/httpdocs
array(17) {
  [0]=>
  string(14) "core/paragraph"
  [1]=>
  NULL
  [2]=>
  string(14) "core/separator"
  [3]=>
  string(9) "core/html"
  [4]=>
  string(12) "core/heading"
  [5]=>
  string(10) "core/group"
  [6]=>
  string(12) "core/columns"
  [7]=>
  string(10) "core/image"
  [8]=>
  string(18) "core-embed/youtube"
  [9]=>
  string(18) "core-embed/twitter"
  [10]=>
  string(9) "core/list"
  [11]=>
  string(10) "core/block"
  [12]=>
  string(10) "core/cover"
  [13]=>
  string(24) "atomic-blocks/ab-sharing"
  [14]=>
  string(10) "core/quote"
  [15]=>
  string(9) "core/code"
  [16]=>
  string(17) "core/preformatted"
}

Happy days, sure we could clean up the output, and we could probably exclude core components etc. Likewise, we are just getting names, but we could store posts etc.

Happy with my success I shared it with the WPUK Slack, and Herb pointed out, to check wp_blocks.

Wait what now…

Ah yes, reusable blocks, turns out they are their own custom post type, which kind of makes sense, he also correctly pointed out to be sure to include other post types which I don’t need for me but is a good point so now our code looks more like:

$args = [
    'numberposts'   => -1,
    'post_type'     => ['page','wp_block','post','wp_area']
];

Ok so by now I hear the grating of teeth at the use of -1 for numberposts, don’t judge, this is a CLI running reporting tool thrown together in minutes.

Ok so we all done, happy days. No… I go and look on the list, and that block I know I used isn’t showing up. Why?

Nested blocks

So if we go look at the output of the parse_blocks we have the innerBlocks array, this is an array of blocks that might be inside our block we have just parsed. This happens for example in sections and columns and JUST ABOUT EVERY DAMN BLOCK IS IN ANOTHER BLOCK!!!!!!!!!!!

I mean that’s fine, we just go through and loop through those, happy… Recursion oh yeah so we extend out and make sure we are looping happily through.

My final very dodgy code looks like:


<?php
$args = [
    'numberposts'   => -1,
    'post_type'     => ['page','wp_block','post','wp_area']
];
$postc = get_posts($args);
$site_blocks = [];
foreach($postc as $post){
  if ( has_blocks( $post->post_content ) ) {
    $blocks = parse_blocks( $post->post_content );
    foreach($blocks as $block)
    {
     	$blockNames = parse_block_names($block);
      $site_blocks = array_unique(array_merge($blockNames,$site_blocks));
    }
  }
}
var_dump($site_blocks);
function parse_block_names($blockObject) {
  $blockNames = [];
  if(!in_array($blockObject['blockName'], $blockNames)) $blockNames[] = $blockObject['blockName'];
  if(!empty($blockObject['innerBlocks'])){
    foreach($blockObject['innerBlocks'] as $innerBlock){
      $innerBlockNames = parse_block_names($innerBlock);
      $blockNames = array_unique(array_merge($blockNames,$innerBlockNames));
    }
  }
  return $blockNames;
}

Which happily generates a list like:

% wp eval-file list_blocks.php                                                                                                                               ~/httpdocs
array(27) {
  [0]=>
  string(14) "core/paragraph"
  [1]=>
  NULL
  [2]=>
  string(15) "core/media-text"
  [4]=>
  string(33) "themeisle-blocks/advanced-columns"
  [5]=>
  string(32) "themeisle-blocks/advanced-column"
  [6]=>
  string(12) "core/columns"
  [7]=>
  string(11) "core/column"
  [8]=>
  string(10) "core/image"
  [9]=>
  string(14) "core/separator"
  [10]=>
  string(26) "atomic-blocks/ab-container"
  [11]=>
  string(9) "core/list"
  [12]=>
  string(37) "contact-form-block/contact-form-block"
  [13]=>
  string(10) "core/block"
  [14]=>
  string(12) "core/heading"
  [15]=>
  string(27) "themeisle-blocks/posts-grid"
  [16]=>
  string(28) "atomic-blocks/ab-testimonial"
  [17]=>
  string(14) "core/shortcode"
  [18]=>
  string(9) "core/code"
  [19]=>
  string(17) "core/preformatted"
  [20]=>
  string(18) "core-embed/youtube"
  [21]=>
  string(10) "core/quote"
  [22]=>
  string(18) "core-embed/twitter"
  [23]=>
  string(9) "core/html"
  [24]=>
  string(24) "atomic-blocks/ab-sharing"
  [25]=>
  string(10) "core/cover"
  [26]=>
  string(33) "themeisle-blocks/advanced-heading"
  [27]=>
  string(10) "core/group"
}

Turned out I really wasn’t using that plugin after all and it’s safe to deactivate.

Future

Block Management is going to be an interesting challenge, I’m already seeing a potential idea for a plugin, that hooks into deactivate script, and says “These plugins blocks are used on these pages” please check before deactivating.

However this 5-minute detour into the world of blocks has been interesting, a couple of caveats I’m sure some blocks use the same names, replace core blocks, generate on the fly and do a myriad of things to avoid detection. To this end, the quick script will no doubt not pick up the edge cases but I leave it here as it might be useful. For me it lets me explore parse_blocks and I learnt about the wp_block and wp_area custom post types.

Helping you and your customers stay safe


WordPress Security Consulting Services

Power Hour Consulting

Want to get expert advice on your site's security? Whether you're dealing with a hacked site or looking to future-proof your security, Tim will provide personalised guidance and answer any questions you may have. A power hour call is an ideal starting place for a project or a way to break deadlocks in complex problems.

Learn more

Site Reviews

Want to feel confident about your site's security and performance? A website review from Tim has got you covered. Using a powerful combination of automated and manual testing to analyse your site for any potential vulnerabilities or performance issues. With a comprehensive report and, importantly, recommendations for each action required.

Learn more

Code Reviews

Is your plugin or theme code secure and performing at its best? Tim provides a comprehensive code review, that combine the power of manual and automated testing, as well as a line-by-line analysis of your code base. With actionable insights, to help you optimise your code's security and performance.

Learn more

Or let's chat about your security?

Book a FREE 20 minute call with me to see how you can improve your WordPress Security.

(No Strings Attached, honest!)