match_blocks()
selects the blocks in post content, or in a given set of blocks, inner blocks, or block HTML, that match the given criteria, such as the block name, block attributes, or position within the set of blocks.
Blocks can be matched by:
- Block name or names (
name
) - Block attributes (
attrs
,with_attrs
) - Block inner HTML (
with_innerhtml
) - The block's positive or negative index within the set (
position
) - Whether the block represents only space (
skip_empty_blocks
) - Whether the block has inner blocks (
has_innerblocks
) - Custom validation classes (
is_valid
)
Passing matching parameters is optional; all non-empty blocks match by default.
Additionally:
- Recursion into inner blocks is supported (
flatten
). - The set of matching blocks can be limited by size (
limit
) or their position in the set of matches (nth_of_type
). - The number of matches can be returned instead of the matched blocks (
count
). - The companion
match_block()
function reduces the filtered set of results to a single parsed block. - Passing a single block instance will return matches from its inner blocks.
match_blocks()
is powered by a set of block validation classes that utilize the Laminas Validator framework and Laminas Validator Extensions package. These validators, along with a base class for validating blocks, are included here. See the validators section for their documentation.
Install the latest version with:
composer require alleyinteractive/wp-match-blocks
Find all paragraph blocks in a post:
<?php
$grafs = \Alley\WP\match_blocks( $post, [ 'name' => 'core/paragraph' ] );
Include paragraphs in inner blocks:
<?php
$grafs = \Alley\WP\match_blocks(
$post,
[
'flatten' => true,
'name' => 'core/paragraph',
]
);
Get the number of paragraph blocks:
<?php
$count = \Alley\WP\match_blocks(
$post,
[
'count' => true,
'name' => 'core/paragraph',
]
);
Get the number of paragraph blocks that are inner blocks of the given group block:
<?php
$blocks = parse_blocks( '<!-- wp:group --><div class="wp-block-group"><!-- wp:paragraph -->…<!-- wp:group /-->' );
$count = \Alley\WP\match_blocks(
$blocks[0],
[
'count' => true,
'name' => 'core/paragraph',
]
);
Get all paragraphs and headings:
<?php
$blocks = \Alley\WP\match_blocks(
'<!-- wp:paragraph -->…',
[
'name' => [ 'core/heading', 'core/paragraph' ],
]
);
Get only paragraphs that have been explicitly aligned:
<?php
$blocks = \Alley\WP\match_blocks(
[ /* blocks */ ],
[
'name' => 'core/paragraph',
'with_attrs' => 'align',
]
);
Get only paragraphs that have been right-aligned:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'attrs' => [
[
'key' => 'align',
'value' => 'right',
],
],
'name' => 'core/paragraph',
]
);
Get only paragraphs that have been aligned, but not to the right:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'attrs' => [
[
'key' => 'align',
'value' => 'right',
'operator' => '!==',
],
],
'name' => 'core/paragraph',
]
);
Get only paragraphs that have been aligned to the left or the right:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'attrs' => [
[
'key' => 'align',
'value' => [ 'left', 'right' ],
'operator' => 'IN',
],
],
'name' => 'core/paragraph',
]
);
Get all images credited to the Associated Press:
<?php
$images = \Alley\WP\match_blocks(
$post,
[
'attrs' => [
[
'key' => 'credit',
'value' => '/(The )?Associated Press/i',
'operator' => 'REGEX',
],
[
'key' => 'credit',
'value' => 'AP',
],
'relation' => 'OR',
],
'name' => 'core/image',
]
);
Get shortcode blocks with a specific shortcode:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'name' => 'core/shortcode',
'with_innerhtml' => '[bc_video',
]
);
Audit a post for YouTube embed blocks that reference videos that are no longer accessible.
<?php
final class YouTube_Video_Exists extends \Alley\WP\Validator\Block_Validator {
// ...
}
$blocks = \Alley\WP\match_blocks(
$post,
[
'name' => 'core/embed',
'attrs' => [
[
'key' => 'providerNameSlug',
'value' => 'youtube',
],
],
'is_valid' => new \Alley\Validator\Not( new YouTube_Video_Exists(), '…' ),
],
);
Get the first three blocks:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'limit' => 3,
]
);
Get the first three paragraph blocks:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'limit' => 3,
'name' => 'core/paragraph',
]
);
Get the third paragraph:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'name' => 'core/paragraph',
'nth_of_type' => 3,
]
);
// Or, skip straight to the parsed block:
$block = \Alley\WP\match_block(
$post,
[
'name' => 'core/paragraph',
'nth_of_type' => '3n',
]
);
Get every third paragraph:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'name' => 'core/paragraph',
'nth_of_type' => '3n',
]
);
Get paragraphs 3-8:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'name' => 'core/paragraph',
'nth_of_type' => [ 'n+3', '-n+8' ],
]
);
Get the block at position 3 in the set if it's a paragraph:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'name' => 'core/paragraph',
'position' => 3,
]
);
Get the last two blocks:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'position' => [ -1, -2 ],
]
);
Get all non-empty blocks:
<?php
$blocks = \Alley\WP\match_blocks( $post );
Get all empty blocks:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'name' => null,
'skip_empty_blocks' => false,
]
);
Get only blocks with inner blocks:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'has_innerblocks' => true,
]
);
Get only blocks without inner blocks:
<?php
$blocks = \Alley\WP\match_blocks(
$post,
[
'has_innerblocks' => false,
]
);
This package provides classes for validating blocks based on the Laminas Validator framework and Laminas Validator Extensions package, plus a custom base block validation class.
match_blocks()
also uses these validators internally, and they can be passed as the is_valid
parameter to match_blocks()
or used on their own.
The abstract Alley\WP\Validator\BlockValidator
class extends Alley\Validator\BaseValidator
and, much like BaseValidator
, standardizes validation of blocks.
When extending BlockValidator
, validation logic goes into a test_block()
method. test_block()
always receives a \WP_Block_Parser_Block
instance; validation will automatically fail if the input is not an instance of \WP_Block
, \WP_Block_Parser_Block
, or an array representation of a block.
Alley\WP\Validator\Block_Attribute
validates whether the block contains, or does not contain, the specified attribute name, value, or name-value pair.
The block passes if a name comparison is specified, and the block contains an attribute whose name matches the comparison; if a value comparison is specified, and the block contains an attribute whose value matches the comparison; or if both name and value comparisons are specified, and the block contains an attribute with a matching name and value.
The following options are supported for Alley\WP\Validator\Block_Attribute
:
key
: The name of a block attribute, or an array of names, or a regular expression pattern. Default none.value
: A block attribute value, or an array of values, or regular expression pattern. Default none.operator
: The operator with which to compare$value
to block attributes. AcceptsCONTAINS
,NOT CONTAINS
(case-sensitive),IN
,NOT IN
,LIKE
,NOT LIKE
(case-insensitive),REGEX
,NOT REGEX
, or any operator supported by\Alley\Validator\Comparison
. Default is===
.key_operator
: Equivalent tooperator
but for$key
.
<?php
// '<!-- wp:media-text {"mediaId":617,"mediaType":"image","isStackedOnMobile":false,"className":"alignwide"} -->';
$valid = new Alley\WP\Validator\Block_Attribute(
[
'key' => 'mediaType',
'value' => 'image',
],
);
$valid = new Alley\WP\Validator\Block_Attribute(
[
'key' => [ 'mediaType', 'mediaId' ],
'key_operator' => 'IN',
],
);
$valid = new Alley\WP\Validator\Block_Attribute(
[
'key' => '/^media/',
'key_operator' => 'REGEX',
'value' => [ 'image', 'video' ],
'operator' => 'IN',
],
);
$valid = new Alley\WP\Validator\Block_Attribute(
[
'key' => '/^media/',
'key_operator' => 'REGEX',
'value' => [ 'audio', 'document' ],
'operator' => 'NOT IN',
],
);
Alley\WP\Validator\Block_InnerHTML
validates whether the block contains, or does not contain, the specified content in its innerHTML
property. The block passes if it contains an innerHTML
value that matches the comparison.
The following options are supported for Alley\WP\Validator\Block_InnerHTML
:
content
: The content to find or a regular expression pattern.operator
: The operator with which to compare$content
to the block inner HTML. AcceptsCONTAINS
,NOT CONTAINS
(case-sensitive),IN
,NOT IN
,LIKE
,NOT LIKE
(case-insensitive),REGEX
,NOT REGEX
, or any operator supported by\Alley\Validator\Comparison
. Default isLIKE
.
<?php
// '
// <!-- wp:paragraph -->
// <p>The goal of this new editor is to make adding rich content to WordPress simple and enjoyable.</p>
// <!-- /wp:paragraph -->
// '
$valid = new Alley\WP\Validator\Block_InnerHTML(
[
'content' => 'wordpress',
'operator' => 'LIKE',
],
);
$valid = new Alley\WP\Validator\Block_InnerHTML(
[
'content' => 'WordPress',
'operator' => 'CONTAINS',
],
);
$valid = new Alley\WP\Validator\Block_InnerHTML(
[
'content' => '/^\s*<p>\s*</p>/',
'operator' => 'NOT REGEX',
],
);
Alley\WP\Validator\Block_Name
validates whether a block has a given name or one of a set of names. The block passes validation if the block name is in the set.
The following options are supported for Alley\WP\Validator\Block_Name
:
-name
: The block name or names.
<?php
$valid = new Alley\WP\Validator\Block_Name(
[
'name' => 'core/paragraph',
]
);
$valid = new Alley\WP\Validator\Block_Name(
[
'name' => [ 'core/gallery', 'jetpack/slideshow', 'jetpack/tiled-gallery' ],
]
);
Alley\WP\Validator\Block_Offset
validates whether the block appears at one of the given numeric offsets within a list of blocks.
The block matches if it appears at one of the offsets in the list.
Identity is determined by comparing the \WP_Block_Parser_Block
instances as arrays.
The following options are supported for Alley\WP\Validator\Block_Offset
:
blocks
: An array or iterable of blocks.offset
: The expected offset or offsets. Negative offsets count from the end of the list.skip_empty_blocks
: Whether to skip blocks that are "empty" according to theNonempty_Block_Validator
when indexing$blocks
. Default true.
<?php
$blocks = parse_blocks(
<<<HTML
<!-- wp:paragraph --><p>Hello, world!</p><!-- /wp:paragraph -->
<!-- wp:archives {\"displayAsDropdown\":true,\"showPostCounts\":true} /-->
<!-- wp:media-text {\"mediaId\":617,\"mediaType\":\"image\",\"isStackedOnMobile\":false,\"className\":\"alignwide\"} -->
HTML
);
$valid = new Alley\WP\Validator\Block_Offset(
[
'blocks' => $blocks,
'offset' => 1,
],
);
$valid->isValid( $blocks[2] ); // true
$valid = new Alley\WP\Validator\Block_Offset(
[
'blocks' => $blocks,
'offset' => [ 4 ],
'skip_empty_blocks' => false,
],
);
$valid->isValid( $blocks[4] ); // true
$valid = new Alley\WP\Validator\Block_Offset(
[
'blocks' => $blocks,
'offset' => -2,
],
);
$valid->isValid( $blocks[2] ); // true
Alley\WP\Validator\Block_InnerBlocks_Count
validates whether the number of inner blocks in the given block passes the specified comparison.
The block passes validation if the comparison is true for the count of inner blocks. Inner blocks within inner blocks don't count towards the total.
The following options are supported for Alley\WP\Validator\Block_InnerBlocks_Count
:
count
: The expected number of inner blocks for the comparison.operator
: The PHP comparison operator used to compare the input block's inner blocks andcount
.
<?php
$blocks = parse_blocks(
<<<HTML
<!-- wp:foo -->
<!-- wp:bar -->
<!-- wp:baz /-->
<!-- /wp:bar -->
<!-- /wp:foo -->
HTML
);
$valid = new \Alley\WP\Validator\Block_InnerBlocks_Count(
[
'count' => 1,
'operator' => '===',
]
);
$valid->isValid( $blocks[0] ); // true
$valid = new \Alley\WP\Validator\Block_InnerBlocks_Count(
[
'count' => 0,
'operator' => '>',
]
);
$valid->isValid( $blocks[0] ); // true
$valid = new \Alley\WP\Validator\Block_InnerBlocks_Count(
[
'count' => 42,
'operator' => '<=',
]
);
$valid->isValid( $blocks[0] ); // true
Alley\WP\Validator\Nonempty_Block
validates that the given block is not "empty" -- for example, not a block representing only line breaks.
The block passes validation if it has a non-null name.
None.
<?php
$blocks = parse_blocks( "\n" );
$valid = new \Alley\WP\Validator\Nonempty_Block();
$valid->isValid( $blocks[0] ); // false