Make WordPress Core

Changeset 56698


Ignore:
Timestamp:
09/26/2023 05:02:36 AM (20 months ago)
Author:
isabel_brison
Message:

Editor: make better use of Tag Processor in layout block support.

Refactors layout support to use a single Tag Processor instance and send one class at a time to add_class().

Props dmsnell, hellofromTonya.
Fixes #59443.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-supports/layout.php

    r56559 r56698  
    548548 */
    549549function wp_render_layout_support_flag( $block_content, $block ) {
    550     $block_type       = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
    551     $support_layout  = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
    552     $has_child_layout = isset( $block['attrs']['style']['layout']['selfStretch'] );
    553 
    554     if ( ! $support_layout && ! $has_child_layout ) {
     550    $block_type            = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
     551    $block_supports_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
     552    $layout_from_parent    = $block['attrs']['style']['layout']['selfStretch'] ?? null;
     553
     554    if ( ! $block_supports_layout && ! $layout_from_parent ) {
    555555        return $block_content;
    556556    }
     557
    557558    $outer_class_names = array();
    558559
    559     if ( $has_child_layout && ( 'fixed' === $block['attrs']['style']['layout']['selfStretch'] || 'fill' === $block['attrs']['style']['layout']['selfStretch'] ) ) {
     560    if ( 'fixed' === $layout_from_parent || 'fill' === $layout_from_parent ) {
    560561        $container_content_class = wp_unique_id( 'wp-container-content-' );
    561562
    562563        $child_layout_styles = array();
    563564
    564         if ( 'fixed' === $block['attrs']['style']['layout']['selfStretch'] && isset( $block['attrs']['style']['layout']['flexSize'] ) ) {
     565        if ( 'fixed' === $layout_from_parent && isset( $block['attrs']['style']['layout']['flexSize'] ) ) {
    565566            $child_layout_styles[] = array(
    566567                'selector'     => ".$container_content_class",
     
    570571                ),
    571572            );
    572         } elseif ( 'fill' === $block['attrs']['style']['layout']['selfStretch'] ) {
     573        } elseif ( 'fill' === $layout_from_parent ) {
    573574            $child_layout_styles[] = array(
    574575                'selector'     => ".$container_content_class",
     
    590591    }
    591592
    592     // Return early if only child layout exists.
    593     if ( ! $support_layout && ! empty( $outer_class_names ) ) {
    594         $content = new WP_HTML_Tag_Processor( $block_content );
    595         $content->next_tag();
    596         $content->add_class( implode( ' ', $outer_class_names ) );
    597         return (string) $content;
     593    // Prep the processor for modifying the block output.
     594    $processor = new WP_HTML_Tag_Processor( $block_content );
     595
     596    // Having no tags implies there are no tags onto which to add class names.
     597    if ( ! $processor->next_tag() ) {
     598        return $block_content;
     599    }
     600
     601    /*
     602     * A block may not support layout but still be affected by a parent block's layout.
     603     *
     604     * In these cases add the appropriate class names and then return early; there's
     605     * no need to investigate on this block whether additional layout constraints apply.
     606     */
     607    if ( ! $block_supports_layout && ! empty( $outer_class_names ) ) {
     608        foreach ( $outer_class_names as $class_name ) {
     609            $processor->add_class( $class_name );
     610        }
     611        return $processor->get_updated_html();
    598612    }
    599613
     
    605619    $layout_definitions = wp_get_layout_definitions();
    606620    $container_class    = wp_unique_id( 'wp-container-' );
    607     $layout_classname   = '';
    608621
    609622    // Set the correct layout type for blocks using legacy content width.
     
    703716    $class_names[] = 'wp-block-' . end( $block_name ) . '-' . $layout_classname;
    704717
    705     $content_with_outer_classnames = '';
    706 
     718    // Add classes to the outermost HTML tag if necessary.
    707719    if ( ! empty( $outer_class_names ) ) {
    708         $content_with_outer_classnames = new WP_HTML_Tag_Processor( $block_content );
    709         $content_with_outer_classnames->next_tag();
    710720        foreach ( $outer_class_names as $outer_class_name ) {
    711             $content_with_outer_classnames->add_class( $outer_class_name );
    712         }
    713 
    714         $content_with_outer_classnames = (string) $content_with_outer_classnames;
     721            $processor->add_class( $outer_class_name );
     722        }
    715723    }
    716724
    717725    /**
    718     * The first chunk of innerContent contains the block markup up until the inner blocks start.
    719     * This targets the opening tag of the inner blocks wrapper, which is the last tag in that chunk.
    720     */
    721     $inner_content_classnames = '';
    722 
    723     if ( isset( $block['innerContent'][0] ) && 'string' === gettype( $block['innerContent'][0] ) && count( $block['innerContent'] ) > 1 ) {
    724         $tags            = new WP_HTML_Tag_Processor( $block['innerContent'][0] );
    725         $last_classnames = '';
    726         while ( $tags->next_tag() ) {
    727             $last_classnames = $tags->get_attribute( 'class' );
    728         }
    729 
    730         $inner_content_classnames = (string) $last_classnames;
    731     }
    732 
    733     $content = $content_with_outer_classnames ? new WP_HTML_Tag_Processor( $content_with_outer_classnames ) : new WP_HTML_Tag_Processor( $block_content );
    734 
    735     if ( $inner_content_classnames ) {
    736         $content->next_tag( array( 'class_name' => $inner_content_classnames ) );
    737         foreach ( $class_names as $class_name ) {
    738             $content->add_class( $class_name );
    739         }
    740     } else {
    741         $content->next_tag();
    742         foreach ( $class_names as $class_name ) {
    743             $content->add_class( $class_name );
    744         }
    745     }
    746 
    747     return (string) $content;
     726     * Attempts to refer to the inner-block wrapping element by its class attribute.
     727     *
     728     * When examining a block's inner content, if a block has inner blocks, then
     729     * the first content item will likely be a text (HTML) chunk immediately
     730     * preceding the inner blocks. The last HTML tag in that chunk would then be
     731     * an opening tag for an element that wraps the inner blocks.
     732     *
     733     * There's no reliable way to associate this wrapper in $block_content because
     734     * it may have changed during the rendering pipeline (as inner contents is
     735     * provided before rendering) and through previous filters. In many cases,
     736     * however, the `class` attribute will be a good-enough identifier, so this
     737     * code finds the last tag in that chunk and stores the `class` attribute
     738     * so that it can be used later when working through the rendered block output
     739     * to identify the wrapping element and add the remaining class names to it.
     740     *
     741     * It's also possible that no inner block wrapper even exists. If that's the
     742     * case this code could apply the class names to an invalid element.
     743     *
     744     * Example:
     745     *
     746     *     $block['innerBlocks']  = array( $list_item );
     747     *     $block['innerContent'] = array( '<ul class="list-wrapper is-unordered">', null, '</ul>' );
     748     *
     749     *     // After rendering, the initial contents may have been modified by other renderers or filters.
     750     *     $block_content = <<<HTML
     751     *         <figure>
     752     *             <ul class="annotated-list list-wrapper is-unordered">
     753     *                 <li>Code</li>
     754     *             </ul><figcaption>It's a list!</figcaption>
     755     *         </figure>
     756     *     HTML;
     757     *
     758     * Although it is possible that the original block-wrapper classes are changed in $block_content
     759     * from how they appear in $block['innerContent'], it's likely that the original class attributes
     760     * are still present in the wrapper as they are in this example. Frequently, additional classes
     761     * will also be present; rarely should classes be removed.
     762     *
     763     * @TODO: Find a better way to match the first inner block. If it's possible to identify where the
     764     *        first inner block starts, then it will be possible to find the last tag before it starts
     765     *        and then that tag, if an opening tag, can be solidly identified as a wrapping element.
     766     *        Can some unique value or class or ID be added to the inner blocks when they process
     767     *        so that they can be extracted here safely without guessing? Can the block rendering function
     768     *        return information about where the rendered inner blocks start?
     769     *
     770     * @var string|null
     771     */
     772    $inner_block_wrapper_classes = null;
     773    $first_chunk                 = $block['innerContent'][0] ?? null;
     774    if ( is_string( $first_chunk ) && count( $block['innerContent'] ) > 1 ) {
     775        $first_chunk_processor = new WP_HTML_Tag_Processor( $first_chunk );
     776        while ( $first_chunk_processor->next_tag() ) {
     777            $class_attribute = $first_chunk_processor->get_attribute( 'class' );
     778            if ( is_string( $class_attribute ) && ! empty( $class_attribute ) ) {
     779                $inner_block_wrapper_classes = $class_attribute;
     780            }
     781        }
     782    }
     783
     784    /*
     785     * If necessary, advance to what is likely to be an inner block wrapper tag.
     786     *
     787     * This advances until it finds the first tag containing the original class
     788     * attribute from above. If none is found it will scan to the end of the block
     789     * and fail to add any class names.
     790     *
     791     * If there is no block wrapper it won't advance at all, in which case the
     792     * class names will be added to the first and outermost tag of the block.
     793     * For cases where this outermost tag is the only tag surrounding inner
     794     * blocks then the outer wrapper and inner wrapper are the same.
     795     */
     796    do {
     797        if ( ! $inner_block_wrapper_classes ) {
     798            break;
     799        }
     800
     801        if ( false !== strpos( $processor->get_attribute( 'class' ), $inner_block_wrapper_classes ) ) {
     802            break;
     803        }
     804    } while ( $processor->next_tag() );
     805
     806    // Add the remaining class names.
     807    foreach ( $class_names as $class_name ) {
     808        $processor->add_class( $class_name );
     809    }
     810
     811    return $processor->get_updated_html();
    748812}
    749813
Note: See TracChangeset for help on using the changeset viewer.