|
| 1 | +<?php |
| 2 | +/* |
| 3 | +Plugin Name: No transients |
| 4 | +Version: 1.0.0 |
| 5 | +Plugin URI: https://beapi.fr |
| 6 | +Author: Be API |
| 7 | +Author URI: https://beapi.fr |
| 8 | +
|
| 9 | +---- |
| 10 | +
|
| 11 | +Copyright 2025 Be API Technical team ([email protected]) |
| 12 | +
|
| 13 | +This program is free software; you can redistribute it and/or modify |
| 14 | +it under the terms of the GNU General Public License as published by |
| 15 | +the Free Software Foundation; either version 2 of the License, or |
| 16 | +(at your option) any later version. |
| 17 | +
|
| 18 | +This program is distributed in the hope that it will be useful, |
| 19 | +but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 21 | +GNU General Public License for more details. |
| 22 | +
|
| 23 | +You should have received a copy of the GNU General Public License |
| 24 | +along with this program; if not, write to the Free Software |
| 25 | +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 26 | +*/ |
| 27 | + |
| 28 | +namespace BeAPI; |
| 29 | + |
| 30 | +if ( ! defined( 'ABSPATH' ) ) { |
| 31 | + exit; |
| 32 | +} |
| 33 | +/** |
| 34 | + * Class to prevent transients from being stored in the database |
| 35 | + * since we're using Redis for object caching |
| 36 | + */ |
| 37 | +class NoTransients { |
| 38 | + /** |
| 39 | + * Initialize the class and set up hooks |
| 40 | + */ |
| 41 | + public function __construct() { |
| 42 | + |
| 43 | + |
| 44 | + // Prevent transients from being retrieved |
| 45 | + add_filter( 'pre_option', [ $this, 'prevent_transient_retrieval' ], 10, 2 ); |
| 46 | + |
| 47 | + // Remove transients from alloptions |
| 48 | + add_filter( 'alloptions', [ $this, 'remove_transients_from_alloptions' ] ); |
| 49 | + |
| 50 | + // Prevent new transients from being added |
| 51 | + add_action( 'added_option', [ $this, 'delete_transient_option' ] ); |
| 52 | + |
| 53 | + // Prevent transients from being updated |
| 54 | + add_action( 'updated_option', [ $this, 'delete_transient_option' ] ); |
| 55 | + |
| 56 | + // Set up cleanup cron job |
| 57 | + add_action( 'init', [ $this, 'setup_cleanup_cron' ] ); |
| 58 | + |
| 59 | + // Show admin notice if no external object cache is being used |
| 60 | + add_action( 'admin_notices', [ $this, 'show_admin_notice' ] ); |
| 61 | + |
| 62 | + add_action( 'beapi_clean_transients', [ $this, 'cleanup_transients' ] ); |
| 63 | + } |
| 64 | + |
| 65 | + /** |
| 66 | + * Make transients appear expired by returning 0 for timeout options |
| 67 | + * |
| 68 | + * @param mixed $pre The pre-option value |
| 69 | + * @param string $option The option name |
| 70 | + * @return mixed The modified pre-option value |
| 71 | + */ |
| 72 | + public function prevent_transient_retrieval( $pre, $option ) { |
| 73 | + // If it's a transient timeout, return 0 to make it appear expired |
| 74 | + if ( str_starts_with( $option, '_transient_timeout' ) ) { |
| 75 | + return 0; |
| 76 | + } |
| 77 | + |
| 78 | + return $pre; |
| 79 | + } |
| 80 | + |
| 81 | + /** |
| 82 | + * Remove transients from alloptions array and delete them from database |
| 83 | + * This is because alloptions is called into the get_transient function, and expiration is not checked if the transient is in alloptions. |
| 84 | + * |
| 85 | + * @param array $alloptions The array of all autoloaded options |
| 86 | + * @return array The filtered options array |
| 87 | + */ |
| 88 | + public function remove_transients_from_alloptions( $alloptions ) { |
| 89 | + foreach ( $alloptions as $option => $value ) { |
| 90 | + if ( ! str_starts_with( $option, '_transient' ) ) { |
| 91 | + continue; |
| 92 | + } |
| 93 | + |
| 94 | + unset( $alloptions[ $option ] ); |
| 95 | + delete_option( $option ); |
| 96 | + } |
| 97 | + |
| 98 | + return $alloptions; |
| 99 | + } |
| 100 | + |
| 101 | + /** |
| 102 | + * Delete transient options when they're added or updated |
| 103 | + * |
| 104 | + * @param string $option The option name |
| 105 | + * @param mixed $value The option value |
| 106 | + */ |
| 107 | + public function delete_transient_option( $option ) { |
| 108 | + if ( str_starts_with( $option, '_transient' ) ) { |
| 109 | + delete_option( $option ); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + /** |
| 114 | + * Set up weekly cron job to clean up transients |
| 115 | + */ |
| 116 | + public function setup_cleanup_cron() { |
| 117 | + if ( ! wp_next_scheduled( 'beapi_clean_transients' ) ) { |
| 118 | + wp_schedule_event( time(), 'weekly', 'beapi_clean_transients' ); |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + /** |
| 123 | + * Clean up all transients from the database |
| 124 | + */ |
| 125 | + public function cleanup_transients() { |
| 126 | + global $wpdb; |
| 127 | + |
| 128 | + // Delete all transients |
| 129 | + $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_%'" ); |
| 130 | + } |
| 131 | + |
| 132 | + /** |
| 133 | + * Show admin notice if no external object cache is being used |
| 134 | + * The removal of transients when not object cache is a big performance issue |
| 135 | + */ |
| 136 | + public function show_admin_notice() { |
| 137 | + if ( wp_using_ext_object_cache() || ! current_user_can( 'manage_options' ) ) { |
| 138 | + return; |
| 139 | + } |
| 140 | + |
| 141 | + echo '<div class="notice notice-error"><p style="font-size: 40px;">Beware transients are not written in database. Please remove <strong>' . esc_html( wp_basename( __FILE__ ) ) . '</strong> from the mu-plugins folder.</p></div>'; |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +// Initialize the class |
| 146 | +new NoTransients(); |
0 commit comments