get_header_images( $document ); break; case 'initial_content_image': $candidate = $this->get_initial_content_image( $document ); break; } if ( $candidate instanceof Element ) { $hero_image_elements[ spl_object_hash( $candidate ) ] = $candidate; } elseif ( is_array( $candidate ) ) { foreach ( $candidate as $hero_image_element ) { $hero_image_elements[ spl_object_hash( $hero_image_element ) ] = $hero_image_element; } } } $this->add_data_hero_candidate_attribute( array_values( $hero_image_elements ) ); } /** * Retrieve the images in the header. * * This returns all non-tiny images which occur before the main content, or else a tiny image that has `logo` in the * class name. * * Note that the `HeroCandidateFiltering` service will have already identified the Header Image and Custom Logo for * any theme using the standard `the_header_image_tag()` and `the_custom_logo()` template tags, respectively. This * remains for here as a fallback to identify Header Images and Custom Logos being rendered via non-standard * template tags. * * @see \AmpProject\AmpWP\Optimizer\HeroCandidateFiltering::add_custom_logo_data_hero_candidate_attribute() * @see \AmpProject\AmpWP\Optimizer\HeroCandidateFiltering::filter_header_image_tag() * * @param Document $document Document to retrieve the header images from. * @return Element[] Header images. */ private function get_header_images( Document $document ) { // Note that 3,508 out of 3,923 themes on WP.org (89%) use the
element. $after_header_element = $document->getElementsByTagName( 'main' )->item( 0 ); // If a theme happens to not use the
element, then fall back to using the first entry-content. if ( ! $after_header_element instanceof Element ) { $after_header_element = $this->get_first_entry_content( $document ); } if ( ! $after_header_element instanceof Element ) { return []; } $query = $document->xpath->query( self::PRECEDING_NON_LAZY_IMAGE_XPATH_QUERY, $after_header_element ); return array_filter( iterator_to_array( $query ), static function ( Element $element ) { // A custom logo may in fact be tiny and yet since it is in the header it should be prerendered. // Note that a theme may not be using `the_custom_logo()` template tag and that is why the `custom-logo` // class is not being checked for specifically. if ( $element->hasAttribute( Attribute::CLASS_ ) && false !== strpos( $element->getAttribute( Attribute::CLASS_ ), 'logo' ) ) { return true; } return ! ( new ImageDimensions( $element ) )->isTiny(); } ); } /** * Retrieve the first entry content. * * @param Document $document Document to retrieve the first entry content from. * @return Element|null First entry content element. */ private function get_first_entry_content( Document $document ) { $query = $document->xpath->query( self::FIRST_ENTRY_CONTENT_XPATH_QUERY, $document->body ); $entry_content = $query->item( 0 ); return $entry_content instanceof Element ? $entry_content : null; } /** * Retrieve the first image that is in the first position in content. * * @param Document $document Document to retrieve the image from. * @return Element|null Image at the beginning of the first entry content. */ private function get_initial_content_image( Document $document ) { $entry_content = $this->get_first_entry_content( $document ); if ( ! $entry_content instanceof Element ) { return null; } $query = $document->xpath->query( self::INITIAL_CONTENT_IMAGE_XPATH_QUERY, $entry_content ); $image = $query->item( 0 ); if ( $image instanceof Element && ! ( new ImageDimensions( $image ) )->isTiny() ) { return $image; } return null; } /** * Add the data-hero attribute to viable hero images. * * @param Element[] $hero_image_elements Elements that are viable hero images. */ private function add_data_hero_candidate_attribute( $hero_image_elements ) { foreach ( $hero_image_elements as $hero_image_element ) { $hero_image_element->setAttribute( Attribute::DATA_HERO_CANDIDATE, null ); } } }