: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @type array[] $innerBlocks List of inner blocks. An array of arrays that
* have the same structure as this one.
* @type string $innerHTML HTML from inside block comment delimiters.
* @type array $innerContent List of string fragments and null markers where
* inner blocks were found.
* @param array $source_block {
* An un-modified copy of `$parsed_block`, as it appeared in the source content.
* See WP_Block_Parser_Block.
* @type string $blockName Name of block.
* @type array $attrs Attributes from block comment delimiters.
* @type array[] $innerBlocks List of inner blocks. An array of arrays that
* have the same structure as this one.
* @type string $innerHTML HTML from inside block comment delimiters.
* @type array $innerContent List of string fragments and null markers where
* inner blocks were found.
* @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block.
$parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block, $parent_block );
if ( $post instanceof WP_Post ) {
$context['postId'] = $post->ID;
* The `postType` context is largely unnecessary server-side, since the ID
* is usually sufficient on its own. That being said, since a block's
* manifest is expected to be shared between the server and the client,
* it should be included to consistently fulfill the expectation.
$context['postType'] = $post->post_type;
* Filters the default context provided to a rendered block.
* @since 5.9.0 The `$parent_block` parameter was added.
* @param array $context Default context.
* @param array $parsed_block {
* A representative array of the block being rendered. See WP_Block_Parser_Block.
* @type string $blockName Name of block.
* @type array $attrs Attributes from block comment delimiters.
* @type array[] $innerBlocks List of inner blocks. An array of arrays that
* have the same structure as this one.
* @type string $innerHTML HTML from inside block comment delimiters.
* @type array $innerContent List of string fragments and null markers where
* inner blocks were found.
* @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block.
$context = apply_filters( 'render_block_context', $context, $parsed_block, $parent_block );
$block = new WP_Block( $parsed_block, $context );
* Parses blocks out of a content string.
* @param string $content Post content.
* Array of block structures.
* A representative array of a single parsed block object. See WP_Block_Parser_Block.
* @type string $blockName Name of block.
* @type array $attrs Attributes from block comment delimiters.
* @type array[] $innerBlocks List of inner blocks. An array of arrays that
* have the same structure as this one.
* @type string $innerHTML HTML from inside block comment delimiters.
* @type array $innerContent List of string fragments and null markers where
* inner blocks were found.
function parse_blocks( $content ) {
* Filter to allow plugins to replace the server-side block parser.
* @param string $parser_class Name of block parser class.
$parser_class = apply_filters( 'block_parser_class', 'WP_Block_Parser' );
$parser = new $parser_class();
return $parser->parse( $content );
* Parses dynamic blocks out of `post_content` and re-renders them.
* @param string $content Post content.
* @return string Updated post content.
function do_blocks( $content ) {
$blocks = parse_blocks( $content );
foreach ( $blocks as $block ) {
$output .= render_block( $block );
// If there are blocks in this content, we shouldn't run wpautop() on it later.
$priority = has_filter( 'the_content', 'wpautop' );
if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) {
remove_filter( 'the_content', 'wpautop', $priority );
add_filter( 'the_content', '_restore_wpautop_hook', $priority + 1 );
* If do_blocks() needs to remove wpautop() from the `the_content` filter, this re-adds it afterwards,
* for subsequent `the_content` usage.
* @param string $content The post content running through this filter.
* @return string The unmodified content.
function _restore_wpautop_hook( $content ) {
$current_priority = has_filter( 'the_content', '_restore_wpautop_hook' );
add_filter( 'the_content', 'wpautop', $current_priority - 1 );
remove_filter( 'the_content', '_restore_wpautop_hook', $current_priority );
* Returns the current version of the block format that the content string is using.
* If the string doesn't contain blocks, it returns 0.
* @param string $content Content to test.
* @return int The block format version is 1 if the content contains one or more blocks, 0 otherwise.
function block_version( $content ) {
return has_blocks( $content ) ? 1 : 0;
* Registers a new block style.
* @since 6.6.0 Added support for registering styles for multiple block types.
* @link https://developer.wordpress.org/block-editor/reference-guides/block-api/block-styles/
* @param string|string[] $block_name Block type name including namespace or array of namespaced block type names.
* @param array $style_properties Array containing the properties of the style name, label,
* style_handle (name of the stylesheet to be enqueued),
* inline_style (string containing the CSS to be added),
* style_data (theme.json-like array to generate CSS from).
* See WP_Block_Styles_Registry::register().
* @return bool True if the block style was registered with success and false otherwise.
function register_block_style( $block_name, $style_properties ) {
return WP_Block_Styles_Registry::get_instance()->register( $block_name, $style_properties );
* Unregisters a block style.
* @param string $block_name Block type name including namespace.
* @param string $block_style_name Block style name.
* @return bool True if the block style was unregistered with success and false otherwise.
function unregister_block_style( $block_name, $block_style_name ) {
return WP_Block_Styles_Registry::get_instance()->unregister( $block_name, $block_style_name );
* Checks whether the current block type supports the feature requested.
* @since 6.4.0 The `$feature` parameter now supports a string.
* @param WP_Block_Type $block_type Block type to check for support.
* @param string|array $feature Feature slug, or path to a specific feature to check support for.
* @param mixed $default_value Optional. Fallback value for feature support. Default false.
* @return bool Whether the feature is supported.
function block_has_support( $block_type, $feature, $default_value = false ) {
$block_support = $default_value;
if ( $block_type instanceof WP_Block_Type ) {
if ( is_array( $feature ) && count( $feature ) === 1 ) {
if ( is_array( $feature ) ) {
$block_support = _wp_array_get( $block_type->supports, $feature, $default_value );
} elseif ( isset( $block_type->supports[ $feature ] ) ) {
$block_support = $block_type->supports[ $feature ];
return true === $block_support || is_array( $block_support );
* Converts typography keys declared under `supports.*` to `supports.typography.*`.
* Displays a `_doing_it_wrong()` notice when a block using the older format is detected.
* @param array $metadata Metadata for registering a block type.
* @return array Filtered metadata for registering a block type.
function wp_migrate_old_typography_shape( $metadata ) {
if ( ! isset( $metadata['supports'] ) ) {
$typography_keys = array(
'__experimentalFontFamily',
'__experimentalFontStyle',
'__experimentalFontWeight',
'__experimentalLetterSpacing',
'__experimentalTextDecoration',
'__experimentalTextTransform',
foreach ( $typography_keys as $typography_key ) {
$support_for_key = isset( $metadata['supports'][ $typography_key ] ) ? $metadata['supports'][ $typography_key ] : null;
if ( null !== $support_for_key ) {
'register_block_type_from_metadata()',
/* translators: 1: Block type, 2: Typography supports key, e.g: fontSize, lineHeight, etc. 3: block.json, 4: Old metadata key, 5: New metadata key. */
__( 'Block "%1$s" is declaring %2$s support in %3$s file under %4$s. %2$s support is now declared under %5$s.' ),
"<code>$typography_key</code>",
'<code>block.json</code>',
"<code>supports.$typography_key</code>",
"<code>supports.typography.$typography_key</code>"
_wp_array_set( $metadata['supports'], array( 'typography', $typography_key ), $support_for_key );
unset( $metadata['supports'][ $typography_key ] );
* Helper function that constructs a WP_Query args array from
* a `Query` block properties.
* It's used in Query Loop, Query Pagination Numbers and Query Pagination Next blocks.
* @since 6.1.0 Added `query_loop_block_query_vars` filter and `parents` support in query.
* @param WP_Block $block Block instance.
* @param int $page Current query's page.
* @return array Returns the constructed WP_Query arguments.
function build_query_vars_from_query_block( $block, $page ) {
'post__not_in' => array(),
if ( isset( $block->context['query'] ) ) {
if ( ! empty( $block->context['query']['postType'] ) ) {
$post_type_param = $block->context['query']['postType'];
if ( is_post_type_viewable( $post_type_param ) ) {
$query['post_type'] = $post_type_param;
if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) {
$sticky = get_option( 'sticky_posts' );
if ( 'only' === $block->context['query']['sticky'] ) {
* Passing an empty array to post__in will return have_posts() as true (and all posts will be returned).
* Logic should be used before hand to determine if WP_Query should be used in the event that the array
* being passed to post__in is empty.
* @see https://core.trac.wordpress.org/ticket/28099
$query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 );
$query['ignore_sticky_posts'] = 1;
$query['post__not_in'] = array_merge( $query['post__not_in'], $sticky );
if ( ! empty( $block->context['query']['exclude'] ) ) {
$excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] );
$excluded_post_ids = array_filter( $excluded_post_ids );
$query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids );
isset( $block->context['query']['perPage'] ) &&
is_numeric( $block->context['query']['perPage'] )
$per_page = absint( $block->context['query']['perPage'] );
isset( $block->context['query']['offset'] ) &&
is_numeric( $block->context['query']['offset'] )
$offset = absint( $block->context['query']['offset'] );
$query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset;
$query['posts_per_page'] = $per_page;
// Migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility.
if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) {
if ( ! empty( $block->context['query']['categoryIds'] ) ) {
'taxonomy' => 'category',
'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ),
'include_children' => false,
if ( ! empty( $block->context['query']['tagIds'] ) ) {
'taxonomy' => 'post_tag',
'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ),
'include_children' => false,
$query['tax_query'] = $tax_query;
if ( ! empty( $block->context['query']['taxQuery'] ) ) {
$query['tax_query'] = array();
foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) {
if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) {
$query['tax_query'][] = array(
'terms' => array_filter( array_map( 'intval', $terms ) ),
'include_children' => false,
isset( $block->context['query']['order'] ) &&
in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true )
$query['order'] = strtoupper( $block->context['query']['order'] );
if ( isset( $block->context['query']['orderBy'] ) ) {
$query['orderby'] = $block->context['query']['orderBy'];
isset( $block->context['query']['author'] )
if ( is_array( $block->context['query']['author'] ) ) {
$query['author__in'] = array_filter( array_map( 'intval', $block->context['query']['author'] ) );
} elseif ( is_string( $block->context['query']['author'] ) ) {
$query['author__in'] = array_filter( array_map( 'intval', explode( ',', $block->context['query']['author'] ) ) );
} elseif ( is_int( $block->context['query']['author'] ) && $block->context['query']['author'] > 0 ) {
$query['author'] = $block->context['query']['author'];
if ( ! empty( $block->context['query']['search'] ) ) {
$query['s'] = $block->context['query']['search'];
if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) {
$query['post_parent__in'] = array_filter( array_map( 'intval', $block->context['query']['parents'] ) );
* Filters the arguments which will be passed to `WP_Query` for the Query Loop Block.
* Anything to this filter should be compatible with the `WP_Query` API to form
* the query context which will be passed down to the Query Loop Block's children.
* This can help, for example, to include additional settings or meta queries not
* directly supported by the core Query Loop Block, and extend its capabilities.
* Please note that this will only influence the query that will be rendered on the
* front-end. The editor preview is not affected by this filter. Also, worth noting
* that the editor preview uses the REST API, so, ideally, one should aim to provide
* attributes which are also compatible with the REST API, in order to be able to
* implement identical queries on both sides.
* @param array $query Array containing parameters for `WP_Query` as parsed by the block context.
* @param WP_Block $block Block instance.
* @param int $page Current query's page.
return apply_filters( 'query_loop_block_query_vars', $query, $block, $page );
* Helper function that returns the proper pagination arrow HTML for
* `QueryPaginationNext` and `QueryPaginationPrevious` blocks based
* on the provided `paginationArrow` from `QueryPagination` context.
* It's used in QueryPaginationNext and QueryPaginationPrevious blocks.
* @param WP_Block $block Block instance.
* @param bool $is_next Flag for handling `next/previous` blocks.
* @return string|null The pagination arrow HTML or null if there is none.
function get_query_pagination_arrow( $block, $is_next ) {
if ( ! empty( $block->context['paginationArrow'] ) && array_key_exists( $block->context['paginationArrow'], $arrow_map ) && ! empty( $arrow_map[ $block->context['paginationArrow'] ] ) ) {
$pagination_type = $is_next ? 'next' : 'previous';
$arrow_attribute = $block->context['paginationArrow'];
$arrow = $arrow_map[ $block->context['paginationArrow'] ][ $pagination_type ];
$arrow_classes = "wp-block-query-pagination-$pagination_type-arrow is-arrow-$arrow_attribute";
return "<span class='$arrow_classes' aria-hidden='true'>$arrow</span>";
* Helper function that constructs a comment query vars array from the passed
* It's used with the Comment Query Loop inner blocks.
* @param WP_Block $block Block instance.
* @return array Returns the comment query parameters to use with the
* WP_Comment_Query constructor.
function build_comment_query_vars_from_block( $block ) {
'orderby' => 'comment_date_gmt',
'no_found_rows' => false,
if ( is_user_logged_in() ) {
$comment_args['include_unapproved'] = array( get_current_user_id() );
$unapproved_email = wp_get_unapproved_comment_author_email();
if ( $unapproved_email ) {
$comment_args['include_unapproved'] = array( $unapproved_email );
if ( ! empty( $block->context['postId'] ) ) {
$comment_args['post_id'] = (int) $block->context['postId'];
if ( get_option( 'thread_comments' ) ) {
$comment_args['hierarchical'] = 'threaded';
$comment_args['hierarchical'] = false;
if ( get_option( 'page_comments' ) === '1' || get_option( 'page_comments' ) === true ) {
$per_page = get_option( 'comments_per_page' );
$default_page = get_option( 'default_comments_page' );