You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
9.3 KiB
PHTML
356 lines
9.3 KiB
PHTML
8 months ago
|
<?php
|
||
|
/**
|
||
|
* The sitemap cache watcher.
|
||
|
*
|
||
|
* @since 0.9.0
|
||
|
* @package RankMath
|
||
|
* @subpackage RankMath\Sitemap
|
||
|
* @author Rank Math <support@rankmath.com>
|
||
|
*
|
||
|
* @copyright Copyright (C) 2008-2019, Yoast BV
|
||
|
* The following code is a derivative work of the code from the Yoast(https://github.com/Yoast/wordpress-seo/), which is licensed under GPL v3.
|
||
|
*/
|
||
|
|
||
|
namespace RankMath\Sitemap;
|
||
|
|
||
|
use RankMath\Helper;
|
||
|
use RankMath\Sitemap\Sitemap;
|
||
|
use RankMath\Traits\Hooker;
|
||
|
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* Cache_Watcher class.
|
||
|
*/
|
||
|
class Cache_Watcher {
|
||
|
|
||
|
use Hooker;
|
||
|
|
||
|
/**
|
||
|
* Holds the options that, when updated, should cause the cache to clear.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
protected static $cache_clear = [];
|
||
|
|
||
|
/**
|
||
|
* Holds the flag to clear all cache.
|
||
|
*
|
||
|
* @var boolean
|
||
|
*/
|
||
|
protected static $clear_all = false;
|
||
|
|
||
|
/**
|
||
|
* Holds the array of types to clear.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
protected static $clear_types = [];
|
||
|
|
||
|
/**
|
||
|
* Holds the array of post types being imported.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
private $importing_post_types = [];
|
||
|
|
||
|
/**
|
||
|
* The constructor.
|
||
|
*/
|
||
|
public function __construct() {
|
||
|
$this->action( 'save_post', 'save_post' );
|
||
|
$this->action( 'transition_post_status', 'status_transition', 10, 3 );
|
||
|
$this->action( 'admin_footer', 'status_transition_bulk_finished' );
|
||
|
|
||
|
$this->action( 'edited_terms', 'invalidate_term', 10, 2 );
|
||
|
$this->action( 'clean_term_cache', 'invalidate_term', 10, 2 );
|
||
|
$this->action( 'clean_object_term_cache', 'invalidate_term', 10, 2 );
|
||
|
|
||
|
$this->action( 'delete_user', 'invalidate_author' );
|
||
|
$this->action( 'user_register', 'invalidate_author' );
|
||
|
$this->action( 'profile_update', 'invalidate_author' );
|
||
|
|
||
|
$this->action( 'rank_math/sitemap/invalidate_object_type', 'invalidate_object_type', 10, 2 );
|
||
|
|
||
|
add_action( 'shutdown', [ __CLASS__, 'clear_queued' ] );
|
||
|
add_action( 'update_option', [ __CLASS__, 'clear_on_option_update' ] );
|
||
|
add_action( 'deleted_term_relationships', [ __CLASS__, 'invalidate' ] );
|
||
|
|
||
|
// Clear cache when user updates any of these options.
|
||
|
self::register_clear_on_option_update( 'home' );
|
||
|
self::register_clear_on_option_update( 'permalink_structure' );
|
||
|
self::register_clear_on_option_update( 'rank_math_modules' );
|
||
|
self::register_clear_on_option_update( 'rank-math-options-titles' );
|
||
|
self::register_clear_on_option_update( 'rank-math-options-general' );
|
||
|
self::register_clear_on_option_update( 'rank-math-options-sitemap' );
|
||
|
self::register_clear_on_option_update( 'date_format' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check for relevant post type before invalidation.
|
||
|
*
|
||
|
* @param int $post_id Post ID to possibly invalidate for.
|
||
|
*/
|
||
|
public function save_post( $post_id ) {
|
||
|
$post = get_post( $post_id );
|
||
|
if ( ! empty( $post->post_password ) || 'auto-draft' === $post->post_status ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
update_user_meta( $post->post_author, 'last_update', get_post_modified_time( 'U', false, $post ) );
|
||
|
$this->invalidate_post( $post_id );
|
||
|
$this->invalidate_author( $post->post_author );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Hooked into transition_post_status. Will initiate search engine pings
|
||
|
* if the post is being published, is a post type that a sitemap is built for
|
||
|
* and is a post that is included in sitemaps.
|
||
|
*
|
||
|
* @param string $new_status New post status.
|
||
|
* @param string $old_status Old post status.
|
||
|
* @param \WP_Post $post Post object.
|
||
|
*/
|
||
|
public function status_transition( $new_status, $old_status, $post ) {
|
||
|
if ( 'publish' !== $new_status ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( defined( 'WP_IMPORTING' ) ) {
|
||
|
$this->status_transition_bulk( $new_status, $old_status, $post );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( $this->can_exclude( $post ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( WP_CACHE ) {
|
||
|
wp_schedule_single_event( ( time() + 300 ), 'rank_math/sitemap/hit_index' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Can exclude post type.
|
||
|
*
|
||
|
* @param \WP_Post $post Post object.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private function can_exclude( $post ) {
|
||
|
$post_type = get_post_type( $post );
|
||
|
wp_cache_delete( 'lastpostmodified:gmt:' . $post_type, 'timeinfo' );
|
||
|
|
||
|
// None of our interest.
|
||
|
// If the post type is excluded in options, we can stop.
|
||
|
return 'nav_menu_item' === $post_type || ! Sitemap::is_object_indexable( $post->ID );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* While bulk importing, just save unique `post_types`.
|
||
|
*
|
||
|
* When importing is done, if we have a `post_type` that is saved in the sitemap
|
||
|
* try to ping the search engines
|
||
|
*
|
||
|
* @param string $new_status New post status.
|
||
|
* @param string $old_status Old post status.
|
||
|
* @param \WP_Post $post Post object.
|
||
|
*/
|
||
|
private function status_transition_bulk( $new_status, $old_status, $post ) {
|
||
|
$this->importing_post_types[] = get_post_type( $post );
|
||
|
$this->importing_post_types = array_unique( $this->importing_post_types );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* After import finished, walk through imported `post_types` and update info.
|
||
|
*/
|
||
|
public function status_transition_bulk_finished() {
|
||
|
if ( ! defined( 'WP_IMPORTING' ) || empty( $this->importing_post_types ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( false === $this->maybe_ping_search_engines() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( WP_CACHE ) {
|
||
|
do_action( 'rank_math/sitemap/hit_index' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if we can ping search engines.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private function maybe_ping_search_engines() {
|
||
|
$ping = false;
|
||
|
$accessible_post_types = Helper::get_accessible_post_types();
|
||
|
|
||
|
foreach ( $this->importing_post_types as $post_type ) {
|
||
|
wp_cache_delete( 'lastpostmodified:gmt:' . $post_type, 'timeinfo' );
|
||
|
|
||
|
if ( in_array( $post_type, $accessible_post_types, true ) ) {
|
||
|
$ping = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $ping;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Helper to invalidate in hooks where type is passed as second argument.
|
||
|
*
|
||
|
* @param int $unused Unused term ID value.
|
||
|
* @param string $taxonomy Taxonomy to invalidate.
|
||
|
*/
|
||
|
public static function invalidate_term( $unused, $taxonomy ) {
|
||
|
if ( false !== Helper::get_settings( 'sitemap.tax_' . $taxonomy . '_sitemap' ) ) {
|
||
|
self::invalidate( $taxonomy );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete cache transients for index and specific type.
|
||
|
*
|
||
|
* Always deletes the main index sitemaps cache, as that's always invalidated by any other change.
|
||
|
*
|
||
|
* @param string $type Sitemap type to invalidate.
|
||
|
*/
|
||
|
public static function invalidate( $type ) {
|
||
|
self::clear( [ $type ] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Invalidate sitemap cache for the post type of a post.
|
||
|
* Don't invalidate for revisions.
|
||
|
*
|
||
|
* @param int $post_id Post ID to invalidate type for.
|
||
|
*/
|
||
|
public static function invalidate_post( $post_id ) {
|
||
|
if ( wp_is_post_revision( $post_id ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
self::invalidate( get_post_type( $post_id ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Invalidate sitemap cache for authors.
|
||
|
*
|
||
|
* @param int $user_id User ID.
|
||
|
*/
|
||
|
public static function invalidate_author( $user_id ) {
|
||
|
$user = get_user_by( 'id', $user_id );
|
||
|
if ( 'user_register' === current_action() || 'profile_update' === current_action() ) {
|
||
|
update_user_meta( $user_id, 'last_update', time() );
|
||
|
}
|
||
|
|
||
|
if ( $user && ! is_null( $user->roles ) && ! in_array( 'subscriber', $user->roles, true ) ) {
|
||
|
self::invalidate( 'author' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function to clear the Sitemap Cache.
|
||
|
*
|
||
|
* @param string $object_type Object type for destination where to save.
|
||
|
* @param int $object_id Object id for destination where to save.
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public static function invalidate_object_type( $object_type, $object_id ) {
|
||
|
if ( 'post' === $object_type ) {
|
||
|
self::invalidate_post( $object_id );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( 'user' === $object_type ) {
|
||
|
self::invalidate_author( $object_id );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( 'term' === $object_type ) {
|
||
|
$term = get_term( $object_id );
|
||
|
self::invalidate_term( $object_id, $term->taxonomy );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete cache transients for given sitemaps types or all by default.
|
||
|
*
|
||
|
* @param array $types Set of sitemap types to delete cache transients for.
|
||
|
*/
|
||
|
public static function clear( $types = [] ) {
|
||
|
if ( ! Sitemap::is_cache_enabled() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// No types provided, clear all.
|
||
|
if ( empty( $types ) ) {
|
||
|
self::$clear_all = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Always invalidate the index sitemap as well.
|
||
|
if ( ! in_array( '1', $types, true ) ) {
|
||
|
array_unshift( $types, '1' );
|
||
|
}
|
||
|
|
||
|
foreach ( $types as $type ) {
|
||
|
if ( ! in_array( $type, self::$clear_types, true ) ) {
|
||
|
self::$clear_types[] = $type;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Invalidate storage for cache types queued to clear.
|
||
|
*/
|
||
|
public static function clear_queued() {
|
||
|
if ( self::$clear_all ) {
|
||
|
Cache::invalidate_storage();
|
||
|
self::$clear_all = false;
|
||
|
self::$clear_types = [];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
foreach ( self::$clear_types as $type ) {
|
||
|
Cache::invalidate_storage( $type );
|
||
|
}
|
||
|
|
||
|
self::$clear_types = [];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds a hook that when given option is updated, the cache is cleared.
|
||
|
*
|
||
|
* @param string $option Option name.
|
||
|
* @param string $type Sitemap type.
|
||
|
*/
|
||
|
public static function register_clear_on_option_update( $option, $type = '' ) {
|
||
|
self::$cache_clear[ $option ] = $type;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clears the transient cache when a given option is updated, if that option has been registered before.
|
||
|
*
|
||
|
* @param string $option The option name that's being updated.
|
||
|
*/
|
||
|
public static function clear_on_option_update( $option ) {
|
||
|
if ( ! array_key_exists( $option, self::$cache_clear ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Clear all caches.
|
||
|
if ( empty( self::$cache_clear[ $option ] ) ) {
|
||
|
self::clear();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Clear specific provided type(s).
|
||
|
$types = (array) self::$cache_clear[ $option ];
|
||
|
self::clear( $types );
|
||
|
}
|
||
|
}
|