272 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			272 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * Handle sitemap caching and invalidation.
 | 
						|
 *
 | 
						|
 * @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\Admin\Database\Database;
 | 
						|
 | 
						|
defined( 'ABSPATH' ) || exit;
 | 
						|
 | 
						|
/**
 | 
						|
 * Cache class.
 | 
						|
 */
 | 
						|
class Cache {
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Cache mode.
 | 
						|
	 *
 | 
						|
	 * @var string
 | 
						|
	 */
 | 
						|
	private $mode = 'db';
 | 
						|
 | 
						|
	/**
 | 
						|
	 * The $wp_filesystem object.
 | 
						|
	 *
 | 
						|
	 * @var object WP_Filesystem
 | 
						|
	 */
 | 
						|
	private $wp_filesystem;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Prefix of the filename for sitemap caches.
 | 
						|
	 *
 | 
						|
	 * @var string
 | 
						|
	 */
 | 
						|
	const STORAGE_KEY_PREFIX = 'rank_math_';
 | 
						|
 | 
						|
	/**
 | 
						|
	 * The constructor.
 | 
						|
	 */
 | 
						|
	public function __construct() {
 | 
						|
		$this->wp_filesystem = Helper::get_filesystem();
 | 
						|
		$this->mode          = $this->is_writable() ? 'file' : 'db';
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Change sitemap caching mode (can be "file" or "db").
 | 
						|
		 */
 | 
						|
		$this->mode = apply_filters( 'rank_math/sitemap/cache_mode', $this->mode );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Is the file writable?
 | 
						|
	 *
 | 
						|
	 * @return bool
 | 
						|
	 */
 | 
						|
	public function is_writable() {
 | 
						|
		if ( is_null( $this->wp_filesystem ) || ! Helper::is_filesystem_direct() ) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		$directory_separator = '/';
 | 
						|
		$folder_path         = $this->get_cache_directory();
 | 
						|
		$test_file           = $folder_path . $this->get_storage_key();
 | 
						|
 | 
						|
		// If folder doesn't exist?
 | 
						|
		if ( ! file_exists( $folder_path ) ) {
 | 
						|
			// Can we create the folder?
 | 
						|
			// returns true if yes and false if not.
 | 
						|
			$permissions = ( defined( 'FS_CHMOD_DIR' ) ) ? FS_CHMOD_DIR : 0755;
 | 
						|
			return $this->wp_filesystem->mkdir( $folder_path, $permissions );
 | 
						|
		}
 | 
						|
 | 
						|
		// Does the file exist?
 | 
						|
		// File exists. Is it writable?
 | 
						|
		if ( file_exists( $test_file ) && ! $this->wp_filesystem->is_writable( $test_file ) ) {
 | 
						|
			// Nope, it's not writable.
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		// Folder exists, but is it actually writable?
 | 
						|
		return $this->wp_filesystem->is_writable( $folder_path );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Get the sitemap that is cached.
 | 
						|
	 *
 | 
						|
	 * @param  string $type Sitemap type.
 | 
						|
	 * @param  int    $page Page number to retrieve.
 | 
						|
	 * @param  bool   $html Is HTML sitemap.
 | 
						|
	 * @return false|string false on no cache found otherwise sitemap file.
 | 
						|
	 */
 | 
						|
	public function get_sitemap( $type, $page, $html = false ) {
 | 
						|
		$filename = $this->get_storage_key( $type, $page, $html );
 | 
						|
		if ( false === $filename || is_null( $this->wp_filesystem ) ) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		$path = self::get_cache_directory() . $filename;
 | 
						|
		if ( 'file' === $this->mode
 | 
						|
			&& is_a( $this->wp_filesystem, 'WP_Filesystem_Direct' )
 | 
						|
			&& $this->wp_filesystem->exists( $path ) ) {
 | 
						|
			return $this->wp_filesystem->get_contents( $path );
 | 
						|
		}
 | 
						|
 | 
						|
		$filename = "sitemap_{$type}_$filename";
 | 
						|
		$sitemap  = get_transient( $filename );
 | 
						|
		return maybe_unserialize( $sitemap );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Store the sitemap page from cache.
 | 
						|
	 *
 | 
						|
	 * @param  string $type    Sitemap type.
 | 
						|
	 * @param  int    $page    Page number to store.
 | 
						|
	 * @param  string $sitemap Sitemap body to store.
 | 
						|
	 * @param  bool   $html    Is HTML sitemap.
 | 
						|
	 * @return boolean
 | 
						|
	 */
 | 
						|
	public function store_sitemap( $type, $page, $sitemap, $html = false ) {
 | 
						|
		$filename = $this->get_storage_key( $type, $page, $html );
 | 
						|
		if ( false === $filename || is_null( $this->wp_filesystem ) ) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( 'file' === $this->mode ) {
 | 
						|
			$stored = $this->wp_filesystem->put_contents( self::get_cache_directory() . $filename, $sitemap, FS_CHMOD_FILE );
 | 
						|
			if ( true === $stored ) {
 | 
						|
				self::cached_files( $filename, $type );
 | 
						|
				return $stored;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		$filename = "sitemap_{$type}_$filename";
 | 
						|
		return set_transient( $filename, maybe_serialize( $sitemap ), DAY_IN_SECONDS * 100 );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Get filename for sitemap.
 | 
						|
	 *
 | 
						|
	 * @param  null|string $type The type to get the key for. Null or '1' for index cache.
 | 
						|
	 * @param  int         $page The page of cache to get the key for.
 | 
						|
	 * @param  boolean     $html Whether to add html extension.
 | 
						|
	 * @return boolean|string The key where the cache is stored on. False if the key could not be generated.
 | 
						|
	 */
 | 
						|
	private function get_storage_key( $type = null, $page = 1, $html = false ) {
 | 
						|
		$type = is_null( $type ) ? '1' : $type;
 | 
						|
 | 
						|
		$filename = self::STORAGE_KEY_PREFIX . md5( "{$type}_{$page}_" . home_url() ) . '.' . ( $html ? 'html' : 'xml' );
 | 
						|
 | 
						|
		return $filename;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Get cache directory.
 | 
						|
	 *
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	public static function get_cache_directory() {
 | 
						|
		$dir     = wp_upload_dir();
 | 
						|
		$default = $dir['basedir'] . '/rank-math';
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Filter XML sitemap cache directory.
 | 
						|
		 *
 | 
						|
		 * @param string $unsigned Default cache directory
 | 
						|
		 */
 | 
						|
		$filtered = apply_filters( 'rank_math/sitemap/cache_directory', $default );
 | 
						|
 | 
						|
		if ( ! is_string( $filtered ) || '' === $filtered ) {
 | 
						|
			$filtered = $default;
 | 
						|
		}
 | 
						|
 | 
						|
		return trailingslashit( $filtered );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Read/Write cached files.
 | 
						|
	 *
 | 
						|
	 * @param  mixed  $value Pass null to get option,
 | 
						|
	 *                       Pass false to delete option,
 | 
						|
	 *                       Pass value to update option.
 | 
						|
	 * @param  string $type  Sitemap type.
 | 
						|
	 * @return mixed
 | 
						|
	 */
 | 
						|
	public static function cached_files( $value = null, $type = '' ) {
 | 
						|
		if ( '' !== $type ) {
 | 
						|
			$options           = Helper::option( 'sitemap_cache_files' );
 | 
						|
			$options[ $value ] = $type;
 | 
						|
			return Helper::option( 'sitemap_cache_files', $options );
 | 
						|
		}
 | 
						|
 | 
						|
		return Helper::option( 'sitemap_cache_files', $value );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Invalidate sitemap cache.
 | 
						|
	 *
 | 
						|
	 * @param null|string $type The type to get the key for. Null for all caches.
 | 
						|
	 */
 | 
						|
	public static function invalidate_storage( $type = null ) {
 | 
						|
		/**
 | 
						|
		 * Filter: 'rank_math/sitemap/invalidate_storage' - Allow developers to disable sitemap cache invalidation.
 | 
						|
		 */
 | 
						|
		if ( ! apply_filters( 'rank_math/sitemap/invalidate_storage', true, $type ) ) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		$wp_filesystem = Helper::get_filesystem();
 | 
						|
		if ( is_null( $wp_filesystem ) ) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		$directory = self::get_cache_directory();
 | 
						|
 | 
						|
		if ( is_null( $type ) ) {
 | 
						|
			$wp_filesystem->delete( $directory, true );
 | 
						|
			wp_mkdir_p( $directory );
 | 
						|
			self::clear_transients();
 | 
						|
			self::cached_files( false );
 | 
						|
			Helper::clear_cache( 'sitemap' );
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		$data  = [];
 | 
						|
		$files = self::cached_files();
 | 
						|
		foreach ( $files as $file => $sitemap_type ) {
 | 
						|
			if ( $type !== $sitemap_type ) {
 | 
						|
				$data[ $file ] = $sitemap_type;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			$wp_filesystem->delete( $directory . $file );
 | 
						|
		}
 | 
						|
 | 
						|
		self::clear_transients( $type );
 | 
						|
		self::cached_files( $data );
 | 
						|
		Helper::clear_cache( 'sitemap/' . $type );
 | 
						|
 | 
						|
		/**
 | 
						|
		 * Action: 'rank_math/sitemap/invalidated_storage' - Runs after sitemap cache invalidation.
 | 
						|
		 */
 | 
						|
		do_action( 'rank_math/sitemap/invalidated_storage', $type );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Reset ALL transient caches.
 | 
						|
	 *
 | 
						|
	 * @param null|string $type The type to get the key for. Null for all caches.
 | 
						|
	 */
 | 
						|
	private static function clear_transients( $type = null ) {
 | 
						|
 | 
						|
		if ( is_null( $type ) ) {
 | 
						|
			return Database::table( 'options' )
 | 
						|
				->whereLike( 'option_name', '_transient_sitemap_' )
 | 
						|
				->delete();
 | 
						|
		}
 | 
						|
 | 
						|
		return Database::table( 'options' )
 | 
						|
			->whereLike( 'option_name', '_transient_sitemap_' . $type )
 | 
						|
			->delete();
 | 
						|
	}
 | 
						|
}
 |