From 74d69be6132164956fd385e7ac28da35075a9ac7 Mon Sep 17 00:00:00 2001 From: Paul Biron Date: Fri, 17 Jul 2026 15:51:13 -0600 Subject: [PATCH 1/6] Remove the XSLT xml-stylesheet processing instruction from all sitemaps and sitemap indices, and add the ability the render the HTML version of the sitemaps and sitemap indices server-side when a user uses the '.html' extension on any sitemap or sitemap index URL. --- src/wp-includes/sitemaps.php | 14 +- .../sitemaps/class-wp-sitemaps-index.php | 22 +- .../sitemaps/class-wp-sitemaps-provider.php | 33 ++- .../sitemaps/class-wp-sitemaps-renderer.php | 238 ++++++++++-------- .../sitemaps/class-wp-sitemaps-stylesheet.php | 139 +++++----- .../sitemaps/class-wp-sitemaps.php | 44 ++-- 6 files changed, 270 insertions(+), 220 deletions(-) diff --git a/src/wp-includes/sitemaps.php b/src/wp-includes/sitemaps.php index 656fc5ecaa975..8663cbf5068a5 100644 --- a/src/wp-includes/sitemaps.php +++ b/src/wp-includes/sitemaps.php @@ -87,28 +87,34 @@ function wp_sitemaps_get_max_urls( $object_type ) { * @param int $max_urls The maximum number of URLs included in a sitemap. Default 2000. * @param string $object_type Object type for sitemap to be filtered (e.g. 'post', 'term', 'user'). */ - return apply_filters( 'wp_sitemaps_max_urls', 2000, $object_type ); + return apply_filters( 'wp_sitemaps_max_urls', 100,/*2000,*/ $object_type ); } /** * Retrieves the full URL for a sitemap. * * @since 5.5.1 + * @since 7.1.0 Added $format parameter. * * @param string $name The sitemap name. * @param string $subtype_name The sitemap subtype name. Default empty string. * @param int $page The page of the sitemap. Default 1. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. Default 'xml'. * @return string|false The sitemap URL or false if the sitemap doesn't exist. */ -function get_sitemap_url( $name, $subtype_name = '', $page = 1 ) { +function get_sitemap_url( $name, $subtype_name = '', $page = 1, $format = 'xml' ) { $sitemaps = wp_sitemaps_get_server(); if ( ! $sitemaps ) { return false; } + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + if ( 'index' === $name ) { - return $sitemaps->index->get_index_url(); + return $sitemaps->index->get_index_url( $format ); } $provider = $sitemaps->registry->get_provider( $name ); @@ -125,5 +131,5 @@ function get_sitemap_url( $name, $subtype_name = '', $page = 1 ) { $page = 1; } - return $provider->get_sitemap_url( $subtype_name, $page ); + return $provider->get_sitemap_url( $subtype_name, $page, $format ); } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-index.php b/src/wp-includes/sitemaps/class-wp-sitemaps-index.php index 940358823f304..d28636658b2eb 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-index.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-index.php @@ -49,16 +49,22 @@ public function __construct( WP_Sitemaps_Registry $registry ) { * Gets a sitemap list for the index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return array[] Array of all sitemaps. */ - public function get_sitemap_list() { + public function get_sitemap_list( $format ) { $sitemaps = array(); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + $providers = $this->registry->get_providers(); /* @var WP_Sitemaps_Provider $provider */ foreach ( $providers as $name => $provider ) { - $sitemap_entries = $provider->get_sitemap_entries(); + $sitemap_entries = $provider->get_sitemap_entries( $format ); // Prevent issues with array_push and empty arrays on PHP < 7.3. if ( ! $sitemap_entries ) { @@ -79,18 +85,24 @@ public function get_sitemap_list() { * Builds the URL for the sitemap index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return string The sitemap index URL. */ - public function get_index_url() { + public function get_index_url( $format ) { global $wp_rewrite; + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + if ( ! $wp_rewrite->using_permalinks() ) { - return home_url( '/?sitemap=index' ); + return home_url( '/?sitemap=index&sitemap-format=' . $format ); } - return home_url( '/wp-sitemap.xml' ); + return home_url( "/wp-sitemap.{$format}" ); } } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php b/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php index 13a03f99a47a7..56a915b628d49 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php @@ -100,32 +100,40 @@ public function get_sitemap_type_data() { * The returned data is used to populate the sitemap entries of the index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return array[] Array of sitemap entries. */ - public function get_sitemap_entries() { + public function get_sitemap_entries( $format ) { $sitemaps = array(); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + $sitemap_types = $this->get_sitemap_type_data(); foreach ( $sitemap_types as $type ) { for ( $page = 1; $page <= $type['pages']; $page++ ) { $sitemap_entry = array( - 'loc' => $this->get_sitemap_url( $type['name'], $page ), + 'loc' => $this->get_sitemap_url( $type['name'], $page, $format ), ); /** * Filters the sitemap entry for the sitemap index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * * @param array $sitemap_entry Sitemap entry for the post. * @param string $object_type Object empty name. * @param string $object_subtype Object subtype name. * Empty string if the object type does not support subtypes. * @param int $page Page number of results. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. */ - $sitemap_entry = apply_filters( 'wp_sitemaps_index_entry', $sitemap_entry, $this->object_type, $type['name'], $page ); + $sitemap_entry = apply_filters( 'wp_sitemaps_index_entry', $sitemap_entry, $this->object_type, $type['name'], $page, $format ); $sitemaps[] = $sitemap_entry; } @@ -138,14 +146,16 @@ public function get_sitemap_entries() { * Gets the URL of a sitemap entry. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * - * @param string $name The name of the sitemap. - * @param int $page The page of the sitemap. + * @param string $name The name of the sitemap. + * @param int $page The page of the sitemap. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return string The composed URL for a sitemap entry. */ - public function get_sitemap_url( $name, $page ) { + public function get_sitemap_url( $name, $page, $format ) { global $wp_rewrite; // Accounts for cases where name is not included, ex: sitemaps-users-1.xml. @@ -157,12 +167,19 @@ public function get_sitemap_url( $name, $page ) { ) ); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + $basename = sprintf( - '/wp-sitemap-%1$s.xml', - implode( '-', $params ) + '/wp-sitemap-%1$s.%2$s', + implode( '-', $params ), + $format ); if ( ! $wp_rewrite->using_permalinks() ) { + $params['sitemap-format'] = $format; + $basename = '/?' . http_build_query( $params, '', '&' ); } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php b/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php index 10a1ef1e7fcd5..f4f76313b0aac 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php @@ -17,121 +17,45 @@ #[AllowDynamicProperties] class WP_Sitemaps_Renderer { /** - * XSL stylesheet for styling a sitemap for web browsers. - * - * @since 5.5.0 - * - * @var string - */ - protected $stylesheet = ''; - - /** - * XSL stylesheet for styling a sitemap for web browsers. + * Renders a sitemap index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * - * @var string - */ - protected $stylesheet_index = ''; - - /** - * WP_Sitemaps_Renderer constructor. - * - * @since 5.5.0 + * @param array $sitemaps Array of sitemap URLs. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. */ - public function __construct() { - $stylesheet_url = $this->get_sitemap_stylesheet_url(); + public function render_index( $sitemaps, $format ) { + $this->check_for_simple_xml_availability(); - if ( $stylesheet_url ) { - $this->stylesheet = ''; + $index_xml = $this->get_sitemap_index_xml( $sitemaps ); + if ( ! $index_xml ) { + return; } - $stylesheet_index_url = $this->get_sitemap_index_stylesheet_url(); - - if ( $stylesheet_index_url ) { - $this->stylesheet_index = ''; + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; } - } - /** - * Gets the URL for the sitemap stylesheet. - * - * @since 5.5.0 - * - * @global WP_Rewrite $wp_rewrite WordPress rewrite component. - * - * @return string The sitemap stylesheet URL. - */ - public function get_sitemap_stylesheet_url() { - global $wp_rewrite; + switch ( $format ) { + case 'html': + $this->check_for_xslt_availability(); - $sitemap_url = home_url( '/wp-sitemap.xsl' ); + $xslt = ( new WP_Sitemaps_Stylesheet() )->get_sitemap_index_stylesheet(); - if ( ! $wp_rewrite->using_permalinks() ) { - $sitemap_url = home_url( '/?sitemap-stylesheet=sitemap' ); - } + $html = $this->generate_html( $index_xml, $xslt ); - /** - * Filters the URL for the sitemap stylesheet. - * - * If a falsey value is returned, no stylesheet will be used and - * the "raw" XML of the sitemap will be displayed. - * - * @since 5.5.0 - * - * @param string $sitemap_url Full URL for the sitemaps XSL file. - */ - return apply_filters( 'wp_sitemaps_stylesheet_url', $sitemap_url ); - } + echo $html; - /** - * Gets the URL for the sitemap index stylesheet. - * - * @since 5.5.0 - * - * @global WP_Rewrite $wp_rewrite WordPress rewrite component. - * - * @return string The sitemap index stylesheet URL. - */ - public function get_sitemap_index_stylesheet_url() { - global $wp_rewrite; + break; + case 'xml': + default: + header( 'Content-Type: application/xml; charset=UTF-8' ); - $sitemap_url = home_url( '/wp-sitemap-index.xsl' ); + // All output is escaped within get_sitemap_xml(). + echo $index_xml; - if ( ! $wp_rewrite->using_permalinks() ) { - $sitemap_url = home_url( '/?sitemap-stylesheet=index' ); - } - - /** - * Filters the URL for the sitemap index stylesheet. - * - * If a falsey value is returned, no stylesheet will be used and - * the "raw" XML of the sitemap index will be displayed. - * - * @since 5.5.0 - * - * @param string $sitemap_url Full URL for the sitemaps index XSL file. - */ - return apply_filters( 'wp_sitemaps_stylesheet_index_url', $sitemap_url ); - } - - /** - * Renders a sitemap index. - * - * @since 5.5.0 - * - * @param array $sitemaps Array of sitemap URLs. - */ - public function render_index( $sitemaps ) { - header( 'Content-Type: application/xml; charset=UTF-8' ); - - $this->check_for_simple_xml_availability(); - - $index_xml = $this->get_sitemap_index_xml( $sitemaps ); - - if ( ! empty( $index_xml ) ) { - // All output is escaped within get_sitemap_index_xml(). - echo $index_xml; + break; } } @@ -146,9 +70,8 @@ public function render_index( $sitemaps ) { public function get_sitemap_index_xml( $sitemaps ) { $sitemap_index = new SimpleXMLElement( sprintf( - '%1$s%2$s%3$s', + '%1$s%2$s', '', - $this->stylesheet_index, '' ) ); @@ -183,19 +106,42 @@ public function get_sitemap_index_xml( $sitemaps ) { * Renders a sitemap. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * - * @param array $url_list Array of URLs for a sitemap. + * @param array $url_list Array of URLs for a sitemap. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. */ - public function render_sitemap( $url_list ) { - header( 'Content-Type: application/xml; charset=UTF-8' ); - + public function render_sitemap( $url_list, $format ) { $this->check_for_simple_xml_availability(); $sitemap_xml = $this->get_sitemap_xml( $url_list ); + if ( empty( $sitemap_xml ) ) { + return; + } + + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + + switch ( $format ) { + case 'html': + $this->check_for_xslt_availability(); + + $xslt = ( new WP_Sitemaps_Stylesheet() )->get_sitemap_stylesheet(); + + $html = $this->generate_html( $sitemap_xml, $xslt ); + + echo $html; - if ( ! empty( $sitemap_xml ) ) { - // All output is escaped within get_sitemap_xml(). - echo $sitemap_xml; + break; + case 'xml': + default: + header( 'Content-Type: application/xml; charset=UTF-8' ); + + // All output is escaped within get_sitemap_xml(). + echo $sitemap_xml; + + break; } } @@ -210,9 +156,8 @@ public function render_sitemap( $url_list ) { public function get_sitemap_xml( $url_list ) { $urlset = new SimpleXMLElement( sprintf( - '%1$s%2$s%3$s', + '%1$s%2$s', '', - $this->stylesheet, '' ) ); @@ -270,4 +215,75 @@ static function () { ); } } + + /** + * Checks for the availability of the DOMDocument & XSLTProcessor extension and errors if missing. + * + * @since 7.1.0 + */ + private function check_for_xslt_availability() { + if ( ! class_exists( 'DOMDocument' ) ) { + wp_die( + sprintf( + /* translators: %s: DOMDocument */ + esc_html( __( 'Could not generate XML sitemap due to missing %s extension' ) ), + 'DOMDocument' + ), + esc_html( __( 'WordPress › Error' ) ), + array( + 'response' => 501, // "Not implemented". + ) + ); + } + + if ( ! class_exists( 'XSLTProcessor' ) ) { + wp_die( + sprintf( + /* translators: %s: XSLTProcessor */ + esc_html( __( 'Could not generate XML sitemap due to missing %s extension' ) ), + 'XSLTProcessor' + ), + esc_html( __( 'WordPress › Error' ) ), + array( + 'response' => 501, // "Not implemented". + ) + ); + } + } + + /** + * Generate the HTML rendering of the sitemap or sitemap index using XSLT. + * + * @since 7.1.0 + * + * @param SimpleXMLElement $xml The XML of the sitemap or sitemap index. + * @param string $stylesheet The XSLT to apply to $xml. + * @return string + */ + protected function generate_html( $xml, $stylesheet ) { + // @todo add some DOM/XSLTProcessor related error checking. + + // Load the XML source document into a DOM. + $dom = new DOMDocument(); + $dom->loadXML( $xml ); + + // Load the XSLT into a DOM. + $xslt = new DOMDocument(); + $xslt->loadXML( $stylesheet ); + + // Create the XSLT processor and load the XSLT. + $proc = new XSLTProcessor(); + $proc->importStylesheet( $xslt ); + + // Run the XSLT on the source XML. + // Note: XSLTProcessor::transformToXML() is poorly named and need NOT produce XML; + // that is, it will produce whatever is specified by xsl:output/@method in the transform, + // which in our case is 'text/html'. + $html = $proc->transformToXML( $dom ); + if ( ! $html ) { + $html = ''; + } + + return $html; + } } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php b/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php index 25319b0a0b7d4..0d6468a7582dc 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php @@ -19,23 +19,12 @@ class WP_Sitemaps_Stylesheet { /** * Renders the XSL stylesheet depending on whether it's the sitemap index or not. * + * @deprecated 7.1.0 Deprecated because browsers will soon not support XSLT. + * * @param string $type Stylesheet type. Either 'sitemap' or 'index'. - * @return never */ public function render_stylesheet( $type ) { - header( 'Content-Type: application/xml; charset=UTF-8' ); - - if ( 'sitemap' === $type ) { - // All content is escaped below. - echo $this->get_sitemap_stylesheet(); - } - - if ( 'index' === $type ) { - // All content is escaped below. - echo $this->get_sitemap_index_stylesheet(); - } - - exit; + _deprecated_function( __FUNCTION__, '7.1.0' ); } /** @@ -85,6 +74,7 @@ public function get_sitemap_stylesheet() { + <!DOCTYPE html> {$title} @@ -93,48 +83,50 @@ public function get_sitemap_stylesheet() { -
-
-

{$title}

-

{$description}

-

{$learn_more}

-
-
-

{$text}

- - - - - - - - - - - - - - - - - +
+
+
+

{$title}

+

{$description}

+

{$learn_more}

+
+
+

{$text}

+
{$url}{$lastmod}{$changefreq}{$priority}
+ - + - + - + - + - - -
{$url} {$lastmod} {$changefreq} {$priority}
+ + + + + + + + + + + + + + + + + + +
- +
@@ -195,6 +187,7 @@ public function get_sitemap_index_stylesheet() { + <!DOCTYPE html> {$title} @@ -203,36 +196,38 @@ public function get_sitemap_index_stylesheet() { -
-
-

{$title}

-

{$description}

-

{$learn_more}

-
-
-

{$text}

- - - - - - - - - - - +
+
+
+

{$title}

+

{$description}

+

{$learn_more}

+
+
+

{$text}

+
{$url}{$lastmod}
+ - + - + - - -
{$url} {$lastmod}
+ + + + + + + + + + + + +
- +
diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps.php b/src/wp-includes/sitemaps/class-wp-sitemaps.php index d4bbe38c86b11..2d35685429c64 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps.php @@ -131,24 +131,31 @@ public function register_rewrites() { // Add rewrite tags. add_rewrite_tag( '%sitemap%', '([^?]+)' ); add_rewrite_tag( '%sitemap-subtype%', '([^?]+)' ); + add_rewrite_tag( '%sitemap-format%', '([^?]+)' ); // Register index route. - add_rewrite_rule( '^wp-sitemap\.xml$', 'index.php?sitemap=index', 'top' ); - - // Register rewrites for the XSL stylesheet. - add_rewrite_tag( '%sitemap-stylesheet%', '([^?]+)' ); - add_rewrite_rule( '^wp-sitemap\.xsl$', 'index.php?sitemap-stylesheet=sitemap', 'top' ); - add_rewrite_rule( '^wp-sitemap-index\.xsl$', 'index.php?sitemap-stylesheet=index', 'top' ); + add_rewrite_rule( '^wp-sitemap\.xml$', 'index.php?sitemap=index&sitemap-format=xml', 'top' ); + add_rewrite_rule( '^wp-sitemap\.html$', 'index.php?sitemap=index&sitemap-format=html', 'top' ); // Register routes for providers. add_rewrite_rule( '^wp-sitemap-([a-z]+?)-([a-z\d_-]+?)-(\d+?)\.xml$', - 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]', + 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]&sitemap-format=xml', + 'top' + ); + add_rewrite_rule( + '^wp-sitemap-([a-z]+?)-([a-z\d_-]+?)-(\d+?)\.html$', + 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]&sitemap-format=html', 'top' ); add_rewrite_rule( '^wp-sitemap-([a-z]+?)-(\d+?)\.xml$', - 'index.php?sitemap=$matches[1]&paged=$matches[2]', + 'index.php?sitemap=$matches[1]&paged=$matches[2]&sitemap-format=xml', + 'top' + ); + add_rewrite_rule( + '^wp-sitemap-([a-z]+?)-(\d+?)\.html$', + 'index.php?sitemap=$matches[1]&paged=$matches[2]&sitemap-format=html', 'top' ); } @@ -164,10 +171,15 @@ public function render_sitemaps() { global $wp_query; $sitemap = sanitize_text_field( get_query_var( 'sitemap' ) ); + $format = sanitize_text_field( get_query_var( 'sitemap-format' ) ); $object_subtype = sanitize_text_field( get_query_var( 'sitemap-subtype' ) ); - $stylesheet_type = sanitize_text_field( get_query_var( 'sitemap-stylesheet' ) ); $paged = absint( get_query_var( 'paged' ) ); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + // Force xml if the format is unknown. + $format = 'xml'; + } + // Bail early if this isn't a sitemap or stylesheet route. if ( ! ( $sitemap || $stylesheet_type ) ) { return; @@ -179,19 +191,11 @@ public function render_sitemaps() { return; } - // Render stylesheet if this is stylesheet route. - if ( $stylesheet_type ) { - $stylesheet = new WP_Sitemaps_Stylesheet(); - - $stylesheet->render_stylesheet( $stylesheet_type ); - exit; - } - // Render the index. if ( 'index' === $sitemap ) { - $sitemap_list = $this->index->get_sitemap_list(); + $sitemap_list = $this->index->get_sitemap_list( $format ); - $this->renderer->render_index( $sitemap_list ); + $this->renderer->render_index( $sitemap_list, $format ); exit; } @@ -214,7 +218,7 @@ public function render_sitemaps() { return; } - $this->renderer->render_sitemap( $url_list ); + $this->renderer->render_sitemap( $url_list, $format ); exit; } From 8a52d7afd66e46c2238680bf1c68aa5a55260526 Mon Sep 17 00:00:00 2001 From: Paul Biron Date: Fri, 17 Jul 2026 15:56:19 -0600 Subject: [PATCH 2/6] Revert "Remove the XSLT xml-stylesheet processing instruction from all sitemaps and sitemap indices, and add the ability the render the HTML version of the sitemaps and sitemap indices server-side when a user uses the '.html' extension on any sitemap or sitemap index URL." This reverts commit 74d69be6132164956fd385e7ac28da35075a9ac7. --- src/wp-includes/sitemaps.php | 14 +- .../sitemaps/class-wp-sitemaps-index.php | 22 +- .../sitemaps/class-wp-sitemaps-provider.php | 33 +-- .../sitemaps/class-wp-sitemaps-renderer.php | 238 ++++++++---------- .../sitemaps/class-wp-sitemaps-stylesheet.php | 139 +++++----- .../sitemaps/class-wp-sitemaps.php | 44 ++-- 6 files changed, 220 insertions(+), 270 deletions(-) diff --git a/src/wp-includes/sitemaps.php b/src/wp-includes/sitemaps.php index 8663cbf5068a5..656fc5ecaa975 100644 --- a/src/wp-includes/sitemaps.php +++ b/src/wp-includes/sitemaps.php @@ -87,34 +87,28 @@ function wp_sitemaps_get_max_urls( $object_type ) { * @param int $max_urls The maximum number of URLs included in a sitemap. Default 2000. * @param string $object_type Object type for sitemap to be filtered (e.g. 'post', 'term', 'user'). */ - return apply_filters( 'wp_sitemaps_max_urls', 100,/*2000,*/ $object_type ); + return apply_filters( 'wp_sitemaps_max_urls', 2000, $object_type ); } /** * Retrieves the full URL for a sitemap. * * @since 5.5.1 - * @since 7.1.0 Added $format parameter. * * @param string $name The sitemap name. * @param string $subtype_name The sitemap subtype name. Default empty string. * @param int $page The page of the sitemap. Default 1. - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. Default 'xml'. * @return string|false The sitemap URL or false if the sitemap doesn't exist. */ -function get_sitemap_url( $name, $subtype_name = '', $page = 1, $format = 'xml' ) { +function get_sitemap_url( $name, $subtype_name = '', $page = 1 ) { $sitemaps = wp_sitemaps_get_server(); if ( ! $sitemaps ) { return false; } - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - $format = 'xml'; - } - if ( 'index' === $name ) { - return $sitemaps->index->get_index_url( $format ); + return $sitemaps->index->get_index_url(); } $provider = $sitemaps->registry->get_provider( $name ); @@ -131,5 +125,5 @@ function get_sitemap_url( $name, $subtype_name = '', $page = 1, $format = 'xml' $page = 1; } - return $provider->get_sitemap_url( $subtype_name, $page, $format ); + return $provider->get_sitemap_url( $subtype_name, $page ); } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-index.php b/src/wp-includes/sitemaps/class-wp-sitemaps-index.php index d28636658b2eb..940358823f304 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-index.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-index.php @@ -49,22 +49,16 @@ public function __construct( WP_Sitemaps_Registry $registry ) { * Gets a sitemap list for the index. * * @since 5.5.0 - * @since 7.1.0 Added $format parameter. * - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return array[] Array of all sitemaps. */ - public function get_sitemap_list( $format ) { + public function get_sitemap_list() { $sitemaps = array(); - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - $format = 'xml'; - } - $providers = $this->registry->get_providers(); /* @var WP_Sitemaps_Provider $provider */ foreach ( $providers as $name => $provider ) { - $sitemap_entries = $provider->get_sitemap_entries( $format ); + $sitemap_entries = $provider->get_sitemap_entries(); // Prevent issues with array_push and empty arrays on PHP < 7.3. if ( ! $sitemap_entries ) { @@ -85,24 +79,18 @@ public function get_sitemap_list( $format ) { * Builds the URL for the sitemap index. * * @since 5.5.0 - * @since 7.1.0 Added $format parameter. * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return string The sitemap index URL. */ - public function get_index_url( $format ) { + public function get_index_url() { global $wp_rewrite; - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - $format = 'xml'; - } - if ( ! $wp_rewrite->using_permalinks() ) { - return home_url( '/?sitemap=index&sitemap-format=' . $format ); + return home_url( '/?sitemap=index' ); } - return home_url( "/wp-sitemap.{$format}" ); + return home_url( '/wp-sitemap.xml' ); } } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php b/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php index 56a915b628d49..13a03f99a47a7 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php @@ -100,40 +100,32 @@ public function get_sitemap_type_data() { * The returned data is used to populate the sitemap entries of the index. * * @since 5.5.0 - * @since 7.1.0 Added $format parameter. * - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return array[] Array of sitemap entries. */ - public function get_sitemap_entries( $format ) { + public function get_sitemap_entries() { $sitemaps = array(); - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - $format = 'xml'; - } - $sitemap_types = $this->get_sitemap_type_data(); foreach ( $sitemap_types as $type ) { for ( $page = 1; $page <= $type['pages']; $page++ ) { $sitemap_entry = array( - 'loc' => $this->get_sitemap_url( $type['name'], $page, $format ), + 'loc' => $this->get_sitemap_url( $type['name'], $page ), ); /** * Filters the sitemap entry for the sitemap index. * * @since 5.5.0 - * @since 7.1.0 Added $format parameter. * * @param array $sitemap_entry Sitemap entry for the post. * @param string $object_type Object empty name. * @param string $object_subtype Object subtype name. * Empty string if the object type does not support subtypes. * @param int $page Page number of results. - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. */ - $sitemap_entry = apply_filters( 'wp_sitemaps_index_entry', $sitemap_entry, $this->object_type, $type['name'], $page, $format ); + $sitemap_entry = apply_filters( 'wp_sitemaps_index_entry', $sitemap_entry, $this->object_type, $type['name'], $page ); $sitemaps[] = $sitemap_entry; } @@ -146,16 +138,14 @@ public function get_sitemap_entries( $format ) { * Gets the URL of a sitemap entry. * * @since 5.5.0 - * @since 7.1.0 Added $format parameter. * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * - * @param string $name The name of the sitemap. - * @param int $page The page of the sitemap. - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. + * @param string $name The name of the sitemap. + * @param int $page The page of the sitemap. * @return string The composed URL for a sitemap entry. */ - public function get_sitemap_url( $name, $page, $format ) { + public function get_sitemap_url( $name, $page ) { global $wp_rewrite; // Accounts for cases where name is not included, ex: sitemaps-users-1.xml. @@ -167,19 +157,12 @@ public function get_sitemap_url( $name, $page, $format ) { ) ); - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - $format = 'xml'; - } - $basename = sprintf( - '/wp-sitemap-%1$s.%2$s', - implode( '-', $params ), - $format + '/wp-sitemap-%1$s.xml', + implode( '-', $params ) ); if ( ! $wp_rewrite->using_permalinks() ) { - $params['sitemap-format'] = $format; - $basename = '/?' . http_build_query( $params, '', '&' ); } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php b/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php index f4f76313b0aac..10a1ef1e7fcd5 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php @@ -17,45 +17,121 @@ #[AllowDynamicProperties] class WP_Sitemaps_Renderer { /** - * Renders a sitemap index. + * XSL stylesheet for styling a sitemap for web browsers. * * @since 5.5.0 - * @since 7.1.0 Added $format parameter. * - * @param array $sitemaps Array of sitemap URLs. - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. + * @var string */ - public function render_index( $sitemaps, $format ) { - $this->check_for_simple_xml_availability(); + protected $stylesheet = ''; - $index_xml = $this->get_sitemap_index_xml( $sitemaps ); - if ( ! $index_xml ) { - return; + /** + * XSL stylesheet for styling a sitemap for web browsers. + * + * @since 5.5.0 + * + * @var string + */ + protected $stylesheet_index = ''; + + /** + * WP_Sitemaps_Renderer constructor. + * + * @since 5.5.0 + */ + public function __construct() { + $stylesheet_url = $this->get_sitemap_stylesheet_url(); + + if ( $stylesheet_url ) { + $this->stylesheet = ''; } - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - $format = 'xml'; + $stylesheet_index_url = $this->get_sitemap_index_stylesheet_url(); + + if ( $stylesheet_index_url ) { + $this->stylesheet_index = ''; } + } - switch ( $format ) { - case 'html': - $this->check_for_xslt_availability(); + /** + * Gets the URL for the sitemap stylesheet. + * + * @since 5.5.0 + * + * @global WP_Rewrite $wp_rewrite WordPress rewrite component. + * + * @return string The sitemap stylesheet URL. + */ + public function get_sitemap_stylesheet_url() { + global $wp_rewrite; - $xslt = ( new WP_Sitemaps_Stylesheet() )->get_sitemap_index_stylesheet(); + $sitemap_url = home_url( '/wp-sitemap.xsl' ); - $html = $this->generate_html( $index_xml, $xslt ); + if ( ! $wp_rewrite->using_permalinks() ) { + $sitemap_url = home_url( '/?sitemap-stylesheet=sitemap' ); + } - echo $html; + /** + * Filters the URL for the sitemap stylesheet. + * + * If a falsey value is returned, no stylesheet will be used and + * the "raw" XML of the sitemap will be displayed. + * + * @since 5.5.0 + * + * @param string $sitemap_url Full URL for the sitemaps XSL file. + */ + return apply_filters( 'wp_sitemaps_stylesheet_url', $sitemap_url ); + } - break; - case 'xml': - default: - header( 'Content-Type: application/xml; charset=UTF-8' ); + /** + * Gets the URL for the sitemap index stylesheet. + * + * @since 5.5.0 + * + * @global WP_Rewrite $wp_rewrite WordPress rewrite component. + * + * @return string The sitemap index stylesheet URL. + */ + public function get_sitemap_index_stylesheet_url() { + global $wp_rewrite; - // All output is escaped within get_sitemap_xml(). - echo $index_xml; + $sitemap_url = home_url( '/wp-sitemap-index.xsl' ); - break; + if ( ! $wp_rewrite->using_permalinks() ) { + $sitemap_url = home_url( '/?sitemap-stylesheet=index' ); + } + + /** + * Filters the URL for the sitemap index stylesheet. + * + * If a falsey value is returned, no stylesheet will be used and + * the "raw" XML of the sitemap index will be displayed. + * + * @since 5.5.0 + * + * @param string $sitemap_url Full URL for the sitemaps index XSL file. + */ + return apply_filters( 'wp_sitemaps_stylesheet_index_url', $sitemap_url ); + } + + /** + * Renders a sitemap index. + * + * @since 5.5.0 + * + * @param array $sitemaps Array of sitemap URLs. + */ + public function render_index( $sitemaps ) { + header( 'Content-Type: application/xml; charset=UTF-8' ); + + $this->check_for_simple_xml_availability(); + + $index_xml = $this->get_sitemap_index_xml( $sitemaps ); + + if ( ! empty( $index_xml ) ) { + // All output is escaped within get_sitemap_index_xml(). + echo $index_xml; } } @@ -70,8 +146,9 @@ public function render_index( $sitemaps, $format ) { public function get_sitemap_index_xml( $sitemaps ) { $sitemap_index = new SimpleXMLElement( sprintf( - '%1$s%2$s', + '%1$s%2$s%3$s', '', + $this->stylesheet_index, '' ) ); @@ -106,42 +183,19 @@ public function get_sitemap_index_xml( $sitemaps ) { * Renders a sitemap. * * @since 5.5.0 - * @since 7.1.0 Added $format parameter. * - * @param array $url_list Array of URLs for a sitemap. - * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. + * @param array $url_list Array of URLs for a sitemap. */ - public function render_sitemap( $url_list, $format ) { + public function render_sitemap( $url_list ) { + header( 'Content-Type: application/xml; charset=UTF-8' ); + $this->check_for_simple_xml_availability(); $sitemap_xml = $this->get_sitemap_xml( $url_list ); - if ( empty( $sitemap_xml ) ) { - return; - } - - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - $format = 'xml'; - } - - switch ( $format ) { - case 'html': - $this->check_for_xslt_availability(); - - $xslt = ( new WP_Sitemaps_Stylesheet() )->get_sitemap_stylesheet(); - - $html = $this->generate_html( $sitemap_xml, $xslt ); - - echo $html; - break; - case 'xml': - default: - header( 'Content-Type: application/xml; charset=UTF-8' ); - - // All output is escaped within get_sitemap_xml(). - echo $sitemap_xml; - - break; + if ( ! empty( $sitemap_xml ) ) { + // All output is escaped within get_sitemap_xml(). + echo $sitemap_xml; } } @@ -156,8 +210,9 @@ public function render_sitemap( $url_list, $format ) { public function get_sitemap_xml( $url_list ) { $urlset = new SimpleXMLElement( sprintf( - '%1$s%2$s', + '%1$s%2$s%3$s', '', + $this->stylesheet, '' ) ); @@ -215,75 +270,4 @@ static function () { ); } } - - /** - * Checks for the availability of the DOMDocument & XSLTProcessor extension and errors if missing. - * - * @since 7.1.0 - */ - private function check_for_xslt_availability() { - if ( ! class_exists( 'DOMDocument' ) ) { - wp_die( - sprintf( - /* translators: %s: DOMDocument */ - esc_html( __( 'Could not generate XML sitemap due to missing %s extension' ) ), - 'DOMDocument' - ), - esc_html( __( 'WordPress › Error' ) ), - array( - 'response' => 501, // "Not implemented". - ) - ); - } - - if ( ! class_exists( 'XSLTProcessor' ) ) { - wp_die( - sprintf( - /* translators: %s: XSLTProcessor */ - esc_html( __( 'Could not generate XML sitemap due to missing %s extension' ) ), - 'XSLTProcessor' - ), - esc_html( __( 'WordPress › Error' ) ), - array( - 'response' => 501, // "Not implemented". - ) - ); - } - } - - /** - * Generate the HTML rendering of the sitemap or sitemap index using XSLT. - * - * @since 7.1.0 - * - * @param SimpleXMLElement $xml The XML of the sitemap or sitemap index. - * @param string $stylesheet The XSLT to apply to $xml. - * @return string - */ - protected function generate_html( $xml, $stylesheet ) { - // @todo add some DOM/XSLTProcessor related error checking. - - // Load the XML source document into a DOM. - $dom = new DOMDocument(); - $dom->loadXML( $xml ); - - // Load the XSLT into a DOM. - $xslt = new DOMDocument(); - $xslt->loadXML( $stylesheet ); - - // Create the XSLT processor and load the XSLT. - $proc = new XSLTProcessor(); - $proc->importStylesheet( $xslt ); - - // Run the XSLT on the source XML. - // Note: XSLTProcessor::transformToXML() is poorly named and need NOT produce XML; - // that is, it will produce whatever is specified by xsl:output/@method in the transform, - // which in our case is 'text/html'. - $html = $proc->transformToXML( $dom ); - if ( ! $html ) { - $html = ''; - } - - return $html; - } } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php b/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php index 0d6468a7582dc..25319b0a0b7d4 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php @@ -19,12 +19,23 @@ class WP_Sitemaps_Stylesheet { /** * Renders the XSL stylesheet depending on whether it's the sitemap index or not. * - * @deprecated 7.1.0 Deprecated because browsers will soon not support XSLT. - * * @param string $type Stylesheet type. Either 'sitemap' or 'index'. + * @return never */ public function render_stylesheet( $type ) { - _deprecated_function( __FUNCTION__, '7.1.0' ); + header( 'Content-Type: application/xml; charset=UTF-8' ); + + if ( 'sitemap' === $type ) { + // All content is escaped below. + echo $this->get_sitemap_stylesheet(); + } + + if ( 'index' === $type ) { + // All content is escaped below. + echo $this->get_sitemap_index_stylesheet(); + } + + exit; } /** @@ -74,7 +85,6 @@ public function get_sitemap_stylesheet() { - <!DOCTYPE html> {$title} @@ -83,50 +93,48 @@ public function get_sitemap_stylesheet() { -
-
-
-

{$title}

-

{$description}

-

{$learn_more}

-
-
-

{$text}

- - +
+
+

{$title}

+

{$description}

+

{$learn_more}

+
+
+

{$text}

+
+ + + + + + + + + + + + + + + + - + - + - + - + - - - - - - - - - - - - - - - - - -
{$url}{$lastmod}{$changefreq}{$priority}
{$url} {$lastmod} {$changefreq} {$priority}
-
+ + +
-
+
@@ -187,7 +195,6 @@ public function get_sitemap_index_stylesheet() { - <!DOCTYPE html> {$title} @@ -196,38 +203,36 @@ public function get_sitemap_index_stylesheet() { -
-
-
-

{$title}

-

{$description}

-

{$learn_more}

-
-
-

{$text}

- - +
+
+

{$title}

+

{$description}

+

{$learn_more}

+
+
+

{$text}

+
+ + + + + + + + + + - + - + - - - - - - - - - - - -
{$url}{$lastmod}
{$url} {$lastmod}
-
+ + +
-
+
diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps.php b/src/wp-includes/sitemaps/class-wp-sitemaps.php index 2d35685429c64..d4bbe38c86b11 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps.php @@ -131,31 +131,24 @@ public function register_rewrites() { // Add rewrite tags. add_rewrite_tag( '%sitemap%', '([^?]+)' ); add_rewrite_tag( '%sitemap-subtype%', '([^?]+)' ); - add_rewrite_tag( '%sitemap-format%', '([^?]+)' ); // Register index route. - add_rewrite_rule( '^wp-sitemap\.xml$', 'index.php?sitemap=index&sitemap-format=xml', 'top' ); - add_rewrite_rule( '^wp-sitemap\.html$', 'index.php?sitemap=index&sitemap-format=html', 'top' ); + add_rewrite_rule( '^wp-sitemap\.xml$', 'index.php?sitemap=index', 'top' ); + + // Register rewrites for the XSL stylesheet. + add_rewrite_tag( '%sitemap-stylesheet%', '([^?]+)' ); + add_rewrite_rule( '^wp-sitemap\.xsl$', 'index.php?sitemap-stylesheet=sitemap', 'top' ); + add_rewrite_rule( '^wp-sitemap-index\.xsl$', 'index.php?sitemap-stylesheet=index', 'top' ); // Register routes for providers. add_rewrite_rule( '^wp-sitemap-([a-z]+?)-([a-z\d_-]+?)-(\d+?)\.xml$', - 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]&sitemap-format=xml', - 'top' - ); - add_rewrite_rule( - '^wp-sitemap-([a-z]+?)-([a-z\d_-]+?)-(\d+?)\.html$', - 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]&sitemap-format=html', + 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]', 'top' ); add_rewrite_rule( '^wp-sitemap-([a-z]+?)-(\d+?)\.xml$', - 'index.php?sitemap=$matches[1]&paged=$matches[2]&sitemap-format=xml', - 'top' - ); - add_rewrite_rule( - '^wp-sitemap-([a-z]+?)-(\d+?)\.html$', - 'index.php?sitemap=$matches[1]&paged=$matches[2]&sitemap-format=html', + 'index.php?sitemap=$matches[1]&paged=$matches[2]', 'top' ); } @@ -171,15 +164,10 @@ public function render_sitemaps() { global $wp_query; $sitemap = sanitize_text_field( get_query_var( 'sitemap' ) ); - $format = sanitize_text_field( get_query_var( 'sitemap-format' ) ); $object_subtype = sanitize_text_field( get_query_var( 'sitemap-subtype' ) ); + $stylesheet_type = sanitize_text_field( get_query_var( 'sitemap-stylesheet' ) ); $paged = absint( get_query_var( 'paged' ) ); - if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { - // Force xml if the format is unknown. - $format = 'xml'; - } - // Bail early if this isn't a sitemap or stylesheet route. if ( ! ( $sitemap || $stylesheet_type ) ) { return; @@ -191,11 +179,19 @@ public function render_sitemaps() { return; } + // Render stylesheet if this is stylesheet route. + if ( $stylesheet_type ) { + $stylesheet = new WP_Sitemaps_Stylesheet(); + + $stylesheet->render_stylesheet( $stylesheet_type ); + exit; + } + // Render the index. if ( 'index' === $sitemap ) { - $sitemap_list = $this->index->get_sitemap_list( $format ); + $sitemap_list = $this->index->get_sitemap_list(); - $this->renderer->render_index( $sitemap_list, $format ); + $this->renderer->render_index( $sitemap_list ); exit; } @@ -218,7 +214,7 @@ public function render_sitemaps() { return; } - $this->renderer->render_sitemap( $url_list, $format ); + $this->renderer->render_sitemap( $url_list ); exit; } From f4519b2fb4e990db42cff7153dcfaa754ec0e3a2 Mon Sep 17 00:00:00 2001 From: Paul Biron Date: Fri, 17 Jul 2026 15:59:14 -0600 Subject: [PATCH 3/6] Remove the XSLT xml-stylesheet processing instruction from all sitemaps and sitemap indices, and add the ability the render the HTML version of the sitemaps and sitemap indices server-side when a user uses the '.html' extension on any sitemap or sitemap index URL. --- src/wp-includes/sitemaps.php | 12 +- .../sitemaps/class-wp-sitemaps-index.php | 22 +- .../sitemaps/class-wp-sitemaps-provider.php | 33 ++- .../sitemaps/class-wp-sitemaps-renderer.php | 238 ++++++++++-------- .../sitemaps/class-wp-sitemaps-stylesheet.php | 139 +++++----- .../sitemaps/class-wp-sitemaps.php | 44 ++-- 6 files changed, 269 insertions(+), 219 deletions(-) diff --git a/src/wp-includes/sitemaps.php b/src/wp-includes/sitemaps.php index 656fc5ecaa975..e549b03b7da69 100644 --- a/src/wp-includes/sitemaps.php +++ b/src/wp-includes/sitemaps.php @@ -94,21 +94,27 @@ function wp_sitemaps_get_max_urls( $object_type ) { * Retrieves the full URL for a sitemap. * * @since 5.5.1 + * @since 7.1.0 Added $format parameter. * * @param string $name The sitemap name. * @param string $subtype_name The sitemap subtype name. Default empty string. * @param int $page The page of the sitemap. Default 1. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. Default 'xml'. * @return string|false The sitemap URL or false if the sitemap doesn't exist. */ -function get_sitemap_url( $name, $subtype_name = '', $page = 1 ) { +function get_sitemap_url( $name, $subtype_name = '', $page = 1, $format = 'xml' ) { $sitemaps = wp_sitemaps_get_server(); if ( ! $sitemaps ) { return false; } + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + if ( 'index' === $name ) { - return $sitemaps->index->get_index_url(); + return $sitemaps->index->get_index_url( $format ); } $provider = $sitemaps->registry->get_provider( $name ); @@ -125,5 +131,5 @@ function get_sitemap_url( $name, $subtype_name = '', $page = 1 ) { $page = 1; } - return $provider->get_sitemap_url( $subtype_name, $page ); + return $provider->get_sitemap_url( $subtype_name, $page, $format ); } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-index.php b/src/wp-includes/sitemaps/class-wp-sitemaps-index.php index 940358823f304..d28636658b2eb 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-index.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-index.php @@ -49,16 +49,22 @@ public function __construct( WP_Sitemaps_Registry $registry ) { * Gets a sitemap list for the index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return array[] Array of all sitemaps. */ - public function get_sitemap_list() { + public function get_sitemap_list( $format ) { $sitemaps = array(); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + $providers = $this->registry->get_providers(); /* @var WP_Sitemaps_Provider $provider */ foreach ( $providers as $name => $provider ) { - $sitemap_entries = $provider->get_sitemap_entries(); + $sitemap_entries = $provider->get_sitemap_entries( $format ); // Prevent issues with array_push and empty arrays on PHP < 7.3. if ( ! $sitemap_entries ) { @@ -79,18 +85,24 @@ public function get_sitemap_list() { * Builds the URL for the sitemap index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return string The sitemap index URL. */ - public function get_index_url() { + public function get_index_url( $format ) { global $wp_rewrite; + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + if ( ! $wp_rewrite->using_permalinks() ) { - return home_url( '/?sitemap=index' ); + return home_url( '/?sitemap=index&sitemap-format=' . $format ); } - return home_url( '/wp-sitemap.xml' ); + return home_url( "/wp-sitemap.{$format}" ); } } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php b/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php index 13a03f99a47a7..56a915b628d49 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-provider.php @@ -100,32 +100,40 @@ public function get_sitemap_type_data() { * The returned data is used to populate the sitemap entries of the index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return array[] Array of sitemap entries. */ - public function get_sitemap_entries() { + public function get_sitemap_entries( $format ) { $sitemaps = array(); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + $sitemap_types = $this->get_sitemap_type_data(); foreach ( $sitemap_types as $type ) { for ( $page = 1; $page <= $type['pages']; $page++ ) { $sitemap_entry = array( - 'loc' => $this->get_sitemap_url( $type['name'], $page ), + 'loc' => $this->get_sitemap_url( $type['name'], $page, $format ), ); /** * Filters the sitemap entry for the sitemap index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * * @param array $sitemap_entry Sitemap entry for the post. * @param string $object_type Object empty name. * @param string $object_subtype Object subtype name. * Empty string if the object type does not support subtypes. * @param int $page Page number of results. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. */ - $sitemap_entry = apply_filters( 'wp_sitemaps_index_entry', $sitemap_entry, $this->object_type, $type['name'], $page ); + $sitemap_entry = apply_filters( 'wp_sitemaps_index_entry', $sitemap_entry, $this->object_type, $type['name'], $page, $format ); $sitemaps[] = $sitemap_entry; } @@ -138,14 +146,16 @@ public function get_sitemap_entries() { * Gets the URL of a sitemap entry. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * - * @param string $name The name of the sitemap. - * @param int $page The page of the sitemap. + * @param string $name The name of the sitemap. + * @param int $page The page of the sitemap. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. * @return string The composed URL for a sitemap entry. */ - public function get_sitemap_url( $name, $page ) { + public function get_sitemap_url( $name, $page, $format ) { global $wp_rewrite; // Accounts for cases where name is not included, ex: sitemaps-users-1.xml. @@ -157,12 +167,19 @@ public function get_sitemap_url( $name, $page ) { ) ); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + $basename = sprintf( - '/wp-sitemap-%1$s.xml', - implode( '-', $params ) + '/wp-sitemap-%1$s.%2$s', + implode( '-', $params ), + $format ); if ( ! $wp_rewrite->using_permalinks() ) { + $params['sitemap-format'] = $format; + $basename = '/?' . http_build_query( $params, '', '&' ); } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php b/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php index 10a1ef1e7fcd5..f4f76313b0aac 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-renderer.php @@ -17,121 +17,45 @@ #[AllowDynamicProperties] class WP_Sitemaps_Renderer { /** - * XSL stylesheet for styling a sitemap for web browsers. - * - * @since 5.5.0 - * - * @var string - */ - protected $stylesheet = ''; - - /** - * XSL stylesheet for styling a sitemap for web browsers. + * Renders a sitemap index. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * - * @var string - */ - protected $stylesheet_index = ''; - - /** - * WP_Sitemaps_Renderer constructor. - * - * @since 5.5.0 + * @param array $sitemaps Array of sitemap URLs. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. */ - public function __construct() { - $stylesheet_url = $this->get_sitemap_stylesheet_url(); + public function render_index( $sitemaps, $format ) { + $this->check_for_simple_xml_availability(); - if ( $stylesheet_url ) { - $this->stylesheet = ''; + $index_xml = $this->get_sitemap_index_xml( $sitemaps ); + if ( ! $index_xml ) { + return; } - $stylesheet_index_url = $this->get_sitemap_index_stylesheet_url(); - - if ( $stylesheet_index_url ) { - $this->stylesheet_index = ''; + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; } - } - /** - * Gets the URL for the sitemap stylesheet. - * - * @since 5.5.0 - * - * @global WP_Rewrite $wp_rewrite WordPress rewrite component. - * - * @return string The sitemap stylesheet URL. - */ - public function get_sitemap_stylesheet_url() { - global $wp_rewrite; + switch ( $format ) { + case 'html': + $this->check_for_xslt_availability(); - $sitemap_url = home_url( '/wp-sitemap.xsl' ); + $xslt = ( new WP_Sitemaps_Stylesheet() )->get_sitemap_index_stylesheet(); - if ( ! $wp_rewrite->using_permalinks() ) { - $sitemap_url = home_url( '/?sitemap-stylesheet=sitemap' ); - } + $html = $this->generate_html( $index_xml, $xslt ); - /** - * Filters the URL for the sitemap stylesheet. - * - * If a falsey value is returned, no stylesheet will be used and - * the "raw" XML of the sitemap will be displayed. - * - * @since 5.5.0 - * - * @param string $sitemap_url Full URL for the sitemaps XSL file. - */ - return apply_filters( 'wp_sitemaps_stylesheet_url', $sitemap_url ); - } + echo $html; - /** - * Gets the URL for the sitemap index stylesheet. - * - * @since 5.5.0 - * - * @global WP_Rewrite $wp_rewrite WordPress rewrite component. - * - * @return string The sitemap index stylesheet URL. - */ - public function get_sitemap_index_stylesheet_url() { - global $wp_rewrite; + break; + case 'xml': + default: + header( 'Content-Type: application/xml; charset=UTF-8' ); - $sitemap_url = home_url( '/wp-sitemap-index.xsl' ); + // All output is escaped within get_sitemap_xml(). + echo $index_xml; - if ( ! $wp_rewrite->using_permalinks() ) { - $sitemap_url = home_url( '/?sitemap-stylesheet=index' ); - } - - /** - * Filters the URL for the sitemap index stylesheet. - * - * If a falsey value is returned, no stylesheet will be used and - * the "raw" XML of the sitemap index will be displayed. - * - * @since 5.5.0 - * - * @param string $sitemap_url Full URL for the sitemaps index XSL file. - */ - return apply_filters( 'wp_sitemaps_stylesheet_index_url', $sitemap_url ); - } - - /** - * Renders a sitemap index. - * - * @since 5.5.0 - * - * @param array $sitemaps Array of sitemap URLs. - */ - public function render_index( $sitemaps ) { - header( 'Content-Type: application/xml; charset=UTF-8' ); - - $this->check_for_simple_xml_availability(); - - $index_xml = $this->get_sitemap_index_xml( $sitemaps ); - - if ( ! empty( $index_xml ) ) { - // All output is escaped within get_sitemap_index_xml(). - echo $index_xml; + break; } } @@ -146,9 +70,8 @@ public function render_index( $sitemaps ) { public function get_sitemap_index_xml( $sitemaps ) { $sitemap_index = new SimpleXMLElement( sprintf( - '%1$s%2$s%3$s', + '%1$s%2$s', '', - $this->stylesheet_index, '' ) ); @@ -183,19 +106,42 @@ public function get_sitemap_index_xml( $sitemaps ) { * Renders a sitemap. * * @since 5.5.0 + * @since 7.1.0 Added $format parameter. * - * @param array $url_list Array of URLs for a sitemap. + * @param array $url_list Array of URLs for a sitemap. + * @param string $format The format for the sitemap index. Accepts 'xml', 'html'. */ - public function render_sitemap( $url_list ) { - header( 'Content-Type: application/xml; charset=UTF-8' ); - + public function render_sitemap( $url_list, $format ) { $this->check_for_simple_xml_availability(); $sitemap_xml = $this->get_sitemap_xml( $url_list ); + if ( empty( $sitemap_xml ) ) { + return; + } + + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + $format = 'xml'; + } + + switch ( $format ) { + case 'html': + $this->check_for_xslt_availability(); + + $xslt = ( new WP_Sitemaps_Stylesheet() )->get_sitemap_stylesheet(); + + $html = $this->generate_html( $sitemap_xml, $xslt ); + + echo $html; - if ( ! empty( $sitemap_xml ) ) { - // All output is escaped within get_sitemap_xml(). - echo $sitemap_xml; + break; + case 'xml': + default: + header( 'Content-Type: application/xml; charset=UTF-8' ); + + // All output is escaped within get_sitemap_xml(). + echo $sitemap_xml; + + break; } } @@ -210,9 +156,8 @@ public function render_sitemap( $url_list ) { public function get_sitemap_xml( $url_list ) { $urlset = new SimpleXMLElement( sprintf( - '%1$s%2$s%3$s', + '%1$s%2$s', '', - $this->stylesheet, '' ) ); @@ -270,4 +215,75 @@ static function () { ); } } + + /** + * Checks for the availability of the DOMDocument & XSLTProcessor extension and errors if missing. + * + * @since 7.1.0 + */ + private function check_for_xslt_availability() { + if ( ! class_exists( 'DOMDocument' ) ) { + wp_die( + sprintf( + /* translators: %s: DOMDocument */ + esc_html( __( 'Could not generate XML sitemap due to missing %s extension' ) ), + 'DOMDocument' + ), + esc_html( __( 'WordPress › Error' ) ), + array( + 'response' => 501, // "Not implemented". + ) + ); + } + + if ( ! class_exists( 'XSLTProcessor' ) ) { + wp_die( + sprintf( + /* translators: %s: XSLTProcessor */ + esc_html( __( 'Could not generate XML sitemap due to missing %s extension' ) ), + 'XSLTProcessor' + ), + esc_html( __( 'WordPress › Error' ) ), + array( + 'response' => 501, // "Not implemented". + ) + ); + } + } + + /** + * Generate the HTML rendering of the sitemap or sitemap index using XSLT. + * + * @since 7.1.0 + * + * @param SimpleXMLElement $xml The XML of the sitemap or sitemap index. + * @param string $stylesheet The XSLT to apply to $xml. + * @return string + */ + protected function generate_html( $xml, $stylesheet ) { + // @todo add some DOM/XSLTProcessor related error checking. + + // Load the XML source document into a DOM. + $dom = new DOMDocument(); + $dom->loadXML( $xml ); + + // Load the XSLT into a DOM. + $xslt = new DOMDocument(); + $xslt->loadXML( $stylesheet ); + + // Create the XSLT processor and load the XSLT. + $proc = new XSLTProcessor(); + $proc->importStylesheet( $xslt ); + + // Run the XSLT on the source XML. + // Note: XSLTProcessor::transformToXML() is poorly named and need NOT produce XML; + // that is, it will produce whatever is specified by xsl:output/@method in the transform, + // which in our case is 'text/html'. + $html = $proc->transformToXML( $dom ); + if ( ! $html ) { + $html = ''; + } + + return $html; + } } diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php b/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php index 25319b0a0b7d4..0d6468a7582dc 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php @@ -19,23 +19,12 @@ class WP_Sitemaps_Stylesheet { /** * Renders the XSL stylesheet depending on whether it's the sitemap index or not. * + * @deprecated 7.1.0 Deprecated because browsers will soon not support XSLT. + * * @param string $type Stylesheet type. Either 'sitemap' or 'index'. - * @return never */ public function render_stylesheet( $type ) { - header( 'Content-Type: application/xml; charset=UTF-8' ); - - if ( 'sitemap' === $type ) { - // All content is escaped below. - echo $this->get_sitemap_stylesheet(); - } - - if ( 'index' === $type ) { - // All content is escaped below. - echo $this->get_sitemap_index_stylesheet(); - } - - exit; + _deprecated_function( __FUNCTION__, '7.1.0' ); } /** @@ -85,6 +74,7 @@ public function get_sitemap_stylesheet() { + <!DOCTYPE html> {$title} @@ -93,48 +83,50 @@ public function get_sitemap_stylesheet() { -
-
-

{$title}

-

{$description}

-

{$learn_more}

-
-
-

{$text}

- - - - - - - - - - - - - - - - - +
+
+
+

{$title}

+

{$description}

+

{$learn_more}

+
+
+

{$text}

+
{$url}{$lastmod}{$changefreq}{$priority}
+ - + - + - + - + - - -
{$url} {$lastmod} {$changefreq} {$priority}
+ + + + + + + + + + + + + + + + + + +
- +
@@ -195,6 +187,7 @@ public function get_sitemap_index_stylesheet() { + <!DOCTYPE html> {$title} @@ -203,36 +196,38 @@ public function get_sitemap_index_stylesheet() { -
-
-

{$title}

-

{$description}

-

{$learn_more}

-
-
-

{$text}

- - - - - - - - - - - +
+
+
+

{$title}

+

{$description}

+

{$learn_more}

+
+
+

{$text}

+
{$url}{$lastmod}
+ - + - + - - -
{$url} {$lastmod}
+ + + + + + + + + + + + +
- +
diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps.php b/src/wp-includes/sitemaps/class-wp-sitemaps.php index d4bbe38c86b11..2d35685429c64 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps.php @@ -131,24 +131,31 @@ public function register_rewrites() { // Add rewrite tags. add_rewrite_tag( '%sitemap%', '([^?]+)' ); add_rewrite_tag( '%sitemap-subtype%', '([^?]+)' ); + add_rewrite_tag( '%sitemap-format%', '([^?]+)' ); // Register index route. - add_rewrite_rule( '^wp-sitemap\.xml$', 'index.php?sitemap=index', 'top' ); - - // Register rewrites for the XSL stylesheet. - add_rewrite_tag( '%sitemap-stylesheet%', '([^?]+)' ); - add_rewrite_rule( '^wp-sitemap\.xsl$', 'index.php?sitemap-stylesheet=sitemap', 'top' ); - add_rewrite_rule( '^wp-sitemap-index\.xsl$', 'index.php?sitemap-stylesheet=index', 'top' ); + add_rewrite_rule( '^wp-sitemap\.xml$', 'index.php?sitemap=index&sitemap-format=xml', 'top' ); + add_rewrite_rule( '^wp-sitemap\.html$', 'index.php?sitemap=index&sitemap-format=html', 'top' ); // Register routes for providers. add_rewrite_rule( '^wp-sitemap-([a-z]+?)-([a-z\d_-]+?)-(\d+?)\.xml$', - 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]', + 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]&sitemap-format=xml', + 'top' + ); + add_rewrite_rule( + '^wp-sitemap-([a-z]+?)-([a-z\d_-]+?)-(\d+?)\.html$', + 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]&sitemap-format=html', 'top' ); add_rewrite_rule( '^wp-sitemap-([a-z]+?)-(\d+?)\.xml$', - 'index.php?sitemap=$matches[1]&paged=$matches[2]', + 'index.php?sitemap=$matches[1]&paged=$matches[2]&sitemap-format=xml', + 'top' + ); + add_rewrite_rule( + '^wp-sitemap-([a-z]+?)-(\d+?)\.html$', + 'index.php?sitemap=$matches[1]&paged=$matches[2]&sitemap-format=html', 'top' ); } @@ -164,10 +171,15 @@ public function render_sitemaps() { global $wp_query; $sitemap = sanitize_text_field( get_query_var( 'sitemap' ) ); + $format = sanitize_text_field( get_query_var( 'sitemap-format' ) ); $object_subtype = sanitize_text_field( get_query_var( 'sitemap-subtype' ) ); - $stylesheet_type = sanitize_text_field( get_query_var( 'sitemap-stylesheet' ) ); $paged = absint( get_query_var( 'paged' ) ); + if ( ! in_array( $format, array( 'xml', 'html' ), true ) ) { + // Force xml if the format is unknown. + $format = 'xml'; + } + // Bail early if this isn't a sitemap or stylesheet route. if ( ! ( $sitemap || $stylesheet_type ) ) { return; @@ -179,19 +191,11 @@ public function render_sitemaps() { return; } - // Render stylesheet if this is stylesheet route. - if ( $stylesheet_type ) { - $stylesheet = new WP_Sitemaps_Stylesheet(); - - $stylesheet->render_stylesheet( $stylesheet_type ); - exit; - } - // Render the index. if ( 'index' === $sitemap ) { - $sitemap_list = $this->index->get_sitemap_list(); + $sitemap_list = $this->index->get_sitemap_list( $format ); - $this->renderer->render_index( $sitemap_list ); + $this->renderer->render_index( $sitemap_list, $format ); exit; } @@ -214,7 +218,7 @@ public function render_sitemaps() { return; } - $this->renderer->render_sitemap( $url_list ); + $this->renderer->render_sitemap( $url_list, $format ); exit; } From 90a756e40158b72bafbe7d3f51e5631d1f862f68 Mon Sep 17 00:00:00 2001 From: Paul Biron Date: Fri, 17 Jul 2026 17:59:25 -0600 Subject: [PATCH 4/6] Ensure the site URL in robots.txt contains the sitemap-format=xml param if pretty permalinks are being used. --- src/wp-includes/sitemaps/class-wp-sitemaps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/sitemaps/class-wp-sitemaps.php b/src/wp-includes/sitemaps/class-wp-sitemaps.php index 2d35685429c64..e9bbe6c188b9f 100644 --- a/src/wp-includes/sitemaps/class-wp-sitemaps.php +++ b/src/wp-includes/sitemaps/class-wp-sitemaps.php @@ -262,7 +262,7 @@ public function redirect_sitemapxml( $bypass, $query ) { */ public function add_robots( $output, $is_public ) { if ( $is_public ) { - $output .= "\nSitemap: " . esc_url( $this->index->get_index_url() ) . "\n"; + $output .= "\nSitemap: " . esc_url( $this->index->get_index_url( 'xml' ) ) . "\n"; } return $output; From a800f519e05ed6b3a13e798c07f31f729e15d4a4 Mon Sep 17 00:00:00 2001 From: Paul Biron Date: Fri, 17 Jul 2026 18:00:06 -0600 Subject: [PATCH 5/6] Fix existing unit tests. --- .../class-wp-sitemaps-large-test-provider.php | 2 +- tests/phpunit/tests/canonical/sitemaps.php | 12 ------------ tests/phpunit/tests/sitemaps/functions.php | 16 ++++++++-------- tests/phpunit/tests/sitemaps/sitemaps.php | 16 ++++++++-------- tests/phpunit/tests/sitemaps/wpSitemapsIndex.php | 12 ++++++------ tests/phpunit/tests/sitemaps/wpSitemapsPosts.php | 4 ++-- .../tests/sitemaps/wpSitemapsRenderer.php | 15 ++++++++++++--- .../tests/sitemaps/wpSitemapsTaxonomies.php | 8 ++++---- 8 files changed, 41 insertions(+), 44 deletions(-) diff --git a/tests/phpunit/includes/class-wp-sitemaps-large-test-provider.php b/tests/phpunit/includes/class-wp-sitemaps-large-test-provider.php index 1620a641b5fb2..b806c03f48656 100644 --- a/tests/phpunit/includes/class-wp-sitemaps-large-test-provider.php +++ b/tests/phpunit/includes/class-wp-sitemaps-large-test-provider.php @@ -43,7 +43,7 @@ public function get_url_list( $page_num, $object_subtype = '' ) { * * @return array[] Array of sitemap entries. */ - public function get_sitemap_entries() { + public function get_sitemap_entries( $format ) { return array_fill( 0, $this->num_entries, array( 'loc' => home_url( '/' ) ) ); } diff --git a/tests/phpunit/tests/canonical/sitemaps.php b/tests/phpunit/tests/canonical/sitemaps.php index 97fa6c4ee8937..1ec0c8a0a81ee 100644 --- a/tests/phpunit/tests/canonical/sitemaps.php +++ b/tests/phpunit/tests/canonical/sitemaps.php @@ -20,12 +20,6 @@ public function test_remove_trailing_slashes_for_sitemap_index_requests() { $this->assertCanonical( '/wp-sitemap.xml/', '/wp-sitemap.xml' ); } - public function test_remove_trailing_slashes_for_sitemap_index_stylesheet_requests() { - $this->set_permalink_structure( '/%postname%/' ); - $this->assertCanonical( '/wp-sitemap-index.xsl', '/wp-sitemap-index.xsl' ); - $this->assertCanonical( '/wp-sitemap-index.xsl/', '/wp-sitemap-index.xsl' ); - } - public function test_remove_trailing_slashes_for_sitemap_requests() { $this->set_permalink_structure( '/%postname%/' ); $this->assertCanonical( '/wp-sitemap-posts-post-1.xml', '/wp-sitemap-posts-post-1.xml' ); @@ -34,12 +28,6 @@ public function test_remove_trailing_slashes_for_sitemap_requests() { $this->assertCanonical( '/wp-sitemap-users-1.xml/', '/wp-sitemap-users-1.xml' ); } - public function test_remove_trailing_slashes_for_sitemap_stylesheet_requests() { - $this->set_permalink_structure( '/%postname%/' ); - $this->assertCanonical( '/wp-sitemap.xsl', '/wp-sitemap.xsl' ); - $this->assertCanonical( '/wp-sitemap.xsl/', '/wp-sitemap.xsl' ); - } - /** * Ensure sitemaps redirects work as expected with pretty permalinks. * diff --git a/tests/phpunit/tests/sitemaps/functions.php b/tests/phpunit/tests/sitemaps/functions.php index 0620d465d4795..7d4dc1f253b89 100644 --- a/tests/phpunit/tests/sitemaps/functions.php +++ b/tests/phpunit/tests/sitemaps/functions.php @@ -98,17 +98,17 @@ public function test_get_sitemap_url_pretty_permalinks( $name, $subtype_name, $p */ public function data_get_sitemap_url_plain_permalinks() { return array( - array( 'posts', 'post', 1, home_url( '/?sitemap=posts&sitemap-subtype=post&paged=1' ) ), - array( 'posts', 'post', 0, home_url( '/?sitemap=posts&sitemap-subtype=post&paged=1' ) ), - array( 'posts', 'page', 1, home_url( '/?sitemap=posts&sitemap-subtype=page&paged=1' ) ), - array( 'posts', 'page', 5, home_url( '/?sitemap=posts&sitemap-subtype=page&paged=5' ) ), + array( 'posts', 'post', 1, home_url( '/?sitemap=posts&sitemap-subtype=post&paged=1&sitemap-format=xml' ) ), + array( 'posts', 'post', 0, home_url( '/?sitemap=posts&sitemap-subtype=post&paged=1&sitemap-format=xml' ) ), + array( 'posts', 'page', 1, home_url( '/?sitemap=posts&sitemap-subtype=page&paged=1&sitemap-format=xml' ) ), + array( 'posts', 'page', 5, home_url( '/?sitemap=posts&sitemap-subtype=page&paged=5&sitemap-format=xml' ) ), // Post type doesn't exist. array( 'posts', 'foo', 5, false ), - array( 'taxonomies', 'category', 1, home_url( '/?sitemap=taxonomies&sitemap-subtype=category&paged=1' ) ), - array( 'taxonomies', 'post_tag', 1, home_url( '/?sitemap=taxonomies&sitemap-subtype=post_tag&paged=1' ) ), + array( 'taxonomies', 'category', 1, home_url( '/?sitemap=taxonomies&sitemap-subtype=category&paged=1&sitemap-format=xml' ) ), + array( 'taxonomies', 'post_tag', 1, home_url( '/?sitemap=taxonomies&sitemap-subtype=post_tag&paged=1&sitemap-format=xml' ) ), // Negative paged, gets converted to its absolute value. - array( 'taxonomies', 'post_tag', -1, home_url( '/?sitemap=taxonomies&sitemap-subtype=post_tag&paged=1' ) ), - array( 'users', '', 4, home_url( '/?sitemap=users&paged=4' ) ), + array( 'taxonomies', 'post_tag', -1, home_url( '/?sitemap=taxonomies&sitemap-subtype=post_tag&paged=1&sitemap-format=xml' ) ), + array( 'users', '', 4, home_url( '/?sitemap=users&paged=4&sitemap-format=xml' ) ), // Users provider doesn't allow subtypes. array( 'users', 'foo', 4, false ), // Provider doesn't exist. diff --git a/tests/phpunit/tests/sitemaps/sitemaps.php b/tests/phpunit/tests/sitemaps/sitemaps.php index 85f9965245842..8de0e73107461 100644 --- a/tests/phpunit/tests/sitemaps/sitemaps.php +++ b/tests/phpunit/tests/sitemaps/sitemaps.php @@ -104,7 +104,7 @@ public function _get_sitemap_entries() { foreach ( $providers as $provider ) { // Using `array_push` is more efficient than `array_merge` in the loop. - array_push( $entries, ...$provider->get_sitemap_entries() ); + array_push( $entries, ...$provider->get_sitemap_entries( 'xml' ) ); } return $entries; @@ -118,19 +118,19 @@ public function test_get_sitemap_entries() { $expected = array( array( - 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=post&paged=1', + 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=post&paged=1&sitemap-format=xml', ), array( - 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=page&paged=1', + 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=page&paged=1&sitemap-format=xml', ), array( - 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=category&paged=1', + 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=category&paged=1&sitemap-format=xml', ), array( - 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=post_tag&paged=1', + 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=post_tag&paged=1&sitemap-format=xml', ), array( - 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=users&paged=1', + 'loc' => 'http://' . WP_TESTS_DOMAIN . '/?sitemap=users&paged=1&sitemap-format=xml', ), ); @@ -187,8 +187,8 @@ public function test_get_sitemap_entries_custom_post_types() { unregister_post_type( 'public_cpt' ); unregister_post_type( 'private_cpt' ); - $this->assertContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=public_cpt&paged=1', $entries, 'Public CPTs are not in the index.' ); - $this->assertNotContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=private_cpt&paged=1', $entries, 'Private CPTs are visible in the index.' ); + $this->assertContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=public_cpt&paged=1&sitemap-format=xml', $entries, 'Public CPTs are not in the index.' ); + $this->assertNotContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=posts&sitemap-subtype=private_cpt&paged=1&sitemap-format=xml', $entries, 'Private CPTs are visible in the index.' ); } /** diff --git a/tests/phpunit/tests/sitemaps/wpSitemapsIndex.php b/tests/phpunit/tests/sitemaps/wpSitemapsIndex.php index 44d6df40f6d2d..54a7c0788ae7a 100644 --- a/tests/phpunit/tests/sitemaps/wpSitemapsIndex.php +++ b/tests/phpunit/tests/sitemaps/wpSitemapsIndex.php @@ -18,7 +18,7 @@ public function test_get_sitemap_list() { $registry->add_provider( 'bar', new WP_Sitemaps_Test_Provider( 'bar' ) ); $sitemap_index = new WP_Sitemaps_Index( $registry ); - $this->assertCount( 24, $sitemap_index->get_sitemap_list() ); + $this->assertCount( 24, $sitemap_index->get_sitemap_list( 'xml' ) ); } /** @@ -41,7 +41,7 @@ public function test_get_sitemap_list_limit() { $this->assertGreaterThan( 50000, $count ); $sitemap_index = new WP_Sitemaps_Index( $registry ); - $this->assertCount( 50000, $sitemap_index->get_sitemap_list() ); + $this->assertCount( 50000, $sitemap_index->get_sitemap_list( 'xml' ) ); } public function test_get_sitemap_list_no_entries() { @@ -50,14 +50,14 @@ public function test_get_sitemap_list_no_entries() { $registry->add_provider( 'foo', new WP_Sitemaps_Empty_Test_Provider( 'foo' ) ); $sitemap_index = new WP_Sitemaps_Index( $registry ); - $this->assertCount( 0, $sitemap_index->get_sitemap_list() ); + $this->assertCount( 0, $sitemap_index->get_sitemap_list( 'xml' ) ); } public function test_get_index_url() { $sitemap_index = new WP_Sitemaps_Index( new WP_Sitemaps_Registry() ); - $index_url = $sitemap_index->get_index_url(); + $index_url = $sitemap_index->get_index_url( 'xml' ); - $this->assertStringEndsWith( '/?sitemap=index', $index_url ); + $this->assertStringEndsWith( '/?sitemap=index&sitemap-format=xml', $index_url ); } public function test_get_index_url_pretty_permalinks() { @@ -65,7 +65,7 @@ public function test_get_index_url_pretty_permalinks() { $this->set_permalink_structure( '/%year%/%postname%/' ); $sitemap_index = new WP_Sitemaps_Index( new WP_Sitemaps_Registry() ); - $index_url = $sitemap_index->get_index_url(); + $index_url = $sitemap_index->get_index_url( 'xml' ); // Clean up permalinks. $this->set_permalink_structure(); diff --git a/tests/phpunit/tests/sitemaps/wpSitemapsPosts.php b/tests/phpunit/tests/sitemaps/wpSitemapsPosts.php index 70b2fbc250cc3..a1c6273f914f2 100644 --- a/tests/phpunit/tests/sitemaps/wpSitemapsPosts.php +++ b/tests/phpunit/tests/sitemaps/wpSitemapsPosts.php @@ -17,11 +17,11 @@ public function test_get_sitemap_entries_homepage() { $posts_provider = new WP_Sitemaps_Posts(); - $post_list = $posts_provider->get_sitemap_entries(); + $post_list = $posts_provider->get_sitemap_entries( 'xml' ); $expected = array( array( - 'loc' => home_url( '/?sitemap=posts&sitemap-subtype=page&paged=1' ), + 'loc' => home_url( '/?sitemap=posts&sitemap-subtype=page&paged=1&sitemap-format=xml' ), ), ); diff --git a/tests/phpunit/tests/sitemaps/wpSitemapsRenderer.php b/tests/phpunit/tests/sitemaps/wpSitemapsRenderer.php index 8ab5e5e3ba827..fdd259760a7b9 100644 --- a/tests/phpunit/tests/sitemaps/wpSitemapsRenderer.php +++ b/tests/phpunit/tests/sitemaps/wpSitemapsRenderer.php @@ -5,13 +5,18 @@ */ class Tests_Sitemaps_wpSitemapsRenderer extends WP_Test_XML_TestCase { + /* + * pvb public function test_get_sitemap_stylesheet_url() { $sitemap_renderer = new WP_Sitemaps_Renderer(); $stylesheet_url = $sitemap_renderer->get_sitemap_stylesheet_url(); $this->assertStringEndsWith( '/?sitemap-stylesheet=sitemap', $stylesheet_url ); } + */ + /* + * pvb public function test_get_sitemap_stylesheet_url_pretty_permalinks() { // Set permalinks for testing. $this->set_permalink_structure( '/%year%/%postname%/' ); @@ -24,14 +29,20 @@ public function test_get_sitemap_stylesheet_url_pretty_permalinks() { $this->assertStringEndsWith( '/wp-sitemap.xsl', $stylesheet_url ); } + */ + /* + * pvb public function test_get_sitemap_index_stylesheet_url() { $sitemap_renderer = new WP_Sitemaps_Renderer(); $stylesheet_url = $sitemap_renderer->get_sitemap_index_stylesheet_url(); $this->assertStringEndsWith( '/?sitemap-stylesheet=index', $stylesheet_url ); } + */ + /* + * pvb public function test_get_sitemap_index_stylesheet_url_pretty_permalinks() { // Set permalinks for testing. $this->set_permalink_structure( '/%year%/%postname%/' ); @@ -44,6 +55,7 @@ public function test_get_sitemap_index_stylesheet_url_pretty_permalinks() { $this->assertStringEndsWith( '/wp-sitemap-index.xsl', $stylesheet_url ); } + */ /** * Test XML output for the sitemap index renderer. @@ -71,7 +83,6 @@ public function test_get_sitemap_index_xml() { $actual = $renderer->get_sitemap_index_xml( $entries ); $expected = '' . - '' . '' . 'http://' . WP_TESTS_DOMAIN . '/wp-sitemap-posts-post-1.xml' . 'http://' . WP_TESTS_DOMAIN . '/wp-sitemap-posts-page-1.xml' . @@ -114,7 +125,6 @@ public function test_get_sitemap_index_xml_with_lastmod() { $actual = $renderer->get_sitemap_index_xml( $entries ); $expected = '' . - '' . '' . 'http://' . WP_TESTS_DOMAIN . '/wp-sitemap-posts-post-1.xml2005-01-01' . 'http://' . WP_TESTS_DOMAIN . '/wp-sitemap-posts-page-1.xml2005-01-01' . @@ -210,7 +220,6 @@ public function test_get_sitemap_xml() { $actual = $renderer->get_sitemap_xml( $url_list ); $expected = '' . - '' . '' . 'http://' . WP_TESTS_DOMAIN . '/2019/10/post-1' . 'http://' . WP_TESTS_DOMAIN . '/2019/10/post-2' . diff --git a/tests/phpunit/tests/sitemaps/wpSitemapsTaxonomies.php b/tests/phpunit/tests/sitemaps/wpSitemapsTaxonomies.php index 456efe06e922f..38b5339ea1abf 100644 --- a/tests/phpunit/tests/sitemaps/wpSitemapsTaxonomies.php +++ b/tests/phpunit/tests/sitemaps/wpSitemapsTaxonomies.php @@ -195,16 +195,16 @@ public function test_get_sitemap_entries_custom_taxonomies() { ); $tax_provider = new WP_Sitemaps_Taxonomies(); - $entries = wp_list_pluck( $tax_provider->get_sitemap_entries(), 'loc' ); + $entries = wp_list_pluck( $tax_provider->get_sitemap_entries( 'xml' ), 'loc' ); // Clean up. unregister_taxonomy_for_object_type( 'public_taxonomy', 'post' ); unregister_taxonomy_for_object_type( 'non_queryable_taxonomy', 'post' ); unregister_taxonomy_for_object_type( 'private_taxonomy', 'post' ); - $this->assertContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=public_taxonomy&paged=1', $entries, 'Public Taxonomies are not in the index.' ); - $this->assertNotContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=non_queryable_taxonomy&paged=1', $entries, 'Private Taxonomies are visible in the index.' ); - $this->assertNotContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=private_taxonomy&paged=1', $entries, 'Private Taxonomies are visible in the index.' ); + $this->assertContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=public_taxonomy&paged=1&sitemap-format=xml', $entries, 'Public Taxonomies are not in the index.' ); + $this->assertNotContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=non_queryable_taxonomy&paged=1&sitemap-format=xml', $entries, 'Private Taxonomies are visible in the index.' ); + $this->assertNotContains( 'http://' . WP_TESTS_DOMAIN . '/?sitemap=taxonomies&sitemap-subtype=private_taxonomy&paged=1&sitemap-format=xml', $entries, 'Private Taxonomies are visible in the index.' ); } /** From 311f42042f99c765c1a9fa831b0771080d30ef88 Mon Sep 17 00:00:00 2001 From: Paul Biron Date: Fri, 17 Jul 2026 18:32:28 -0600 Subject: [PATCH 6/6] Fix the testPublicQueryVarsAreAsExpected() unit test. --- tests/phpunit/tests/query/vars.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/query/vars.php b/tests/phpunit/tests/query/vars.php index 87e7fa7bb75c6..eec2ef1d04439 100644 --- a/tests/phpunit/tests/query/vars.php +++ b/tests/phpunit/tests/query/vars.php @@ -73,7 +73,7 @@ public function testPublicQueryVarsAreAsExpected() { 'rest_route', 'sitemap', 'sitemap-subtype', - 'sitemap-stylesheet', + 'sitemap-format', ), $wp->public_query_vars,