diff --git a/wp-content/mu-plugins/object-cache-bootstrap.php b/wp-content/mu-plugins/object-cache-bootstrap.php index e22f05b..a8f4f48 100644 --- a/wp-content/mu-plugins/object-cache-bootstrap.php +++ b/wp-content/mu-plugins/object-cache-bootstrap.php @@ -3,162 +3,175 @@ * MU-Plugin: Object Cache Bootstrap * * Автоопределение backend кеширования + versioned cache + автоинвалидация. - * Dev → Redis (через redis-cache plugin + Predis). - * Prod → Memcached (через Memcached Object Cache plugin, устанавливается вручную). + * Dev → Redis (через redis-cache plugin). + * Prod → Memcached (через Memcached Object Cache plugin). + * Работает с любым active drop-in (object-cache.php). * * @package Dekart + * @version 2.0 */ // ============================================================ -// 1. АВТООПРЕДЕЛЕНИЕ BACKEND +// 1. КОНСТАНТЫ ДЛЯ ПЛАГИНОВ (выполняется до загрузки WP) +// ============================================================ + +// Redis: константы для redis-cache plugin +if ( ! defined( 'WP_REDIS_HOST' ) ) { + define( 'WP_REDIS_HOST', '127.0.0.1' ); +} +if ( ! defined( 'WP_REDIS_PORT' ) ) { + define( 'WP_REDIS_PORT', 6379 ); +} +if ( ! defined( 'WP_REDIS_DATABASE' ) ) { + define( 'WP_REDIS_DATABASE', 0 ); +} + +// ============================================================ +// 2. АВТООПРЕДЕЛЕНИЕ BACKEND // ============================================================ /** - * Определить доступный backend для object cache. + * Определить активный backend object cache. * * @return string 'memcached' | 'redis' | 'none' */ function dekart_detect_cache_backend(): string { - // 1. Приоритет: Memcached (проверка порта) - $memcached_sock = @fsockopen( '127.0.0.1', 11211, $errno, $errstr, 0.5 ); - if ( $memcached_sock ) { - fclose( $memcached_sock ); - return 'memcached'; + // 1. По active drop-in: Memcached Object Cache + if ( class_exists( 'Memcache' ) || class_exists( 'Memcached' ) ) { + $mc_sock = @fsockopen( '127.0.0.1', 11211, $errno, $errstr, 0.5 ); + if ( $mc_sock ) { + fclose( $mc_sock ); + return 'memcached'; + } } - // 2. Redis: проверяем через Predis (установлен composer) + // 2. По active drop-in: Redis (через Predis от redis-cache) if ( class_exists( 'Predis\Client' ) ) { - $redis_sock = @fsockopen( '127.0.0.1', 6379, $errno, $errstr, 0.5 ); - if ( $redis_sock ) { - fclose( $redis_sock ); + $r_sock = @fsockopen( '127.0.0.1', 6379, $errno, $errstr, 0.5 ); + if ( $r_sock ) { + fclose( $r_sock ); return 'redis'; } } - // 3. Ничего не доступно + // 3. Есть object-cache.php, но сервис не отвечает + if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) { + return 'dropin-no-connection'; + } + return 'none'; } // ============================================================ -// 2. КОНСТАНТЫ ДЛЯ ПЛАГИНОВ -// ============================================================ - -$backend = dekart_detect_cache_backend(); - -// Redis: define константы для redis-cache plugin -if ( 'redis' === $backend ) { - if ( ! defined( 'WP_REDIS_HOST' ) ) { - define( 'WP_REDIS_HOST', '127.0.0.1' ); - } - if ( ! defined( 'WP_REDIS_PORT' ) ) { - define( 'WP_REDIS_PORT', 6379 ); - } - if ( ! defined( 'WP_REDIS_DATABASE' ) ) { - define( 'WP_REDIS_DATABASE', 0 ); - } -} - -// ============================================================ -// 3. ЛОГИРОВАНИЕ (dev only) -// ============================================================ - -if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - error_log( '[Object Cache] Backend detected: ' . $backend ); - if ( 'none' === $backend ) { - error_log( '[Object Cache] WARNING: No cache backend available. Site will run without object cache.' ); - } -} - -// ============================================================ -// 4. VERSIONED CACHE KEY +// 3. VERSIONED CACHE KEY // ============================================================ /** - * Получить текущую версию кеша. + * Текущая версия кеша. */ function my_cache_version(): int { return (int) get_option( 'my_cache_version', 1 ); } /** - * Обернуть ключ кеша с версией. - * Позволяет инвалидировать весь кеш без flush — достаточно изменить версию. + * Обернуть ключ с префиксом версии. + * Инвалидация: достаточно изменить версию. * * @param string $key Исходный ключ. - * @return string Ключ с префиксом версии. + * @return string */ function my_cache_key( string $key ): string { return 'v:' . my_cache_version() . ':' . $key; } // ============================================================ -// 5. ИНВАЛИДАЦИЯ КЕША +// 4. ИНВАЛИДАЦИЯ КЕША (version bump + flush) // ============================================================ /** - * Увеличить версию кеша + сбросить object cache. - * Все кешированные данные сбрасываются — при следующем запросе читаются свежие. + * Сбросить object cache + увеличить версию. + * Безопасно от рекурсии — static guard. */ function my_cache_bump_version(): void { - // Сбрасываем object cache (Redis / Memcached) - wp_cache_flush(); + static $running = false; + if ( $running ) { + return; + } + $running = true; - // Обновляем версию для my_cache_key() + wp_cache_flush(); $v = my_cache_version(); update_option( 'my_cache_version', $v + 1, false ); - if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - error_log( sprintf( '[Object Cache] Flushed + version bumped: %d → %d', $v, $v + 1 ) ); - } + $running = false; } -// ============================================================ -// 6. ХУКИ АВТОИНВАЛИДАЦИИ -// ============================================================ +// branch_* опции → сброс кеша (настройки филиала) +add_action( 'update_option', function ( $option ) { + if ( str_starts_with( $option, 'branch_' ) ) { + my_cache_bump_version(); + } +}, 10, 1 ); -// Опции: при любом изменении сбрасываем версию -add_action( 'update_option', 'my_cache_bump_version' ); -add_action( 'added_option', 'my_cache_bump_version' ); -add_action( 'deleted_option', 'my_cache_bump_version' ); - -// Посты: при сохранении (включая ACF, Carbon Fields) +// Посты (включая Carbon Fields) add_action( 'save_post', 'my_cache_bump_version' ); - -// Carbon Fields: сохранение мета-боксов add_action( 'carbon_fields_post_meta_container_saved', 'my_cache_bump_version' ); // ============================================================ -// 7. ХЕЛПЕР: ПРОМЫВКА КЕША ИЗ АДМИНКИ -// ============================================================ - -/** - * Добавить кнопку "Сбросить кеш" в админ-бар (только для админов). - */ -add_action( 'admin_bar_menu', function ( WP_Admin_Bar $wp_admin_bar ) { - if ( ! current_user_can( 'manage_options' ) ) { - return; - } - - // Показываем текущий backend - $backend_label = 'none' === $backend ? 'no-cache' : $backend; - $wp_admin_bar->add_node( array( - 'id' => 'dekart-cache-status', - 'title' => sprintf( - 'Cache: %s | v%s', - $backend_label, - my_cache_version() - ), - 'href' => '#', - ) ); -}, 100 ); - -// ============================================================ -// 8. WP-CLI: командa для сброса версии +// 5. WP-CLI: установка/сброс кеша // ============================================================ if ( defined( 'WP_CLI' ) && WP_CLI ) { + + /** + * Сброс версии кеша. + */ WP_CLI::add_command( 'dekart cache-flush', function () { my_cache_bump_version(); - WP_CLI::success( 'Cache version bumped. All versioned cache keys are invalidated.' ); + WP_CLI::success( 'Cache flushed + version bumped.' ); + } ); + + /** + * Установить drop-in Memcached Object Cache. + * + * Копирует object-cache.php из плагина memcached в wp-content/. + * Использовать ПОСЛЕ удаления Redis-плагина. + */ + WP_CLI::add_command( 'dekart cache-memcached', function () { + $source = WP_CONTENT_DIR . '/plugins/memcached/object-cache.php'; + $target = WP_CONTENT_DIR . '/object-cache.php'; + + if ( ! file_exists( $source ) ) { + WP_CLI::error( 'Memcached Object Cache plugin not installed. Run: wp plugin install memcached' ); + return; + } + + if ( ! class_exists( 'Memcache' ) && ! class_exists( 'Memcached' ) ) { + WP_CLI::warning( 'PHP memcache/memcached extension not found.' ); + WP_CLI::line( 'Install it on your server first, then run this command again.' ); + WP_CLI::line( 'Debian/Ubuntu: sudo apt install php-memcache' ); + WP_CLI::line( 'After install, copy the drop-in manually:' ); + WP_CLI::line( " cp {$source} {$target}" ); + return; + } + + if ( copy( $source, $target ) ) { + WP_CLI::success( 'Memcached drop-in installed to wp-content/object-cache.php' ); + } else { + WP_CLI::error( 'Failed to copy drop-in. Check file permissions.' ); + } } ); } + +// ============================================================ +// 6. ЛОГИРОВАНИЕ (dev only) +// ============================================================ + +if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { + add_action( 'init', function () { + $b = dekart_detect_cache_backend(); + if ( 'none' === $b ) { + error_log( '[Object Cache] WARNING: No cache backend available.' ); + } + }, 0 ); +}