DEV Community

Tim Nash
Tim Nash

Posted on • Originally published at timnash.co.uk on

Finding all your sites Gutenberg Blocks

This post was originally posted on TimNash.co.uk on 18th Jan 2020. Hi, I'm Tim, tend to talk about WordPress and Security on my site, I hope you enjoy this repost.

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.

Source

Top comments (0)