Make WordPress Core

Changeset 56698


Ignore:
Timestamp:
09/26/2023 05:02:36 AM (3 years 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.