WordPress.org

Make WordPress Core

Changeset 48237


Ignore:
Timestamp:
06/30/2020 07:28:07 PM (3 months ago)
Author:
flixos90
Message:

Media: Introduce wp_img_tag_add_width_and_height_attr() to add dimension attributes to images.

Following up on [48170], this changeset moves the new logic to add missing img dimension attributes into a separate function that is run first within wp_filter_content_tags(). It also adds a utility function wp_image_src_get_dimensions() with logic reused from wp_image_add_srcset_and_sizes(), and it ensures that width and height attributes only get added if both of the attributes are missing on the original img tag.

This changeset furthermore improves test coverage and separates tests for the different aspects of img tag modification.

Props azaozz.
Fixes #50367. See #44427.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/media.php

    r48198 r48237  
    14931493
    14941494/**
     1495 * Determines an image's width and height dimensions based on the source file.
     1496 *
     1497 * @since 5.5.0
     1498 *
     1499 * @param string $image_src  The image source file.
     1500 * @param array  $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'.
     1501 * @return array|false Array with first element being the width and second element being the height,
     1502 *                     or false if dimensions cannot be determined.
     1503 */
     1504function wp_image_src_get_dimensions( $image_src, $image_meta ) {
     1505    $image_filename = wp_basename( $image_src );
     1506
     1507    if ( wp_basename( $image_meta['file'] ) === $image_filename ) {
     1508        return array(
     1509            (int) $image_meta['width'],
     1510            (int) $image_meta['height'],
     1511        );
     1512    }
     1513
     1514    foreach ( $image_meta['sizes'] as $image_size_data ) {
     1515        if ( $image_filename === $image_size_data['file'] ) {
     1516            return array(
     1517                (int) $image_size_data['width'],
     1518                (int) $image_size_data['height'],
     1519            );
     1520        }
     1521    }
     1522
     1523    return false;
     1524}
     1525
     1526/**
    14951527 * Adds 'srcset' and 'sizes' attributes to an existing 'img' element.
    14961528 *
    14971529 * @since 4.4.0
    1498  * @since 5.5.0 `width` and `height` are now added if not already present.
    14991530 *
    15001531 * @see wp_calculate_image_srcset()
     
    15271558    }
    15281559
    1529     $attr = '';
    1530 
    15311560    $width  = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0;
    15321561    $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
    15331562
    1534     if ( ! $width || ! $height ) {
    1535         /*
    1536          * If attempts to parse the size value failed, attempt to use the image meta data to match
    1537          * the image file name from 'src' against the available sizes for an attachment.
    1538          */
    1539         $image_filename = wp_basename( $image_src );
    1540 
    1541         if ( wp_basename( $image_meta['file'] ) === $image_filename ) {
    1542             $width  = (int) $image_meta['width'];
    1543             $height = (int) $image_meta['height'];
    1544         } else {
    1545             foreach ( $image_meta['sizes'] as $image_size_data ) {
    1546                 if ( $image_filename === $image_size_data['file'] ) {
    1547                     $width  = (int) $image_size_data['width'];
    1548                     $height = (int) $image_size_data['height'];
    1549                     break;
    1550                 }
    1551             }
    1552         }
    1553 
    1554         if ( ! $width || ! $height ) {
     1563    if ( $width && $height ) {
     1564        $size_array = array( $width, $height );
     1565    } else {
     1566        $size_array = wp_image_src_get_dimensions( $image_src, $image_meta );
     1567        if ( ! $size_array ) {
    15551568            return $image;
    15561569        }
    1557 
    1558         // Add width and height if not present.
    1559         $attr .= ' ' . trim( image_hwstring( $width, $height ) );
    1560     }
    1561 
    1562     $size_array = array( $width, $height );
    1563     $srcset     = wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id );
     1570    }
     1571
     1572    $srcset = wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id );
    15641573
    15651574    if ( $srcset ) {
     
    15741583    if ( $srcset && $sizes ) {
    15751584        // Format the 'srcset' and 'sizes' string and escape attributes.
    1576         $attr .= sprintf( ' srcset="%s"', esc_attr( $srcset ) );
     1585        $attr = sprintf( ' srcset="%s"', esc_attr( $srcset ) );
    15771586
    15781587        if ( is_string( $sizes ) ) {
    15791588            $attr .= sprintf( ' sizes="%s"', esc_attr( $sizes ) );
    15801589        }
    1581     }
    1582 
    1583     if ( empty( $attr ) ) {
    1584         return $image;
    1585     }
    1586 
    1587     // Add extra attributes to the image markup.
    1588     return preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $attr . ' />', $image );
     1590
     1591        // Add extra attributes to the image markup.
     1592        return preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $attr . ' />', $image );
     1593    }
     1594
     1595    return $image;
    15891596}
    15901597
     
    16221629 * @since 5.5.0
    16231630 *
     1631 * @see wp_img_tag_add_width_and_height_attr()
     1632 * @see wp_img_tag_add_srcset_and_sizes_attr()
    16241633 * @see wp_img_tag_add_loading_attr()
    1625  * @see wp_img_tag_add_srcset_and_sizes_attr()
    16261634 *
    16271635 * @param string $content The HTML content to be filtered.
     
    16771685        $filtered_image = $image;
    16781686
     1687        // Add 'width' and 'height' attributes if applicable.
     1688        if ( $attachment_id > 0 && false === strpos( $filtered_image, ' width=' ) && false === strpos( $filtered_image, ' height=' ) ) {
     1689            $filtered_image = wp_img_tag_add_width_and_height_attr( $filtered_image, $context, $attachment_id );
     1690        }
     1691
    16791692        // Add 'srcset' and 'sizes' attributes if applicable.
    16801693        if ( $attachment_id > 0 && false === strpos( $filtered_image, ' srcset=' ) ) {
     
    17241737        }
    17251738
    1726         // Images should have dimension attributes for the `loading` attribute to be added.
    1727         if ( false === strpos( $image, ' width=' ) || false === strpos( $image, ' height=' ) ) {
     1739        // Images should have source and dimension attributes for the `loading` attribute to be added.
     1740        if ( false === strpos( $image, ' src=' ) || false === strpos( $image, ' width=' ) || false === strpos( $image, ' height=' ) ) {
    17281741            return $image;
    17291742        }
    17301743
    1731         $quote = null;
    1732 
    1733         // Check if the img tag is valid (has `src` attribute) and get the quote character.
    1734         // In almost all cases it will have src and a double quote.
    1735         if ( false !== strpos( $image, ' src="' ) ) {
    1736             $quote = '"';
    1737         } elseif ( preg_match( '/\ssrc\s*=(["\'])/', $image, $matches ) ) {
    1738             $quote = $matches[1];
    1739         }
    1740 
    1741         if ( $quote ) {
    1742             $loading = "loading={$quote}{$value}{$quote}";
    1743 
    1744             return str_replace( '<img', "<img {$loading}", $image );
     1744        return str_replace( '<img', '<img loading="' . esc_attr( $value ) . '"', $image );
     1745    }
     1746
     1747    return $image;
     1748}
     1749
     1750/**
     1751 * Adds `width` and `height` attributes to an `img` HTML tag.
     1752 *
     1753 * @since 5.5.0
     1754 *
     1755 * @param string $image         The HTML `img` tag where the attribute should be added.
     1756 * @param string $context       Additional context to pass to the filters.
     1757 * @param int    $attachment_id Image attachment ID.
     1758 * @return string Converted 'img' element with 'width' and 'height' attributes added.
     1759 */
     1760function wp_img_tag_add_width_and_height_attr( $image, $context, $attachment_id ) {
     1761    $image_src         = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : '';
     1762    list( $image_src ) = explode( '?', $image_src );
     1763
     1764    // Return early if we couldn't get the image source.
     1765    if ( ! $image_src ) {
     1766        return $image;
     1767    }
     1768
     1769    /**
     1770     * Filters whether to add the missing `width` and `height` HTML attributes to the img tag. Default `true`.
     1771     *
     1772     * Returning anything else than `true` will not add the attributes.
     1773     *
     1774     * @since 5.5.0
     1775     *
     1776     * @param bool   $value         The filtered value, defaults to `true`.
     1777     * @param string $image         The HTML `img` tag where the attribute should be added.
     1778     * @param string $context       Additional context about how the function was called or where the img tag is.
     1779     * @param int    $attachment_id The image attachment ID.
     1780     */
     1781    $add = apply_filters( 'wp_img_tag_add_width_and_height_attr', true, $image, $context, $attachment_id );
     1782
     1783    if ( true === $add ) {
     1784        $image_meta = wp_get_attachment_metadata( $attachment_id );
     1785        $size_array = wp_image_src_get_dimensions( $image_src, $image_meta );
     1786
     1787        if ( $size_array ) {
     1788            $hw = trim( image_hwstring( $size_array[0], $size_array[1] ) );
     1789            return str_replace( '<img', "<img {$hw}", $image );
    17451790        }
    17461791    }
  • trunk/tests/phpunit/tests/media.php

    r48190 r48237  
    19641964    /**
    19651965     * @ticket 33641
    1966      * @ticket 50367
    1967      */
    1968     function test_wp_filter_content_tags() {
     1966     */
     1967    function test_wp_filter_content_tags_srcset_sizes() {
    19691968        $image_meta = wp_get_attachment_metadata( self::$large_id );
    19701969        $size_array = $this->_get_image_size_array_from_meta( $image_meta, 'medium' );
     
    19751974        // Function used to build HTML for the editor.
    19761975        $img                  = get_image_tag( self::$large_id, '', '', '', 'medium' );
    1977         $img                  = wp_img_tag_add_loading_attr( $img, 'test' );
    19781976        $img_no_size_in_class = str_replace( 'size-', '', $img );
    19791977        $img_no_width_height  = str_replace( ' width="' . $size_array[0] . '"', '', $img );
     
    19841982        $img_html5            = str_replace( ' />', '>', $img );
    19851983
    1986         $hwstring = image_hwstring( $size_array[0], $size_array[1] );
    1987 
    19881984        // Manually add srcset and sizes to the markup from get_image_tag().
    19891985        $respimg                  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img );
    19901986        $respimg_no_size_in_class = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_size_in_class );
    1991         $respimg_no_width_height  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $hwstring . $srcset . ' ' . $sizes . ' />', $img_no_width_height );
     1987        $respimg_no_width_height  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_width_height );
    19921988        $respimg_with_sizes_attr  = preg_replace( '|<img ([^>]+) />|', '<img $1 ' . $srcset . ' />', $img_with_sizes_attr );
    19931989        $respimg_xhtml            = preg_replace( '|<img ([^>]+)/>|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_xhtml );
     
    20011997            %2$s
    20021998
    2003             <p>Image, no width and height attributes. Should have width, height, srcset and sizes (from matching the file name).</p>
     1999            <p>Image, no width and height attributes. Should have srcset and sizes (from matching the file name).</p>
    20042000            %3$s
    20052001
     
    20192015        $content_filtered   = sprintf( $content, $respimg, $respimg_no_size_in_class, $respimg_no_width_height, $img_no_size_id, $respimg_with_sizes_attr, $respimg_xhtml, $respimg_html5 );
    20202016
     2017        // Do not add width, height, and loading.
     2018        add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
     2019        add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
     2020
    20212021        $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) );
     2022
     2023        remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
     2024        remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
    20222025    }
    20232026
     
    20332036     * @ticket 33641
    20342037     */
    2035     function test_wp_filter_content_tags_wrong() {
     2038    function test_wp_filter_content_tags_srcset_sizes_wrong() {
    20362039        $img = get_image_tag( self::$large_id, '', '', '', 'medium' );
    20372040        $img = wp_img_tag_add_loading_attr( $img, 'test' );
     
    20462049     * @ticket 33641
    20472050     */
    2048     function test_wp_filter_content_tags_with_preexisting_srcset() {
     2051    function test_wp_filter_content_tags_srcset_sizes_with_preexisting_srcset() {
    20492052        // Generate HTML and add a dummy srcset attribute.
    20502053        $img = get_image_tag( self::$large_id, '', '', '', 'medium' );
     
    25332536
    25342537    /**
     2538     * @ticket 50367
     2539     */
     2540    function test_wp_filter_content_tags_width_height() {
     2541        $image_meta = wp_get_attachment_metadata( self::$large_id );
     2542        $size_array = $this->_get_image_size_array_from_meta( $image_meta, 'medium' );
     2543
     2544        $img                 = get_image_tag( self::$large_id, '', '', '', 'medium' );
     2545        $img_no_width_height = str_replace( ' width="' . $size_array[0] . '"', '', $img );
     2546        $img_no_width_height = str_replace( ' height="' . $size_array[1] . '"', '', $img_no_width_height );
     2547        $img_no_width        = str_replace( ' width="' . $size_array[0] . '"', '', $img );
     2548        $img_no_height       = str_replace( ' height="' . $size_array[1] . '"', '', $img );
     2549
     2550        $hwstring = image_hwstring( $size_array[0], $size_array[1] );
     2551
     2552        // Manually add width and height to the markup from get_image_tag().
     2553        $respimg_no_width_height = str_replace( '<img ', '<img ' . $hwstring, $img_no_width_height );
     2554
     2555        $content = '
     2556            <p>Image, with width and height. Should NOT be modified.</p>
     2557            %1$s
     2558
     2559            <p>Image, no width and height attributes. Should have width, height, srcset and sizes (from matching the file name).</p>
     2560            %2$s
     2561
     2562            <p>Image, no width but height attribute. Should NOT be modified.</p>
     2563            %3$s
     2564
     2565            <p>Image, no height but width attribute. Should NOT be modified.</p>
     2566            %4$s';
     2567
     2568        $content_unfiltered = sprintf( $content, $img, $img_no_width_height, $img_no_width, $img_no_height );
     2569        $content_filtered   = sprintf( $content, $img, $respimg_no_width_height, $img_no_width, $img_no_height );
     2570
     2571        // Do not add loading, srcset, and sizes.
     2572        add_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
     2573        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     2574
     2575        $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) );
     2576
     2577        remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' );
     2578        remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
     2579    }
     2580
     2581    /**
    25352582     * @ticket 44427
    25362583     * @ticket 50367
    25372584     */
    2538     function test_wp_lazy_load_content_media() {
     2585    function test_wp_filter_content_tags_loading_lazy() {
    25392586        $image_meta = wp_get_attachment_metadata( self::$large_id );
    25402587        $size_array = $this->_get_image_size_array_from_meta( $image_meta, 'medium' );
     
    25712618        $content_filtered   = sprintf( $content, $lazy_img, $lazy_img_xhtml, $lazy_img_html5, $img_eager, $img_no_width_height, $iframe );
    25722619
    2573         // Do not add srcset and sizes.
     2620        // Do not add width, height, srcset, and sizes.
     2621        add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
    25742622        add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
    25752623
    25762624        $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) );
    25772625
     2626        remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' );
    25782627        remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' );
    25792628    }
     
    25822631     * @ticket 44427
    25832632     */
    2584     function test_wp_lazy_load_content_media_opted_in() {
     2633    function test_wp_filter_content_tags_loading_lazy_opted_in() {
    25852634        $img      = get_image_tag( self::$large_id, '', '', '', 'medium' );
    25862635        $lazy_img = wp_img_tag_add_loading_attr( $img, 'test' );
     
    26072656     * @ticket 44427
    26082657     */
    2609     function test_wp_lazy_load_content_media_opted_out() {
     2658    function test_wp_filter_content_tags_loading_lazy_opted_out() {
    26102659        $img = get_image_tag( self::$large_id, '', '', '', 'medium' );
    26112660
     
    26272676
    26282677    /**
    2629      * @ticket 44427
    2630      */
    2631     function test_wp_img_tag_add_loading_attr_single_quote() {
    2632         $img = "<img src='example.png' alt='' width='300' height='225' />";
     2678     * @ticket 50367
     2679     */
     2680    function test_wp_img_tag_add_loading_attr_without_src() {
     2681        $img = '<img alt=" width="300" height="225" />';
    26332682        $img = wp_img_tag_add_loading_attr( $img, 'test' );
    26342683
    2635         $this->assertContains( " loading='lazy'", $img );
     2684        $this->assertNotContains( ' loading="lazy"', $img );
    26362685    }
    26372686}
Note: See TracChangeset for help on using the changeset viewer.