WPSEO_Sitemaps_Cache_Validator

Handles storage keys for sitemaps caching and invalidation.

Defined (1)

The class is defined in the following location(s).

/inc/sitemaps/class-sitemaps-cache-validator.php  
  1. class WPSEO_Sitemaps_Cache_Validator { 
  2.  
  3. /** @var string Prefix of the transient key for sitemap caches */ 
  4. const STORAGE_KEY_PREFIX = 'yst_sm_'; 
  5.  
  6. /** Name of the option that holds the global validation value */ 
  7. const VALIDATION_GLOBAL_KEY = 'wpseo_sitemap_cache_validator_global'; 
  8.  
  9. /** The format which creates the key of the option that holds the type validation value */ 
  10. const VALIDATION_TYPE_KEY_FORMAT = 'wpseo_sitemap_%s_cache_validator'; 
  11.  
  12. /** 
  13. * Get the cache key for a certain type and page 
  14. * A type of cache would be something like 'page', 'post' or 'video'. 
  15. * Example key format for sitemap type "post", page 1: wpseo_sitemap_post_1:akfw3e_23azBa 
  16. * @since 3.2 
  17. * @param null|string $type The type to get the key for. Null or self::SITEMAP_INDEX_TYPE for index cache. 
  18. * @param int $page The page of cache to get the key for. 
  19. * @return bool|string The key where the cache is stored on. False if the key could not be generated. 
  20. */ 
  21. public static function get_storage_key( $type = null, $page = 1 ) { 
  22.  
  23. // Using SITEMAP_INDEX_TYPE for sitemap index cache. 
  24. $type = is_null( $type ) ? WPSEO_Sitemaps::SITEMAP_INDEX_TYPE : $type; 
  25.  
  26. $global_cache_validator = self::get_validator(); 
  27. $type_cache_validator = self::get_validator( $type ); 
  28.  
  29. $prefix = self::STORAGE_KEY_PREFIX; 
  30. $postfix = sprintf( '_%d:%s_%s', $page, $global_cache_validator, $type_cache_validator ); 
  31.  
  32. try { 
  33. $type = self::truncate_type( $type, $prefix, $postfix ); 
  34. } catch ( OutOfBoundsException $exception ) { 
  35. // Maybe do something with the exception, for now just mark as invalid. 
  36. return false; 
  37.  
  38. // Build key. 
  39. $full_key = $prefix . $type . $postfix; 
  40.  
  41. return $full_key; 
  42.  
  43. /** 
  44. * If the type is over length make sure we compact it so we don't have any database problems 
  45. * When there are more 'extremely long' post types, changes are they have variations in either the start or ending. 
  46. * Because of this, we cut out the excess in the middle which should result in less chance of collision. 
  47. * @since 3.2 
  48. * @param string $type The type of sitemap to be used. 
  49. * @param string $prefix The part before the type in the cache key. Only the length is used. 
  50. * @param string $postfix The part after the type in the cache key. Only the length is used. 
  51. * @return string The type with a safe length to use 
  52. * @throws OutOfRangeException When there is less than 15 characters of space for a key that is originally longer. 
  53. */ 
  54. public static function truncate_type( $type, $prefix = '', $postfix = '' ) { 
  55. /** 
  56. * This length has been restricted by the database column length of 64 in the past. 
  57. * The prefix added by WordPress is '_transient_' because we are saving to a transient. 
  58. * We need to use a timeout on the transient, otherwise the values get autoloaded, this adds 
  59. * another restriction to the length. 
  60. */ 
  61. $max_length = 45; // 64 - 19 ('_transient_timeout_') 
  62. $max_length -= strlen( $prefix ); 
  63. $max_length -= strlen( $postfix ); 
  64.  
  65. if ( strlen( $type ) > $max_length ) { 
  66.  
  67. if ( $max_length < 15 ) { 
  68. /** 
  69. * If this happens the most likely cause is a page number that is too high. 
  70. * So this would not happen unintentionally.. 
  71. * Either by trying to cause a high server load, finding backdoors or misconfiguration. 
  72. */ 
  73. throw new OutOfRangeException( 
  74. __( 
  75. 'Trying to build the sitemap cache key, but the postfix and prefix combination leaves too little room to do this. You are probably requesting a page that is way out of the expected range.',  
  76. 'wordpress-seo' 
  77. ); 
  78.  
  79. $half = ( $max_length / 2 ); 
  80.  
  81. $first_part = substr( $type, 0, ( ceil( $half ) - 1 ) ); 
  82. $last_part = substr( $type, ( 1 - floor( $half ) ) ); 
  83.  
  84. $type = $first_part . '..' . $last_part; 
  85.  
  86. return $type; 
  87.  
  88. /** 
  89. * Invalidate sitemap cache 
  90. * @since 3.2 
  91. * @param null|string $type The type to get the key for. Null for all caches. 
  92. * @return void 
  93. */ 
  94. public static function invalidate_storage( $type = null ) { 
  95.  
  96. // Global validator gets cleared when no type is provided. 
  97. $old_validator = null; 
  98.  
  99. // Get the current type validator. 
  100. if ( ! is_null( $type ) ) { 
  101. $old_validator = self::get_validator( $type ); 
  102.  
  103. // Refresh validator. 
  104. self::create_validator( $type ); 
  105.  
  106. if ( ! wp_using_ext_object_cache() ) { 
  107. // Clean up current cache from the database. 
  108. self::cleanup_database( $type, $old_validator ); 
  109.  
  110. // External object cache pushes old and unretrieved items out by itself so we don't have to do anything for that. 
  111.  
  112. /** 
  113. * Cleanup invalidated database cache 
  114. * @since 3.2 
  115. * @param null|string $type The type of sitemap to clear cache for. 
  116. * @param null|string $validator The validator to clear cache of. 
  117. * @return void 
  118. */ 
  119. public static function cleanup_database( $type = null, $validator = null ) { 
  120.  
  121. global $wpdb; 
  122.  
  123. if ( is_null( $type ) ) { 
  124. // Clear all cache if no type is provided. 
  125. $like = sprintf( '%s%%', self::STORAGE_KEY_PREFIX ); 
  126. else { 
  127. // Clear type cache for all type keys. 
  128. $like = sprintf( '%1$s%2$s_%%', self::STORAGE_KEY_PREFIX, $type ); 
  129.  
  130. /** 
  131. * Add slashes to the LIKE "_" single character wildcard. 
  132. * We can't use `esc_like` here because we need the % in the query. 
  133. */ 
  134. $where = array(); 
  135. $where[] = sprintf( "option_name LIKE '%s'", addcslashes( '_transient_' . $like, '_' ) ); 
  136. $where[] = sprintf( "option_name LIKE '%s'", addcslashes( '_transient_timeout_' . $like, '_' ) ); 
  137.  
  138. // Delete transients. 
  139. $query = sprintf( 'DELETE FROM %1$s WHERE %2$s', $wpdb->options, implode( ' OR ', $where ) ); 
  140. $wpdb->query( $query ); 
  141.  
  142. /** 
  143. * Get the current cache validator 
  144. * Without the type the global validator is returned. 
  145. * This can invalidate -all- keys in cache at once 
  146. * With the type parameter the validator for that specific 
  147. * type can be invalidated 
  148. * @since 3.2 
  149. * @param string $type Provide a type for a specific type validator, empty for global validator. 
  150. * @return null|string The validator for the supplied type. 
  151. */ 
  152. public static function get_validator( $type = '' ) { 
  153.  
  154. $key = self::get_validator_key( $type ); 
  155.  
  156. $current = get_option( $key, null ); 
  157. if ( ! is_null( $current ) ) { 
  158. return $current; 
  159.  
  160. if ( self::create_validator( $type ) ) { 
  161. return self::get_validator( $type ); 
  162.  
  163. return null; 
  164.  
  165. /** 
  166. * Get the cache validator option key for the specified type 
  167. * @since 3.2 
  168. * @param string $type Provide a type for a specific type validator, empty for global validator. 
  169. * @return string Validator to be used to generate the cache key. 
  170. */ 
  171. public static function get_validator_key( $type = '' ) { 
  172.  
  173. if ( empty( $type ) ) { 
  174. return self::VALIDATION_GLOBAL_KEY; 
  175.  
  176. return sprintf( self::VALIDATION_TYPE_KEY_FORMAT, $type ); 
  177.  
  178. /** 
  179. * Refresh the cache validator value 
  180. * @since 3.2 
  181. * @param string $type Provide a type for a specific type validator, empty for global validator. 
  182. * @return bool True if validator key has been saved as option. 
  183. */ 
  184. public static function create_validator( $type = '' ) { 
  185.  
  186. $key = self::get_validator_key( $type ); 
  187.  
  188. // Generate new validator. 
  189. $microtime = microtime(); 
  190.  
  191. // Remove space. 
  192. list( $milliseconds, $seconds ) = explode( ' ', $microtime ); 
  193.  
  194. // Transients are purged every 24h. 
  195. $seconds = ( $seconds % DAY_IN_SECONDS ); 
  196. $milliseconds = intval( substr( $milliseconds, 2, 3 ), 10 ); 
  197.  
  198. // Combine seconds and milliseconds and convert to integer. 
  199. $validator = intval( $seconds . '' . $milliseconds, 10 ); 
  200.  
  201. // Apply base 61 encoding. 
  202. $compressed = self::convert_base10_to_base61( $validator ); 
  203.  
  204. return update_option( $key, $compressed, false ); 
  205.  
  206. /** 
  207. * Encode to base61 format. 
  208. * @since 3.2 
  209. * This is base64 (numeric + alpha + alpha upper case) without the 0. 
  210. * @param int $base10 The number that has to be converted to base 61. 
  211. * @return string Base 61 converted string. 
  212. * @throws InvalidArgumentException When the input is not an integer. 
  213. */ 
  214. public static function convert_base10_to_base61( $base10 ) { 
  215.  
  216. if ( ! is_int( $base10 ) ) { 
  217. throw new InvalidArgumentException( __( 'Expected an integer as input.', 'wordpress-seo' ) ); 
  218.  
  219. // Characters that will be used in the conversion. 
  220. $characters = '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
  221. $length = strlen( $characters ); 
  222.  
  223. $remainder = $base10; 
  224. $output = ''; 
  225.  
  226. do { 
  227. // Building from right to left in the result. 
  228. $index = ( $remainder % $length ); 
  229.  
  230. // Prepend the character to the output. 
  231. $output = $characters[ $index ] . $output; 
  232.  
  233. // Determine the remainder after removing the applied number. 
  234. $remainder = floor( $remainder / $length ); 
  235.  
  236. // Keep doing it until we have no remainder left. 
  237. } while ( $remainder ); 
  238.  
  239. return $output;