ache Pro + WooCommerce = ❤️', 'redis-cache' ), sprintf( // translators: %s = Link to the plugin's settings screen. wp_kses_post( __( 'Object Cache Pro is a business class object cache that’s highly-optimized for WooCommerce to provide true reliability, peace of mind and faster load times for your store. Learn more »', 'redis-cache' ) ), esc_url( network_admin_url( $this->page ) ) ) ); } /** * Registers all hooks associated with the shutdown hook * * @return void */ public function register_shutdown_hooks() { if ( ! defined( 'WP_REDIS_DISABLE_COMMENT' ) || ! WP_REDIS_DISABLE_COMMENT ) { add_action( 'shutdown', [ $this, 'maybe_print_comment' ], 0 ); } } /** * Displays the redis cache html comment * * @return void */ public function maybe_print_comment() { /** @var \WP_Object_Cache $wp_object_cache */ global $wp_object_cache; if ( ( defined( 'WP_CLI' ) && WP_CLI ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || ( defined( 'JSON_REQUEST' ) && JSON_REQUEST ) || ( defined( 'IFRAME_REQUEST' ) && IFRAME_REQUEST ) || ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'WC_API_REQUEST' ) && WC_API_REQUEST ) ) { return; } if ( function_exists( 'wp_is_json_request' ) && wp_is_json_request() ) { return; } if ( ! isset( $wp_object_cache->cache, $wp_object_cache->diagnostics ) || ! is_array( $wp_object_cache->cache ) ) { return; } if ($this->incompatible_content_type()) { return; } $message = sprintf( 'Performance optimized by Redis Object Cache. Learn more: %s', 'https://wprediscache.com' ); if ( ! WP_DEBUG_DISPLAY ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped printf( "\n\n", $message ); return; } $bytes = strlen( serialize( $wp_object_cache->cache ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize $debug = sprintf( // translators: %1$d = number of objects. %2$s = human-readable size of cache. %3$s = name of the used client. __( 'Retrieved %1$d objects (%2$s) from Redis using %3$s.', 'redis-cache' ), $wp_object_cache->cache_hits, function_exists( 'size_format' ) ? size_format( $bytes ) : "{$bytes} bytes", $wp_object_cache->diagnostics['client'] ); printf( "\n", $message, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_html( $debug ) ); } /** * Whether incompatible content type headers were sent. * * @return bool */ protected function incompatible_content_type() { $jsonContentType = static function ($headers) { foreach ($headers as $header => $value) { if (stripos((string) $header, 'content-type') === false) { continue; } if (stripos((string) $value, '/json') === false) { continue; } return true; } return false; }; if (function_exists('headers_list')) { $headers = []; foreach (headers_list() as $header) { [$name, $value] = explode(':', $header); $headers[$name] = $value; } if ($jsonContentType($headers)) { return true; } } if (function_exists('apache_response_headers')) { if ($headers = apache_response_headers()) { return $jsonContentType($headers); } } return false; } /** * Initializes the WP filesystem API to be ready for use * * @param string $url The URL to post the form to. * @param bool $silent Whether to ask the user for credentials if necessary or not. * @return bool */ public function initialize_filesystem( $url, $silent = false ) { if ( $silent ) { ob_start(); } $credentials = request_filesystem_credentials( $url ); if ( false === $credentials ) { if ( $silent ) { ob_end_clean(); } return false; } if ( ! WP_Filesystem( $credentials ) ) { request_filesystem_credentials( $url ); if ( $silent ) { ob_end_clean(); } return false; } return true; } /** * Determines whether object cache file modifications are allowed. * * @return bool */ function is_file_mod_allowed() { return apply_filters( 'file_mod_allowed', ! defined( 'DISALLOW_FILE_MODS' ) || ! DISALLOW_FILE_MODS, 'object_cache_dropin' ); } /** * Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in * * @return true|WP_Error */ public function test_filesystem_writing() { /** @var \WP_Filesystem_Base $wp_filesystem */ global $wp_filesystem; if ( ! $this->is_file_mod_allowed() ) { return new WP_Error( 'disallowed', __( 'File modifications are not allowed.', 'redis-cache' ) ); } if ( ! $this->initialize_filesystem( '', true ) ) { return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'redis-cache' ) ); } $dropin_check = ! defined( 'WP_REDIS_DISABLE_DROPIN_CHECK' ) || ! WP_REDIS_DISABLE_DROPIN_CHECK; if ( ! $dropin_check ) { if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) { return true; } if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR . '/object-cache.php' ) ) { return new WP_Error( 'writable', __( 'Object cache drop-in is not writable.', 'redis-cache' ) ); } return true; } $cachefile = WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php'; $testfile = WP_CONTENT_DIR . '/object-cache.tmp'; if ( ! $wp_filesystem->exists( $cachefile ) ) { return new WP_Error( 'exists', __( 'Object cache file doesn’t exist.', 'redis-cache' ) ); } if ( $wp_filesystem->exists( $testfile ) ) { if ( ! $wp_filesystem->delete( $testfile ) ) { return new WP_Error( 'delete', __( 'Test file exists, but couldn’t be deleted.', 'redis-cache' ) ); } } if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) { return new WP_Error( 'writable', __( 'Content directory is not writable.', 'redis-cache' ) ); } if ( ! $wp_filesystem->copy( $cachefile, $testfile, true, FS_CHMOD_FILE ) ) { return new WP_Error( 'copy', __( 'Failed to copy test file.', 'redis-cache' ) ); } if ( ! $wp_filesystem->exists( $testfile ) ) { return new WP_Error( 'exists', __( 'Copied test file doesn’t exist.', 'redis-cache' ) ); } $meta = get_file_data( $testfile, [ 'Version' => 'Version' ] ); if ( $meta['Version'] !== WP_REDIS_VERSION ) { return new WP_Error( 'version', __( 'Couldn’t verify test file contents.', 'redis-cache' ) ); } if ( ! $wp_filesystem->delete( $testfile ) ) { return new WP_Error( 'delete', __( 'Copied test file couldn’t be deleted.', 'redis-cache' ) ); } return true; } /** * Calls the drop-in update method if necessary * * @return void */ public function maybe_update_dropin() { if ( defined( 'WP_REDIS_DISABLE_DROPIN_AUTOUPDATE' ) && WP_REDIS_DISABLE_DROPIN_AUTOUPDATE ) { return; } if ( $this->object_cache_dropin_outdated() ) { add_action( 'shutdown', [ $this, 'update_dropin' ] ); } } /** * Redirects to the plugin settings if the plugin was just activated * * @return void */ public function maybe_redirect() { if ( ! get_transient( '_rediscache_activation_redirect' ) ) { return; } if ( ! $this->current_user_can_manage_redis() ) { return; } delete_transient( '_rediscache_activation_redirect' ); wp_safe_redirect( network_admin_url( $this->page ) ); } /** * Updates the `object-cache.php` drop-in * * @return void */ public function update_dropin() { global $wp_filesystem; if ( ! $this->validate_object_cache_dropin() ) { return; } if ( $this->initialize_filesystem( '', true ) ) { $result = $wp_filesystem->copy( WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true, FS_CHMOD_FILE ); /** * Fires on cache enable event * * @since 1.3.5 * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful. */ do_action( 'redis_object_cache_update_dropin', $result ); } } /** * Plugin activation hook * * @return void */ public static function on_activation() { set_transient( '_rediscache_activation_redirect', 1, 30 ); } /** * Plugin deactivation hook * * @param string $plugin Plugin basename. * @return void */ public function on_deactivation( $plugin ) { global $wp_filesystem; ob_start(); if ( $plugin === WP_REDIS_BASENAME ) { $timestamp = wp_next_scheduled( 'rediscache_discard_metrics' ); if ( $timestamp ) { wp_unschedule_event( $timestamp, 'rediscache_discard_metrics' ); } (new Predis)->flush(); if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) { $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' ); } } ob_end_clean(); } /** * Helper method to retrieve a nonced plugin action link * * @param string $action The action to be executed once the link is followed. * @return string */ public function action_link( $action ) { if ( ! in_array( $action, $this->actions, true ) ) { return ''; } return wp_nonce_url( network_admin_url( add_query_arg( 'action', $action, $this->page ) ), $action ); } /** * Obscure `password` URL parameter. * * @param string $url * @return string */ public function obscure_url_secrets( $url ) { return preg_replace( '/password=[^&]+/', 'password=*****', (string) $url ); } /** * The capability required to manage Redis. * * @return string */ public function manage_redis_capability() { return is_multisite() ? 'manage_network_options' : 'manage_options'; } /** * Does the current user have the capability to manage Redis? * * @return bool */ public function current_user_can_manage_redis() { return current_user_can( $this->manage_redis_capability() ); } /** * Prevent LiteSpeed Cache from overwriting the `object-cache.php` drop-in. * * @return void */ public function litespeed_disable_objectcache() { if ( isset( $_POST['LSCWP_CTRL'], $_POST['LSCWP_NONCE'], $_POST['object'] ) ) { $_POST['object'] = '0'; } } /** * Returns `true` if the plugin was installed by AccelerateWP from CloudLinux. * * @param bool $ignore_banner_constant * @return bool */ public static function acceleratewp_install( $ignore_banner_constant = false ) { $path = defined( 'WP_REDIS_PATH' ) ? WP_REDIS_PATH : null; $scheme = defined( 'WP_REDIS_SCHEME' ) ? WP_REDIS_SCHEME : null; if ( $scheme === 'unix' && strpos( (string) $path, '.clwpos/redis.sock' ) !== false ) { if ( $ignore_banner_constant ) { return true; } else { return defined( 'WP_REDIS_DISABLE_BANNERS' ) && WP_REDIS_DISABLE_BANNERS; } } return false; } }