wpseek.com
A WordPress-centric search engine for devs and theme authors
wp_render_layout_support_flag is private and should not be used in themes or plugins directly.
wp_render_layout_support_flag › WordPress Function
Since5.8.0
Deprecatedn/a
› wp_render_layout_support_flag ( $block_content, $block )
| Access: |
|
| Parameters: (2) |
|
| Returns: |
|
| Defined at: |
|
| Codex: | |
| Change Log: |
|
Renders the layout config to the block wrapper.
Source
function wp_render_layout_support_flag( $block_content, $block ) {
static $global_styles = null;
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$block_supports_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
$style_attr = $block['attrs']['style'] ?? array();
$child_layout = $style_attr['layout'] ?? null;
/*
* Collect responsive viewport child layout overrides so that a block with
* only responsive child layout (no base child layout) is still processed.
*/
$viewport_child_layouts = array();
foreach ( WP_Theme_JSON::RESPONSIVE_BREAKPOINTS as $breakpoint => $media_query ) {
$viewport_child = wp_get_layout_child_values( $style_attr[ $breakpoint ]['layout'] ?? null );
if ( ! empty( $viewport_child ) ) {
$viewport_child_layouts[ $breakpoint ] = array(
'media_query' => $media_query,
'child_layout' => $viewport_child,
);
}
}
if ( ! $block_supports_layout && ! $child_layout && empty( $viewport_child_layouts ) ) {
return $block_content;
}
$outer_class_names = array();
// Child layout specific logic.
if ( $child_layout || ! empty( $viewport_child_layouts ) ) {
$base_child_layout = wp_get_layout_child_values( $child_layout );
$parent_layout = $block['parentLayout'] ?? array();
/*
* Generates a unique class for child block layout styles.
*
* To ensure consistent class generation across different page renders,
* only properties that affect layout styling are used. These properties
* come from `$block['attrs']['style']['layout']`, viewport overrides in
* `$block['attrs']['style'][$breakpoint]['layout']`, and `$block['parentLayout']`.
*
* As long as these properties coincide, the generated class will be the same.
*/
$container_content_hash_input = array(
'layout' => $base_child_layout,
'parentLayout' => array_intersect_key(
$parent_layout,
array_flip( array( 'minimumColumnWidth', 'columnCount' ) )
),
);
foreach ( $viewport_child_layouts as $breakpoint => $viewport_data ) {
$container_content_hash_input[ $breakpoint ] = $viewport_data['child_layout'];
}
$container_content_class = wp_unique_id_from_values(
$container_content_hash_input,
'wp-container-content-'
);
$child_layout_styles = wp_get_child_layout_style_rules( ".$container_content_class", $base_child_layout, $parent_layout );
/*
* Emit responsive child layout CSS using the same container-content class
* so that base and responsive child layout share the exact same selector.
*/
foreach ( $viewport_child_layouts as $viewport_data ) {
$viewport_child_styles = wp_get_child_layout_style_rules(
".$container_content_class",
$base_child_layout,
$parent_layout,
$viewport_data['child_layout']
);
foreach ( $viewport_child_styles as $index => $rule ) {
$viewport_child_styles[ $index ]['rules_group'] = $viewport_data['media_query'];
}
$child_layout_styles = array_merge( $child_layout_styles, $viewport_child_styles );
}
/*
* Add to the style engine store to enqueue and render layout styles.
* Return styles here just to check if any exist.
*/
$child_css = wp_style_engine_get_stylesheet_from_css_rules(
$child_layout_styles,
array(
'context' => 'block-supports',
'prettify' => false,
)
);
if ( $child_css ) {
$outer_class_names[] = $container_content_class;
}
}
// Prep the processor for modifying the block output.
$processor = new WP_HTML_Tag_Processor( $block_content );
// Having no tags implies there are no tags onto which to add class names.
if ( ! $processor->next_tag() ) {
return $block_content;
}
/*
* A block may not support layout but still be affected by a parent block's layout.
*
* In these cases add the appropriate class names and then return early; there's
* no need to investigate on this block whether additional layout constraints apply.
*/
if ( ! $block_supports_layout && ! empty( $outer_class_names ) ) {
foreach ( $outer_class_names as $class_name ) {
$processor->add_class( $class_name );
}
return $processor->get_updated_html();
} elseif ( ! $block_supports_layout ) {
// Ensure layout classnames are not injected if there is no layout support.
return $block_content;
}
$global_settings = wp_get_global_settings();
$fallback_layout = $block_type->supports['layout']['default'] ?? array();
if ( empty( $fallback_layout ) ) {
$fallback_layout = $block_type->supports['__experimentalLayout']['default'] ?? array();
}
$used_layout = $block['attrs']['layout'] ?? $fallback_layout;
$class_names = array();
$layout_definitions = wp_get_layout_definitions();
// Set the correct layout type for blocks using legacy content width.
if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) {
$used_layout['type'] = 'constrained';
}
$root_padding_aware_alignments = $global_settings['useRootPaddingAwareAlignments'] ?? false;
if (
$root_padding_aware_alignments &&
isset( $used_layout['type'] ) &&
'constrained' === $used_layout['type']
) {
$class_names[] = 'has-global-padding';
}
/*
* The following section was added to reintroduce a small set of layout classnames that were
* removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is
* not intended to provide an extended set of classes to match all block layout attributes
* here.
*/
if ( ! empty( $block['attrs']['layout']['orientation'] ) ) {
$class_names[] = 'is-' . sanitize_title( $block['attrs']['layout']['orientation'] );
}
if ( ! empty( $block['attrs']['layout']['justifyContent'] ) ) {
$class_names[] = 'is-content-justification-' . sanitize_title( $block['attrs']['layout']['justifyContent'] );
}
if ( ! empty( $block['attrs']['layout']['flexWrap'] ) && 'nowrap' === $block['attrs']['layout']['flexWrap'] ) {
$class_names[] = 'is-nowrap';
}
// Get classname for layout type.
if ( isset( $used_layout['type'] ) ) {
$layout_classname = $layout_definitions[ $used_layout['type'] ]['className'] ?? '';
} else {
$layout_classname = $layout_definitions['default']['className'] ?? '';
}
if ( $layout_classname && is_string( $layout_classname ) ) {
$class_names[] = sanitize_title( $layout_classname );
}
/*
* Only generate Layout styles if the theme has not opted-out.
* Attribute-based Layout classnames are output in all cases.
*/
if ( ! current_theme_supports( 'disable-layout-styles' ) ) {
$gap_value = wp_sanitize_block_gap_value( $style_attr['spacing']['blockGap'] ?? null );
$fallback_gap_value = $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ?? '0.5em';
$block_spacing = $style_attr['spacing'] ?? null;
/*
* If a block's block.json skips serialization for spacing or spacing.blockGap,
* don't apply the user-defined value to the styles.
*/
$should_skip_gap_serialization = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
$block_gap = $global_settings['spacing']['blockGap'] ?? null;
$has_block_gap_support = isset( $block_gap );
// Get default blockGap value from global styles for use in layouts like grid.
// Check style variation first, then block-specific styles, then fall back to root styles.
$block_name = $block['blockName'] ?? '';
if ( null === $global_styles ) {
$global_styles = wp_get_global_styles();
}
// Check if the block has an active style variation with a blockGap value.
// Only check the registry if the className contains a variation class to avoid unnecessary lookups.
$variation_block_gap_value = null;
$block_class_name = $block['attrs']['className'] ?? '';
if ( $block_class_name && str_contains( $block_class_name, 'is-style-' ) && $block_name ) {
$styles_registry = WP_Block_Styles_Registry::get_instance();
$registered_styles = $styles_registry->get_registered_styles_for_block( $block_name );
$variation_name = wp_get_block_style_variation_name_from_registered_style( $block_class_name, $registered_styles );
if ( $variation_name ) {
$variation_block_gap_value = $global_styles['blocks'][ $block_name ]['variations'][ $variation_name ]['spacing']['blockGap'] ?? null;
}
}
$global_block_gap_value = $variation_block_gap_value ?? $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? $global_styles['spacing']['blockGap'] ?? null;
if ( null !== $global_block_gap_value ) {
$fallback_gap_value = $global_block_gap_value;
}
$container_class_hash_input = array(
$used_layout,
$has_block_gap_support,
$gap_value,
$should_skip_gap_serialization,
$fallback_gap_value,
$block_spacing,
);
foreach ( array_keys( WP_Theme_JSON::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) {
$viewport_style = $style_attr[ $breakpoint ] ?? null;
if ( ! is_array( $viewport_style ) ) {
continue;
}
$viewport_container_layout = wp_get_layout_container_values( $viewport_style['layout'] ?? null );
if ( ! empty( $viewport_container_layout ) ) {
$container_class_hash_input[] = array(
'breakpoint' => $breakpoint,
'layout' => $viewport_container_layout,
);
}
if ( isset( $viewport_style['spacing']['blockGap'] ) ) {
$container_class_hash_input[] = array(
'breakpoint' => $breakpoint,
'blockGap' => wp_sanitize_block_gap_value( $viewport_style['spacing']['blockGap'] ),
);
}
}
/*
* Generates a unique ID based on all the data required to obtain the
* corresponding layout style. Keeps the CSS class names the same
* even for different blocks on different places, as long as they have
* the same layout definition. Makes the CSS class names stable across
* paginations for features like the enhanced pagination of the Query block.
*/
$container_class = wp_unique_id_from_values(
$container_class_hash_input,
'wp-container-' . sanitize_title( $block['blockName'] ) . '-is-layout-'
);
$style = wp_get_layout_style(
".$container_class",
$used_layout,
$has_block_gap_support,
$gap_value,
$should_skip_gap_serialization,
$fallback_gap_value,
$block_spacing
);
/*
* Emit responsive container layout styles using the same $container_class
* selector as the base layout so they target the inner block wrapper.
*/
foreach ( WP_Theme_JSON::RESPONSIVE_BREAKPOINTS as $breakpoint => $media_query ) {
$viewport_style = $style_attr[ $breakpoint ] ?? null;
if ( ! is_array( $viewport_style ) ) {
continue;
}
$viewport_container_layout = wp_get_layout_container_values( $viewport_style['layout'] ?? null );
$has_viewport_layout = ! empty( $viewport_container_layout );
$has_viewport_block_gap = isset( $viewport_style['spacing']['blockGap'] );
if ( ! $has_viewport_layout && ! $has_viewport_block_gap ) {
continue;
}
$viewport_gap_value = $has_viewport_block_gap
? wp_sanitize_block_gap_value( $viewport_style['spacing']['blockGap'] )
: $gap_value;
$viewport_block_spacing = is_array( $viewport_style['spacing'] ?? null )
? array_replace( is_array( $block_spacing ) ? $block_spacing : array(), $viewport_style['spacing'] )
: $block_spacing;
$viewport_styles = wp_get_layout_style(
".$container_class",
$used_layout,
$has_block_gap_support,
$viewport_gap_value,
$should_skip_gap_serialization,
$fallback_gap_value,
$viewport_block_spacing,
array(
'rules_group' => $media_query,
'viewport_overrides' => $viewport_container_layout,
'has_block_gap_override' => $has_viewport_block_gap,
)
);
if ( ! empty( $viewport_styles ) && ! in_array( $container_class, $class_names, true ) ) {
$class_names[] = $container_class;
}
}
// Only add container class and enqueue block support styles if unique styles were generated.
if ( ! empty( $style ) ) {
$class_names[] = $container_class;
}
}
// Add combined layout and block classname for global styles to hook onto.
$split_block_name = explode( '/', $block['blockName'] );
$full_block_name = 'core' === $split_block_name[0] ? end( $split_block_name ) : implode( '-', $split_block_name );
$class_names[] = 'wp-block-' . $full_block_name . '-' . $layout_classname;
// Add classes to the outermost HTML tag if necessary.
if ( ! empty( $outer_class_names ) ) {
foreach ( $outer_class_names as $outer_class_name ) {
$processor->add_class( $outer_class_name );
}
}
/**
* Attempts to refer to the inner-block wrapping element by its class attribute.
*
* When examining a block's inner content, if a block has inner blocks, then
* the first content item will likely be a text (HTML) chunk immediately
* preceding the inner blocks. The last HTML tag in that chunk would then be
* an opening tag for an element that wraps the inner blocks.
*
* There's no reliable way to associate this wrapper in $block_content because
* it may have changed during the rendering pipeline (as inner contents is
* provided before rendering) and through previous filters. In many cases,
* however, the `class` attribute will be a good-enough identifier, so this
* code finds the last tag in that chunk and stores the `class` attribute
* so that it can be used later when working through the rendered block output
* to identify the wrapping element and add the remaining class names to it.
*
* It's also possible that no inner block wrapper even exists. If that's the
* case this code could apply the class names to an invalid element.
*
* Example:
*
* $block['innerBlocks'] = array( $list_item );
* $block['innerContent'] = array( '<ul class="list-wrapper is-unordered">', null, '</ul>' );
*
* // After rendering, the initial contents may have been modified by other renderers or filters.
* $block_content = <<<HTML
* <figure>
* <ul class="annotated-list list-wrapper is-unordered">
* <li>Code</li>
* </ul><figcaption>It's a list!</figcaption>
* </figure>
* HTML;
*
* Although it is possible that the original block-wrapper classes are changed in $block_content
* from how they appear in $block['innerContent'], it's likely that the original class attributes
* are still present in the wrapper as they are in this example. Frequently, additional classes
* will also be present; rarely should classes be removed.
*
* @todo Find a better way to match the first inner block. If it's possible to identify where the
* first inner block starts, then it will be possible to find the last tag before it starts
* and then that tag, if an opening tag, can be solidly identified as a wrapping element.
* Can some unique value or class or ID be added to the inner blocks when they process
* so that they can be extracted here safely without guessing? Can the block rendering function
* return information about where the rendered inner blocks start?
*
* @var string|null
*/
$inner_block_wrapper_classes = null;
$first_chunk = $block['innerContent'][0] ?? null;
if ( is_string( $first_chunk ) && count( $block['innerContent'] ) > 1 ) {
$first_chunk_processor = new WP_HTML_Tag_Processor( $first_chunk );
while ( $first_chunk_processor->next_tag() ) {
$class_attribute = $first_chunk_processor->get_attribute( 'class' );
if ( is_string( $class_attribute ) && ! empty( $class_attribute ) ) {
$inner_block_wrapper_classes = $class_attribute;
}
}
}
/*
* If necessary, advance to what is likely to be an inner block wrapper tag.
*
* This advances until it finds the first tag containing the original class
* attribute from above. If none is found it will scan to the end of the block
* and fail to add any class names.
*
* If there is no block wrapper it won't advance at all, in which case the
* class names will be added to the first and outermost tag of the block.
* For cases where this outermost tag is the only tag surrounding inner
* blocks then the outer wrapper and inner wrapper are the same.
*/
do {
if ( ! $inner_block_wrapper_classes ) {
break;
}
$class_attribute = $processor->get_attribute( 'class' );
if ( is_string( $class_attribute ) && str_contains( $class_attribute, $inner_block_wrapper_classes ) ) {
break;
}
} while ( $processor->next_tag() );
// Add the remaining class names.
foreach ( $class_names as $class_name ) {
$processor->add_class( $class_name );
}
return $processor->get_updated_html();
}