PluginProbe
Gutenberg / trunk
Gutenberg vtrunk
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
← All changes | lib/block-supports/layout.php +152 -69 23.7.0 → trunk View file →
@@ -283,19 +283,39 @@
283 283 *
284 284 * Regex for CSS value borrowed from `safecss_filter_attr`, used here to only match
285 285 * against the value, not the CSS attribute.
286 286 *
287 - * @param string|array|null $gap_value Block gap value.
288 - * @return string|array|null Sanitized block gap value.
287 + * Numeric zero is converted to a string because it is valid CSS without a unit.
288 + * Other non-string values are rejected.
289 + *
290 + * @param mixed $gap_value Block gap value.
291 + * @return string|string[]|null Sanitized block gap value.
289 292 */
290 293 function gutenberg_sanitize_block_gap_value( $gap_value ) {
291 294 if ( is_array( $gap_value ) ) {
292 295 foreach ( $gap_value as $key => $value ) {
293 - $gap_value[ $key ] = ! is_scalar( $value ) || ( $value && preg_match( '%[\\\(&=}]|/\*%', (string) $value ) ) ? null : $value;
296 + $sanitized_value = gutenberg_sanitize_block_gap_value( $value );
297 + if ( ! is_string( $sanitized_value ) ) {
298 + unset( $gap_value[ $key ] );
299 + continue;
300 + }
301 + $gap_value[ $key ] = $sanitized_value;
294 302 }
295 - return $gap_value;
303 + return empty( $gap_value ) ? null : $gap_value;
296 304 }
297 305
306 + if ( ( is_int( $gap_value ) || is_float( $gap_value ) ) && 0.0 === (float) $gap_value ) {
307 + return '0';
308 + }
309 +
310 + if ( ! is_string( $gap_value ) ) {
311 + return null;
312 + }
313 +
314 + if ( '' === trim( $gap_value ) ) {
315 + return null;
316 + }
317 +
298 318 return $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
299 319 }
300 320
301 321 /**
@@ -300,11 +320,11 @@
300 320
301 321 /**
302 322 * Returns child layout styles for a block affected by its parent's layout.
303 323 *
304 - * @param string $selector CSS selector.
305 - * @param array $child_layout Child layout values.
306 - * @param array $parent_layout Parent layout values.
324 + * @param string $selector CSS selector.
325 + * @param array $child_layout Child layout values.
326 + * @param array $parent_layout Parent layout values.
307 327 * @param array|null $viewport_overrides Optional. Child viewport layout overrides to emit.
308 328 * @return array Child layout style rules.
309 329 */
310 330 function gutenberg_get_child_layout_style_rules( $selector, $child_layout, $parent_layout = array(), $viewport_overrides = null ) {
@@ -359,10 +379,19 @@
359 379 $child_layout_declarations['flex-grow'] = '1';
360 380 }
361 381 }
362 382
363 - $column_start = $child_layout['columnStart'] ?? null;
364 - $column_span = $child_layout['columnSpan'] ?? null;
383 + /*
384 + * Grid line numbers and spans are whole numbers. The editor stores them as numbers, but
385 + * content saved by WordPress 6.3 to 6.6 stored them as numeric strings, and that
386 + * migration only runs when a block is parsed in JavaScript, so the front end still sees
387 + * strings. Accept any numeric value and cast it, and treat anything else as absent
388 + * because it can't render as valid CSS.
389 + */
390 + $column_start_attr = $child_layout['columnStart'] ?? null;
391 + $column_start = is_numeric( $column_start_attr ) ? (int) $column_start_attr : null;
392 + $column_span_attr = $child_layout['columnSpan'] ?? null;
393 + $column_span = is_numeric( $column_span_attr ) ? (int) $column_span_attr : null;
365 394 if ( null === $viewport_overrides || $has_viewport_property_override( 'columnStart' ) || $has_viewport_property_override( 'columnSpan' ) ) {
366 395 if ( $column_start && $column_span ) {
367 396 $child_layout_declarations['grid-column'] = "$column_start / span $column_span";
368 397 } elseif ( $column_start ) {
@@ -371,10 +400,12 @@
371 400 $child_layout_declarations['grid-column'] = "span $column_span";
372 401 }
373 402 }
374 403
375 - $row_start = $child_layout['rowStart'] ?? null;
376 - $row_span = $child_layout['rowSpan'] ?? null;
404 + $row_start_attr = $child_layout['rowStart'] ?? null;
405 + $row_start = is_numeric( $row_start_attr ) ? (int) $row_start_attr : null;
406 + $row_span_attr = $child_layout['rowSpan'] ?? null;
407 + $row_span = is_numeric( $row_span_attr ) ? (int) $row_span_attr : null;
377 408 if ( null === $viewport_overrides || $has_viewport_property_override( 'rowStart' ) || $has_viewport_property_override( 'rowSpan' ) ) {
378 409 if ( $row_start && $row_span ) {
379 410 $child_layout_declarations['grid-row'] = "$row_start / span $row_span";
380 411 } elseif ( $row_start ) {
@@ -390,10 +421,11 @@
390 421 'declarations' => $child_layout_declarations,
391 422 );
392 423 }
393 424
394 - $minimum_column_width = $parent_layout['minimumColumnWidth'] ?? null;
395 - $column_count = $parent_layout['columnCount'] ?? null;
425 + $minimum_column_width_attr = $parent_layout['minimumColumnWidth'] ?? null;
426 + $minimum_column_width = is_string( $minimum_column_width_attr ) ? $minimum_column_width_attr : null;
427 + $column_count = $parent_layout['columnCount'] ?? null;
396 428
397 429 /*
398 430 * If columnSpan or columnStart is set, and the parent grid is responsive, i.e. if it has a minimumColumnWidth set,
399 431 * the columnSpan should be removed once the grid is smaller than the span, and columnStart should be removed
@@ -459,20 +491,27 @@
459 491
460 492 /**
461 493 * Generates the CSS corresponding to the provided layout.
462 494 *
463 - * @param string $selector CSS selector.
464 - * @param array $layout Layout object. The one that is passed has already checked
465 - * the existence of default block layout.
466 - * @param bool $has_block_gap_support Optional. Whether the theme has support for the block gap. Default false.
467 - * @param string|string[]|null $gap_value Optional. The block gap value to apply. Default null.
468 - * @param bool $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the editor. Default false.
469 - * @param string|array $fallback_gap_value Optional. The block gap value to apply. If it's an array expected properties are "top" and/or "left". Default '0.5em'.
470 - * @param array|null $block_spacing Optional. Custom spacing set on the block. Default null.
471 - * @param array $options Optional. Extra options for internal callers. Default empty array.
495 + * @param string $selector CSS selector.
496 + * @param array $layout Layout object. The one that is passed has already checked
497 + * the existence of default block layout.
498 + * @param bool $has_block_gap_support Optional. Whether the theme has support for the block gap. Default false.
499 + * @param string|string[]|int|float|null $gap_value Optional. The block gap value to apply. Only zero is accepted as a
500 + * numeric value. Default null.
501 + * @param bool $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the
502 + * editor. Default false.
503 + * @param string|string[]|int|float|null $fallback_gap_value Optional. The fallback block gap value to apply. Only zero is accepted
504 + * as a numeric value. Default '0.5em'.
505 + * @param array|null $block_spacing Optional. Custom spacing set on the block. Default null.
506 + * @param array $options Optional. Extra options for internal callers. Default empty array.
472 507 * @return string CSS styles, or empty string.
473 508 */
474 509 function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null, $options = array() ) {
510 + // Normalize here as well as at external data boundaries because this function has direct callers.
511 + $gap_value = gutenberg_sanitize_block_gap_value( $gap_value );
512 + $fallback_gap_value = gutenberg_sanitize_block_gap_value( $fallback_gap_value ) ?? '0.5em';
513 +
475 514 $base_layout = is_array( $layout ) ? $layout : array();
476 515 $viewport_overrides = $options['viewport_overrides'] ?? null;
477 516 $layout_for_styles = null === $viewport_overrides ? $base_layout : array_replace( $base_layout, $viewport_overrides );
478 517 $layout_type = $base_layout['type'] ?? 'default';
@@ -519,11 +558,16 @@
519 558 );
520 559 }
521 560 }
522 561 } elseif ( 'constrained' === $layout_type ) {
523 - $content_size = $layout_for_styles['contentSize'] ?? '';
524 - $wide_size = $layout_for_styles['wideSize'] ?? '';
525 - $justify_content = $layout_for_styles['justifyContent'] ?? 'center';
562 + // The schemas and editor UI only produce strings here, so treat a non-string
563 + // value as absent rather than casting it — it couldn't render as valid CSS anyway.
564 + $content_size_attr = $layout_for_styles['contentSize'] ?? null;
565 + $content_size = is_string( $content_size_attr ) ? $content_size_attr : '';
566 + $wide_size_attr = $layout_for_styles['wideSize'] ?? null;
567 + $wide_size = is_string( $wide_size_attr ) ? $wide_size_attr : '';
568 + $justify_content_attr = $layout_for_styles['justifyContent'] ?? null;
569 + $justify_content = is_string( $justify_content_attr ) ? $justify_content_attr : 'center';
526 570
527 571 // Check if viewport-specific ("override") values exist. Null values are valid and mean the user cleared a value inherited from the default viewport.
528 572 $has_justify_content_override = null !== $viewport_overrides && $has_viewport_property_override( 'justifyContent' );
529 573 $has_content_size_override = null !== $viewport_overrides && $has_viewport_property_override( 'contentSize' );
@@ -732,9 +776,9 @@
732 776 $combined_gap_value .= "$process_value ";
733 777 }
734 778 $gap_value = trim( $combined_gap_value );
735 779
736 - if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
780 + if ( '' !== $gap_value && ! $should_skip_gap_serialization ) {
737 781 $layout_styles[] = array(
738 782 'selector' => $selector,
739 783 'declarations' => array( 'gap' => $gap_value ),
740 784 );
@@ -740,25 +784,39 @@
740 784 );
741 785 }
742 786 }
743 787
788 + $flex_justify_content = $layout_for_styles['justifyContent'] ?? null;
789 + $flex_vertical_alignment = $layout_for_styles['verticalAlignment'] ?? null;
790 +
744 791 if ( 'horizontal' === $layout_orientation ) {
745 792 /*
793 + * `row` is the flex default, so the base layout never declares it. A viewport
794 + * override that switches a vertical base layout to horizontal has to declare
795 + * it explicitly, otherwise the base `flex-direction: column` keeps applying.
796 + */
797 + if ( null !== $viewport_overrides && $has_viewport_property_override( 'orientation' ) ) {
798 + $layout_styles[] = array(
799 + 'selector' => $selector,
800 + 'declarations' => array( 'flex-direction' => 'row' ),
801 + );
802 + }
803 + /*
746 804 * Add this style only if is not empty for backwards compatibility,
747 805 * since we intend to convert blocks that had flex layout implemented
748 806 * by custom css.
749 807 */
750 - if ( $should_output_flex_justification && ! empty( $layout_for_styles['justifyContent'] ) && array_key_exists( $layout_for_styles['justifyContent'], $justify_content_options ) ) {
808 + if ( $should_output_flex_justification && ! empty( $flex_justify_content ) && is_string( $flex_justify_content ) && array_key_exists( $flex_justify_content, $justify_content_options ) ) {
751 809 $layout_styles[] = array(
752 810 'selector' => $selector,
753 - 'declarations' => array( 'justify-content' => $justify_content_options[ $layout_for_styles['justifyContent'] ] ),
811 + 'declarations' => array( 'justify-content' => $justify_content_options[ $flex_justify_content ] ),
754 812 );
755 813 }
756 814
757 - if ( $should_output_flex_alignment && ! empty( $layout_for_styles['verticalAlignment'] ) && array_key_exists( $layout_for_styles['verticalAlignment'], $vertical_alignment_options ) ) {
815 + if ( $should_output_flex_alignment && ! empty( $flex_vertical_alignment ) && is_string( $flex_vertical_alignment ) && array_key_exists( $flex_vertical_alignment, $vertical_alignment_options ) ) {
758 816 $layout_styles[] = array(
759 817 'selector' => $selector,
760 - 'declarations' => array( 'align-items' => $vertical_alignment_options[ $layout_for_styles['verticalAlignment'] ] ),
818 + 'declarations' => array( 'align-items' => $vertical_alignment_options[ $flex_vertical_alignment ] ),
761 819 );
762 820 }
763 821 } else {
764 822 if ( $should_output_flex_orientation ) {
@@ -766,12 +824,12 @@
766 824 'selector' => $selector,
767 825 'declarations' => array( 'flex-direction' => 'column' ),
768 826 );
769 827 }
770 - if ( $should_output_flex_justification && ! empty( $layout_for_styles['justifyContent'] ) && array_key_exists( $layout_for_styles['justifyContent'], $justify_content_options ) ) {
828 + if ( $should_output_flex_justification && ! empty( $flex_justify_content ) && is_string( $flex_justify_content ) && array_key_exists( $flex_justify_content, $justify_content_options ) ) {
771 829 $layout_styles[] = array(
772 830 'selector' => $selector,
773 - 'declarations' => array( 'align-items' => $justify_content_options[ $layout_for_styles['justifyContent'] ] ),
831 + 'declarations' => array( 'align-items' => $justify_content_options[ $flex_justify_content ] ),
774 832 );
775 833 } elseif ( $should_output_flex_justification ) {
776 834 $layout_styles[] = array(
777 835 'selector' => $selector,
@@ -777,18 +835,27 @@
777 835 'selector' => $selector,
778 836 'declarations' => array( 'align-items' => 'flex-start' ),
779 837 );
780 838 }
781 - if ( $should_output_flex_alignment && ! empty( $layout_for_styles['verticalAlignment'] ) && array_key_exists( $layout_for_styles['verticalAlignment'], $vertical_alignment_options ) ) {
839 + if ( $should_output_flex_alignment && ! empty( $flex_vertical_alignment ) && is_string( $flex_vertical_alignment ) && array_key_exists( $flex_vertical_alignment, $vertical_alignment_options ) ) {
782 840 $layout_styles[] = array(
783 841 'selector' => $selector,
784 - 'declarations' => array( 'justify-content' => $vertical_alignment_options[ $layout_for_styles['verticalAlignment'] ] ),
842 + 'declarations' => array( 'justify-content' => $vertical_alignment_options[ $flex_vertical_alignment ] ),
785 843 );
786 844 }
787 845 }
788 846 } elseif ( 'grid' === $layout_type ) {
789 847 /*
790 - * If the gap value is an array, we use the "left" value because it represents the vertical gap, which
848 + * Column and row counts are whole numbers, for the same reason as the grid line
849 + * numbers in gutenberg_get_child_layout_style_rules().
850 + */
851 + $column_count_attr = $layout_for_styles['columnCount'] ?? null;
852 + $column_count = is_numeric( $column_count_attr ) ? (int) $column_count_attr : null;
853 + $row_count_attr = $layout_for_styles['rowCount'] ?? null;
854 + $row_count = is_numeric( $row_count_attr ) ? (int) $row_count_attr : null;
855 +
856 + /*
857 + * If the gap value is an array, we use the "left" value because it represents the horizontal gap, which
791 858 * is the relevant one for computation of responsive grid columns.
792 859 */
793 860 if ( is_array( $fallback_gap_value ) ) {
794 861 $responsive_gap_value = $fallback_gap_value['left'] ?? reset( $fallback_gap_value );
@@ -815,12 +882,14 @@
815 882 $index_to_splice = strrpos( $process_value, '|' ) + 1;
816 883 $slug = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) );
817 884 $process_value = "var(--wp--preset--spacing--$slug)";
818 885 }
886 + if ( ! is_array( $gap_value ) || 'left' === $gap_side ) {
887 + $responsive_gap_value = $process_value;
888 + }
819 889 $combined_gap_value .= "$process_value ";
820 890 }
821 - $gap_value = trim( $combined_gap_value );
822 - $responsive_gap_value = $gap_value;
891 + $gap_value = trim( $combined_gap_value );
823 892 }
824 893
825 894 // Ensure 0 values have a unit so they work in calc().
826 895 if ( '0' === $responsive_gap_value || 0 === $responsive_gap_value ) {
@@ -830,14 +899,14 @@
830 899 /* Styles should be output either if there are no viewport overrides (this is the default case), or if the user has set a new viewport-specific
831 900 * value for any of the grid properties.
832 901 */
833 902 $should_output_grid_columns = null === $viewport_overrides || $has_viewport_property_override( 'minimumColumnWidth' ) || $has_viewport_property_override( 'columnCount' ) || $has_viewport_property_override( 'autoFit' );
834 - $uses_gap_in_grid_columns = ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] );
903 + $uses_gap_in_grid_columns = ! empty( $column_count ) && ! empty( $layout_for_styles['minimumColumnWidth'] );
835 904 if ( $has_block_gap_override && $uses_gap_in_grid_columns ) {
836 905 $should_output_grid_columns = true;
837 906 }
838 907
839 - $should_output_grid_rows = ( null === $viewport_overrides || $has_viewport_property_override( 'rowCount' ) ) && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['rowCount'] );
908 + $should_output_grid_rows = ( null === $viewport_overrides || $has_viewport_property_override( 'rowCount' ) ) && ! empty( $column_count ) && ! empty( $row_count );
840 909 $grid_declarations = array();
841 910
842 911 /* When enabled, columns stretch to fill the available space using
843 912 * `auto-fit`; otherwise empty tracks are preserved with `auto-fill`.
@@ -843,13 +912,13 @@
843 912 * `auto-fit`; otherwise empty tracks are preserved with `auto-fill`.
844 913 */
845 914 $auto_placement = ! empty( $layout_for_styles['autoFit'] ) ? 'auto-fit' : 'auto-fill';
846 915
847 - if ( $should_output_grid_columns && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] ) ) {
848 - $max_value = 'max(min(' . $layout_for_styles['minimumColumnWidth'] . ', 100%), (100% - (' . $responsive_gap_value . ' * (' . $layout_for_styles['columnCount'] . ' - 1))) /' . $layout_for_styles['columnCount'] . ')';
916 + if ( $should_output_grid_columns && ! empty( $column_count ) && ! empty( $layout_for_styles['minimumColumnWidth'] ) ) {
917 + $max_value = 'max(min(' . $layout_for_styles['minimumColumnWidth'] . ', 100%), (100% - (' . $responsive_gap_value . ' * (' . $column_count . ' - 1))) /' . $column_count . ')';
849 918 $grid_declarations['grid-template-columns'] = 'repeat(' . $auto_placement . ', minmax(' . $max_value . ', 1fr))';
850 - } elseif ( $should_output_grid_columns && ! empty( $layout_for_styles['columnCount'] ) ) {
851 - $grid_declarations['grid-template-columns'] = 'repeat(' . $layout_for_styles['columnCount'] . ', minmax(0, 1fr))';
919 + } elseif ( $should_output_grid_columns && ! empty( $column_count ) ) {
920 + $grid_declarations['grid-template-columns'] = 'repeat(' . $column_count . ', minmax(0, 1fr))';
852 921 } elseif ( $should_output_grid_columns ) {
853 922 $minimum_column_width = ! empty( $layout_for_styles['minimumColumnWidth'] ) ? $layout_for_styles['minimumColumnWidth'] : '12rem';
854 923 $grid_declarations['grid-template-columns'] = 'repeat(' . $auto_placement . ', minmax(min(' . $minimum_column_width . ', 100%), 1fr))';
855 924 }
@@ -855,9 +924,9 @@
855 924 }
856 925
857 926 if ( ! empty( $grid_declarations ) ) {
858 927 $base_has_container_type = empty( $base_layout['columnCount'] ) || ( ! empty( $base_layout['columnCount'] ) && ! empty( $base_layout['minimumColumnWidth'] ) );
859 - if ( empty( $layout_for_styles['columnCount'] ) || ! empty( $layout_for_styles['minimumColumnWidth'] ) ) {
928 + if ( empty( $column_count ) || ! empty( $layout_for_styles['minimumColumnWidth'] ) ) {
860 929 if ( null === $viewport_overrides || ! $base_has_container_type ) {
861 930 $grid_declarations['container-type'] = 'inline-size';
862 931 }
863 932 }
@@ -869,9 +938,9 @@
869 938
870 939 if ( $should_output_grid_rows ) {
871 940 $layout_styles[] = array(
872 941 'selector' => $selector,
873 - 'declarations' => array( 'grid-template-rows' => 'repeat(' . $layout_for_styles['rowCount'] . ', minmax(1rem, auto))' ),
942 + 'declarations' => array( 'grid-template-rows' => 'repeat(' . $row_count . ', minmax(1rem, auto))' ),
874 943 );
875 944 }
876 945
877 946 if ( $has_block_gap_support && $should_output_block_gap && null !== $gap_value && ! $should_skip_gap_serialization ) {
@@ -915,10 +984,10 @@
915 984 * and increments on each call. The returned value is not universally unique,
916 985 * but it is unique across the life of the PHP process and it's stable per
917 986 * prefix.
918 987 *
919 - * @param string $prefix Prefix for the returned ID.
920 - * @return string Incremental ID per prefix.
988 + * @param string $prefix Prefix for the returned ID.
989 + * @return string Incremental ID per prefix.
921 990 */
922 991 function gutenberg_incremental_id_per_prefix( $prefix = '' ) {
923 992 static $id_counters = array();
924 993 if ( ! array_key_exists( $prefix, $id_counters ) ) {
@@ -947,15 +1016,13 @@
947 1016
948 1017 /**
949 1018 * Renders the layout config to the block wrapper.
950 1019 *
951 - * @param string $block_content Rendered block content.
952 - * @param array $block Block object.
953 - * @return string Filtered block content.
1020 + * @param string $block_content Rendered block content.
1021 + * @param array $block Block object.
1022 + * @return string Filtered block content.
954 1023 */
955 1024 function gutenberg_render_layout_support_flag( $block_content, $block ) {
956 - static $global_styles = null;
957 -
958 1025 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
959 1026 $block_supports_layout = block_has_support( $block_type, array( 'layout' ), false ) || block_has_support( $block_type, array( '__experimentalLayout' ), false );
960 1027 $style_attr = gutenberg_resolve_style_state_aliases(
961 1028 $block['attrs']['style'] ?? array(),
@@ -1120,22 +1187,25 @@
1120 1187 * removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is
1121 1188 * not intended to provide an extended set of classes to match all block layout attributes
1122 1189 * here.
1123 1190 */
1124 - if ( ! empty( $block['attrs']['layout']['orientation'] ) ) {
1125 - $class_names[] = 'is-' . sanitize_title( $block['attrs']['layout']['orientation'] );
1191 + $orientation = $block['attrs']['layout']['orientation'] ?? null;
1192 + if ( ! empty( $orientation ) && is_string( $orientation ) ) {
1193 + $class_names[] = 'is-' . sanitize_title( $orientation );
1126 1194 }
1127 1195
1128 - if ( ! empty( $block['attrs']['layout']['justifyContent'] ) ) {
1129 - $class_names[] = 'is-content-justification-' . sanitize_title( $block['attrs']['layout']['justifyContent'] );
1196 + $justify_content = $block['attrs']['layout']['justifyContent'] ?? null;
1197 + if ( ! empty( $justify_content ) && is_string( $justify_content ) ) {
1198 + $class_names[] = 'is-content-justification-' . sanitize_title( $justify_content );
1130 1199 }
1131 1200
1132 - if ( ! empty( $block['attrs']['layout']['flexWrap'] ) && 'nowrap' === $block['attrs']['layout']['flexWrap'] ) {
1201 + $flex_wrap = $block['attrs']['layout']['flexWrap'] ?? null;
1202 + if ( ! empty( $flex_wrap ) && 'nowrap' === $flex_wrap ) {
1133 1203 $class_names[] = 'is-nowrap';
1134 1204 }
1135 1205
1136 1206 // Get classname for layout type.
1137 - if ( isset( $used_layout['type'] ) ) {
1207 + if ( isset( $used_layout['type'] ) && is_string( $used_layout['type'] ) ) {
1138 1208 $layout_classname = $layout_definitions[ $used_layout['type'] ]['className'] ?? '';
1139 1209 } else {
1140 1210 $layout_classname = $layout_definitions['default']['className'] ?? '';
1141 1211 }
@@ -1151,9 +1221,11 @@
1151 1221 if ( ! current_theme_supports( 'disable-layout-styles' ) ) {
1152 1222
1153 1223 $gap_value = gutenberg_sanitize_block_gap_value( $block['attrs']['style']['spacing']['blockGap'] ?? null );
1154 1224
1155 - $fallback_gap_value = $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ?? '0.5em';
1225 + $fallback_gap_value = gutenberg_sanitize_block_gap_value(
1226 + $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ?? null
1227 + ) ?? '0.5em';
1156 1228 $block_spacing = $block['attrs']['style']['spacing'] ?? null;
1157 1229
1158 1230 /*
1159 1231 * If a block's block.json skips serialization for spacing or spacing.blockGap,
@@ -1165,12 +1237,10 @@
1165 1237 $has_block_gap_support = isset( $block_gap );
1166 1238
1167 1239 // Get default blockGap value from global styles for use in layouts like grid.
1168 1240 // Check style variation first, then block-specific styles, then fall back to root styles.
1169 - $block_name = $block['blockName'] ?? '';
1170 - if ( null === $global_styles ) {
1171 - $global_styles = gutenberg_get_global_styles();
1172 - }
1241 + $block_name = $block['blockName'] ?? '';
1242 + $global_styles = gutenberg_get_global_styles();
1173 1243
1174 1244 // Check if the block has an active style variation with a blockGap value.
1175 1245 // Only check the registry if the className contains a variation class to avoid unnecessary lookups.
1176 1246 $variation_block_gap_value = null;
@@ -1185,9 +1255,21 @@
1185 1255 $variation_block_gap_value = $global_styles['blocks'][ $block_name ]['variations'][ $variation_name ]['spacing']['blockGap'] ?? null;
1186 1256 }
1187 1257 }
1188 1258
1189 - $global_block_gap_value = $variation_block_gap_value ?? $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? $global_styles['spacing']['blockGap'] ?? null;
1259 + $global_block_gap_candidates = array(
1260 + $variation_block_gap_value,
1261 + $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? null,
1262 + $global_styles['spacing']['blockGap'] ?? null,
1263 + );
1264 + $global_block_gap_value = null;
1265 + foreach ( $global_block_gap_candidates as $candidate_gap_value ) {
1266 + $candidate_gap_value = gutenberg_sanitize_block_gap_value( $candidate_gap_value );
1267 + if ( null !== $candidate_gap_value ) {
1268 + $global_block_gap_value = $candidate_gap_value;
1269 + break;
1270 + }
1271 + }
1190 1272
1191 1273 if ( null !== $global_block_gap_value ) {
1192 1274 $fallback_gap_value = $global_block_gap_value;
1193 1275 }
@@ -1453,14 +1535,15 @@
1453 1535 * For themes without theme.json file, make sure
1454 1536 * to restore the inner div for the group block
1455 1537 * to avoid breaking styles relying on that div.
1456 1538 *
1457 - * @param string $block_content Rendered block content.
1458 - * @param array $block Block object.
1459 - * @return string Filtered block content.
1539 + * @param string $block_content Rendered block content.
1540 + * @param array $block Block object.
1541 + * @return string Filtered block content.
1460 1542 */
1461 1543 function gutenberg_restore_group_inner_container( $block_content, $block ) {
1462 - $tag_name = $block['attrs']['tagName'] ?? 'div';
1544 + $tag_name_attr = $block['attrs']['tagName'] ?? null;
1545 + $tag_name = is_string( $tag_name_attr ) ? $tag_name_attr : 'div';
1463 1546 $group_with_inner_container_regex = sprintf(
1464 1547 '/(^\s*<%1$s\b[^>]*wp-block-group(\s|")[^>]*>)(\s*<div\b[^>]*wp-block-group__inner-container(\s|")[^>]*>)((.|\S|\s)*)/U',
1465 1548 preg_quote( $tag_name, '/' )
1466 1549 );
@@ -1526,9 +1609,9 @@
1526 1609 * to restore the outer div for the aligned image block
1527 1610 * to avoid breaking styles relying on that div.
1528 1611 *
1529 1612 * @param string $block_content Rendered block content.
1530 - * @param array $block Block object.
1613 + * @param array $block Block object.
1531 1614 * @return string Filtered block content.
1532 1615 */
1533 1616 function gutenberg_restore_image_outer_container( $block_content, $block ) {
1534 1617 if ( wp_theme_has_theme_json() ) {