class-sitemaps-cache.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\XML_Sitemaps
  6. */
  7. /**
  8. * Handles sitemaps caching and invalidation.
  9. *
  10. * @since 3.2
  11. */
  12. class WPSEO_Sitemaps_Cache {
  13. /**
  14. * Holds the options that, when updated, should cause the cache to clear.
  15. *
  16. * @var array
  17. */
  18. protected static $cache_clear = [];
  19. /**
  20. * Mirror of enabled status for static calls.
  21. *
  22. * @var bool
  23. */
  24. protected static $is_enabled = false;
  25. /**
  26. * Holds the flag to clear all cache.
  27. *
  28. * @var bool
  29. */
  30. protected static $clear_all = false;
  31. /**
  32. * Holds the array of types to clear.
  33. *
  34. * @var array
  35. */
  36. protected static $clear_types = [];
  37. /**
  38. * Hook methods for invalidation on necessary events.
  39. */
  40. public function __construct() {
  41. add_action( 'init', [ $this, 'init' ] );
  42. add_action( 'deleted_term_relationships', [ __CLASS__, 'invalidate' ] );
  43. add_action( 'update_option', [ __CLASS__, 'clear_on_option_update' ] );
  44. add_action( 'edited_terms', [ __CLASS__, 'invalidate_helper' ], 10, 2 );
  45. add_action( 'clean_term_cache', [ __CLASS__, 'invalidate_helper' ], 10, 2 );
  46. add_action( 'clean_object_term_cache', [ __CLASS__, 'invalidate_helper' ], 10, 2 );
  47. add_action( 'user_register', [ __CLASS__, 'invalidate_author' ] );
  48. add_action( 'delete_user', [ __CLASS__, 'invalidate_author' ] );
  49. add_action( 'shutdown', [ __CLASS__, 'clear_queued' ] );
  50. }
  51. /**
  52. * Setup context for static calls.
  53. */
  54. public function init() {
  55. self::$is_enabled = $this->is_enabled();
  56. }
  57. /**
  58. * If cache is enabled.
  59. *
  60. * @since 3.2
  61. *
  62. * @return boolean
  63. */
  64. public function is_enabled() {
  65. /**
  66. * Filter if XML sitemap transient cache is enabled.
  67. *
  68. * @param bool $unsigned Enable cache or not, defaults to true.
  69. */
  70. return apply_filters( 'wpseo_enable_xml_sitemap_transient_caching', false );
  71. }
  72. /**
  73. * Retrieve the sitemap page from cache.
  74. *
  75. * @since 3.2
  76. *
  77. * @param string $type Sitemap type.
  78. * @param int $page Page number to retrieve.
  79. *
  80. * @return string|boolean
  81. */
  82. public function get_sitemap( $type, $page ) {
  83. $transient_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
  84. if ( false === $transient_key ) {
  85. return false;
  86. }
  87. return get_transient( $transient_key );
  88. }
  89. /**
  90. * Get the sitemap that is cached.
  91. *
  92. * @param string $type Sitemap type.
  93. * @param int $page Page number to retrieve.
  94. *
  95. * @return null|WPSEO_Sitemap_Cache_Data Null on no cache found otherwise object containing sitemap and meta data.
  96. */
  97. public function get_sitemap_data( $type, $page ) {
  98. $sitemap = $this->get_sitemap( $type, $page );
  99. if ( empty( $sitemap ) ) {
  100. return null;
  101. }
  102. // Unserialize Cache Data object (is_serialized doesn't recognize classes).
  103. if ( is_string( $sitemap ) && 0 === strpos( $sitemap, 'C:24:"WPSEO_Sitemap_Cache_Data"' ) ) {
  104. $sitemap = unserialize( $sitemap );
  105. }
  106. // What we expect it to be if it is set.
  107. if ( $sitemap instanceof WPSEO_Sitemap_Cache_Data_Interface ) {
  108. return $sitemap;
  109. }
  110. return null;
  111. }
  112. /**
  113. * Store the sitemap page from cache.
  114. *
  115. * @since 3.2
  116. *
  117. * @param string $type Sitemap type.
  118. * @param int $page Page number to store.
  119. * @param string $sitemap Sitemap body to store.
  120. * @param bool $usable Is this a valid sitemap or a cache of an invalid sitemap.
  121. *
  122. * @return bool
  123. */
  124. public function store_sitemap( $type, $page, $sitemap, $usable = true ) {
  125. $transient_key = WPSEO_Sitemaps_Cache_Validator::get_storage_key( $type, $page );
  126. if ( false === $transient_key ) {
  127. return false;
  128. }
  129. $status = ( $usable ) ? WPSEO_Sitemap_Cache_Data::OK : WPSEO_Sitemap_Cache_Data::ERROR;
  130. $sitemap_data = new WPSEO_Sitemap_Cache_Data();
  131. $sitemap_data->set_sitemap( $sitemap );
  132. $sitemap_data->set_status( $status );
  133. return set_transient( $transient_key, $sitemap_data, DAY_IN_SECONDS );
  134. }
  135. /**
  136. * Delete cache transients for index and specific type.
  137. *
  138. * Always deletes the main index sitemaps cache, as that's always invalidated by any other change.
  139. *
  140. * @since 1.5.4
  141. * @since 3.2 Changed from function wpseo_invalidate_sitemap_cache() to method in this class.
  142. *
  143. * @param string $type Sitemap type to invalidate.
  144. *
  145. * @return void
  146. */
  147. public static function invalidate( $type ) {
  148. self::clear( [ $type ] );
  149. }
  150. /**
  151. * Helper to invalidate in hooks where type is passed as second argument.
  152. *
  153. * @since 3.2
  154. *
  155. * @param int $unused Unused term ID value.
  156. * @param string $type Taxonomy to invalidate.
  157. *
  158. * @return void
  159. */
  160. public static function invalidate_helper( $unused, $type ) {
  161. if (
  162. WPSEO_Options::get( 'noindex-' . $type ) === false ||
  163. WPSEO_Options::get( 'noindex-tax-' . $type ) === false
  164. ) {
  165. self::invalidate( $type );
  166. }
  167. }
  168. /**
  169. * Invalidate sitemap cache for authors.
  170. *
  171. * @param int $user_id User ID.
  172. *
  173. * @return bool True if the sitemap was properly invalidated. False otherwise.
  174. */
  175. public static function invalidate_author( $user_id ) {
  176. $user = get_user_by( 'id', $user_id );
  177. if ( $user === false ) {
  178. return false;
  179. }
  180. if ( 'user_register' === current_action() ) {
  181. update_user_meta( $user_id, '_yoast_wpseo_profile_updated', time() );
  182. }
  183. if ( empty( $user->roles ) || in_array( 'subscriber', $user->roles, true ) ) {
  184. return false;
  185. }
  186. self::invalidate( 'author' );
  187. return true;
  188. }
  189. /**
  190. * Invalidate sitemap cache for the post type of a post.
  191. *
  192. * Don't invalidate for revisions.
  193. *
  194. * @since 1.5.4
  195. * @since 3.2 Changed from function wpseo_invalidate_sitemap_cache_on_save_post() to method in this class.
  196. *
  197. * @param int $post_id Post ID to invalidate type for.
  198. *
  199. * @return void
  200. */
  201. public static function invalidate_post( $post_id ) {
  202. if ( wp_is_post_revision( $post_id ) ) {
  203. return;
  204. }
  205. self::invalidate( get_post_type( $post_id ) );
  206. }
  207. /**
  208. * Delete cache transients for given sitemaps types or all by default.
  209. *
  210. * @since 1.8.0
  211. * @since 3.2 Moved from WPSEO_Utils to this class.
  212. *
  213. * @param array $types Set of sitemap types to delete cache transients for.
  214. *
  215. * @return void
  216. */
  217. public static function clear( $types = [] ) {
  218. if ( ! self::$is_enabled ) {
  219. return;
  220. }
  221. // No types provided, clear all.
  222. if ( empty( $types ) ) {
  223. self::$clear_all = true;
  224. return;
  225. }
  226. // Always invalidate the index sitemap as well.
  227. if ( ! in_array( WPSEO_Sitemaps::SITEMAP_INDEX_TYPE, $types ) ) {
  228. array_unshift( $types, WPSEO_Sitemaps::SITEMAP_INDEX_TYPE );
  229. }
  230. foreach ( $types as $type ) {
  231. if ( ! in_array( $type, self::$clear_types ) ) {
  232. self::$clear_types[] = $type;
  233. }
  234. }
  235. }
  236. /**
  237. * Invalidate storage for cache types queued to clear.
  238. */
  239. public static function clear_queued() {
  240. if ( self::$clear_all ) {
  241. WPSEO_Sitemaps_Cache_Validator::invalidate_storage();
  242. self::$clear_all = false;
  243. self::$clear_types = [];
  244. return;
  245. }
  246. foreach ( self::$clear_types as $type ) {
  247. WPSEO_Sitemaps_Cache_Validator::invalidate_storage( $type );
  248. }
  249. self::$clear_types = [];
  250. }
  251. /**
  252. * Adds a hook that when given option is updated, the cache is cleared.
  253. *
  254. * @since 3.2
  255. *
  256. * @param string $option Option name.
  257. * @param string $type Sitemap type.
  258. */
  259. public static function register_clear_on_option_update( $option, $type = '' ) {
  260. self::$cache_clear[ $option ] = $type;
  261. }
  262. /**
  263. * Clears the transient cache when a given option is updated, if that option has been registered before.
  264. *
  265. * @since 3.2
  266. *
  267. * @param string $option The option name that's being updated.
  268. *
  269. * @return void
  270. */
  271. public static function clear_on_option_update( $option ) {
  272. if ( array_key_exists( $option, self::$cache_clear ) ) {
  273. if ( empty( self::$cache_clear[ $option ] ) ) {
  274. // Clear all caches.
  275. self::clear();
  276. }
  277. else {
  278. // Clear specific provided type(s).
  279. $types = (array) self::$cache_clear[ $option ];
  280. self::clear( $types );
  281. }
  282. }
  283. }
  284. }