class-wp-sitemaps.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <?php
  2. /**
  3. * Sitemaps: WP_Sitemaps class
  4. *
  5. * This is the main class integrating all other classes.
  6. *
  7. * @package WordPress
  8. * @subpackage Sitemaps
  9. * @since 5.5.0
  10. */
  11. /**
  12. * Class WP_Sitemaps.
  13. *
  14. * @since 5.5.0
  15. */
  16. #[AllowDynamicProperties]
  17. class WP_Sitemaps {
  18. /**
  19. * The main index of supported sitemaps.
  20. *
  21. * @since 5.5.0
  22. *
  23. * @var WP_Sitemaps_Index
  24. */
  25. public $index;
  26. /**
  27. * The main registry of supported sitemaps.
  28. *
  29. * @since 5.5.0
  30. *
  31. * @var WP_Sitemaps_Registry
  32. */
  33. public $registry;
  34. /**
  35. * An instance of the renderer class.
  36. *
  37. * @since 5.5.0
  38. *
  39. * @var WP_Sitemaps_Renderer
  40. */
  41. public $renderer;
  42. /**
  43. * WP_Sitemaps constructor.
  44. *
  45. * @since 5.5.0
  46. */
  47. public function __construct() {
  48. $this->registry = new WP_Sitemaps_Registry();
  49. $this->renderer = new WP_Sitemaps_Renderer();
  50. $this->index = new WP_Sitemaps_Index( $this->registry );
  51. }
  52. /**
  53. * Initiates all sitemap functionality.
  54. *
  55. * If sitemaps are disabled, only the rewrite rules will be registered
  56. * by this method, in order to properly send 404s.
  57. *
  58. * @since 5.5.0
  59. */
  60. public function init() {
  61. // These will all fire on the init hook.
  62. $this->register_rewrites();
  63. add_action( 'template_redirect', array( $this, 'render_sitemaps' ) );
  64. if ( ! $this->sitemaps_enabled() ) {
  65. return;
  66. }
  67. $this->register_sitemaps();
  68. // Add additional action callbacks.
  69. add_filter( 'pre_handle_404', array( $this, 'redirect_sitemapxml' ), 10, 2 );
  70. add_filter( 'robots_txt', array( $this, 'add_robots' ), 0, 2 );
  71. }
  72. /**
  73. * Determines whether sitemaps are enabled or not.
  74. *
  75. * @since 5.5.0
  76. *
  77. * @return bool Whether sitemaps are enabled.
  78. */
  79. public function sitemaps_enabled() {
  80. $is_enabled = (bool) get_option( 'blog_public' );
  81. /**
  82. * Filters whether XML Sitemaps are enabled or not.
  83. *
  84. * When XML Sitemaps are disabled via this filter, rewrite rules are still
  85. * in place to ensure a 404 is returned.
  86. *
  87. * @see WP_Sitemaps::register_rewrites()
  88. *
  89. * @since 5.5.0
  90. *
  91. * @param bool $is_enabled Whether XML Sitemaps are enabled or not. Defaults
  92. * to true for public sites.
  93. */
  94. return (bool) apply_filters( 'wp_sitemaps_enabled', $is_enabled );
  95. }
  96. /**
  97. * Registers and sets up the functionality for all supported sitemaps.
  98. *
  99. * @since 5.5.0
  100. */
  101. public function register_sitemaps() {
  102. $providers = array(
  103. 'posts' => new WP_Sitemaps_Posts(),
  104. 'taxonomies' => new WP_Sitemaps_Taxonomies(),
  105. 'users' => new WP_Sitemaps_Users(),
  106. );
  107. /* @var WP_Sitemaps_Provider $provider */
  108. foreach ( $providers as $name => $provider ) {
  109. $this->registry->add_provider( $name, $provider );
  110. }
  111. }
  112. /**
  113. * Registers sitemap rewrite tags and routing rules.
  114. *
  115. * @since 5.5.0
  116. */
  117. public function register_rewrites() {
  118. // Add rewrite tags.
  119. add_rewrite_tag( '%sitemap%', '([^?]+)' );
  120. add_rewrite_tag( '%sitemap-subtype%', '([^?]+)' );
  121. // Register index route.
  122. add_rewrite_rule( '^wp-sitemap\.xml$', 'index.php?sitemap=index', 'top' );
  123. // Register rewrites for the XSL stylesheet.
  124. add_rewrite_tag( '%sitemap-stylesheet%', '([^?]+)' );
  125. add_rewrite_rule( '^wp-sitemap\.xsl$', 'index.php?sitemap-stylesheet=sitemap', 'top' );
  126. add_rewrite_rule( '^wp-sitemap-index\.xsl$', 'index.php?sitemap-stylesheet=index', 'top' );
  127. // Register routes for providers.
  128. add_rewrite_rule(
  129. '^wp-sitemap-([a-z]+?)-([a-z\d_-]+?)-(\d+?)\.xml$',
  130. 'index.php?sitemap=$matches[1]&sitemap-subtype=$matches[2]&paged=$matches[3]',
  131. 'top'
  132. );
  133. add_rewrite_rule(
  134. '^wp-sitemap-([a-z]+?)-(\d+?)\.xml$',
  135. 'index.php?sitemap=$matches[1]&paged=$matches[2]',
  136. 'top'
  137. );
  138. }
  139. /**
  140. * Renders sitemap templates based on rewrite rules.
  141. *
  142. * @since 5.5.0
  143. *
  144. * @global WP_Query $wp_query WordPress Query object.
  145. */
  146. public function render_sitemaps() {
  147. global $wp_query;
  148. $sitemap = sanitize_text_field( get_query_var( 'sitemap' ) );
  149. $object_subtype = sanitize_text_field( get_query_var( 'sitemap-subtype' ) );
  150. $stylesheet_type = sanitize_text_field( get_query_var( 'sitemap-stylesheet' ) );
  151. $paged = absint( get_query_var( 'paged' ) );
  152. // Bail early if this isn't a sitemap or stylesheet route.
  153. if ( ! ( $sitemap || $stylesheet_type ) ) {
  154. return;
  155. }
  156. if ( ! $this->sitemaps_enabled() ) {
  157. $wp_query->set_404();
  158. status_header( 404 );
  159. return;
  160. }
  161. // Render stylesheet if this is stylesheet route.
  162. if ( $stylesheet_type ) {
  163. $stylesheet = new WP_Sitemaps_Stylesheet();
  164. $stylesheet->render_stylesheet( $stylesheet_type );
  165. exit;
  166. }
  167. // Render the index.
  168. if ( 'index' === $sitemap ) {
  169. $sitemap_list = $this->index->get_sitemap_list();
  170. $this->renderer->render_index( $sitemap_list );
  171. exit;
  172. }
  173. $provider = $this->registry->get_provider( $sitemap );
  174. if ( ! $provider ) {
  175. return;
  176. }
  177. if ( empty( $paged ) ) {
  178. $paged = 1;
  179. }
  180. $url_list = $provider->get_url_list( $paged, $object_subtype );
  181. // Force a 404 and bail early if no URLs are present.
  182. if ( empty( $url_list ) ) {
  183. $wp_query->set_404();
  184. status_header( 404 );
  185. return;
  186. }
  187. $this->renderer->render_sitemap( $url_list );
  188. exit;
  189. }
  190. /**
  191. * Redirects a URL to the wp-sitemap.xml
  192. *
  193. * @since 5.5.0
  194. *
  195. * @param bool $bypass Pass-through of the pre_handle_404 filter value.
  196. * @param WP_Query $query The WP_Query object.
  197. * @return bool Bypass value.
  198. */
  199. public function redirect_sitemapxml( $bypass, $query ) {
  200. // If a plugin has already utilized the pre_handle_404 function, return without action to avoid conflicts.
  201. if ( $bypass ) {
  202. return $bypass;
  203. }
  204. // 'pagename' is for most permalink types, name is for when the %postname% is used as a top-level field.
  205. if ( 'sitemap-xml' === $query->get( 'pagename' )
  206. || 'sitemap-xml' === $query->get( 'name' )
  207. ) {
  208. wp_safe_redirect( $this->index->get_index_url() );
  209. exit();
  210. }
  211. return $bypass;
  212. }
  213. /**
  214. * Adds the sitemap index to robots.txt.
  215. *
  216. * @since 5.5.0
  217. *
  218. * @param string $output robots.txt output.
  219. * @param bool $public Whether the site is public.
  220. * @return string The robots.txt output.
  221. */
  222. public function add_robots( $output, $public ) {
  223. if ( $public ) {
  224. $output .= "\nSitemap: " . esc_url( $this->index->get_index_url() ) . "\n";
  225. }
  226. return $output;
  227. }
  228. }