shkola/wp-content/mu-plugins/object-cache-bootstrap.php
Dekart Deploy Bot a6222a31ab fix: restore MU-plugin cache logic with WP_CACHE_KEY_SALT isolation
- Root cause: two prod sites share one Memcached, no WP_CACHE_KEY_SALT
  → cache keys collide → data mixes between sites
- Primary fix: WP_CACHE_KEY_SALT defined via DB_NAME in wp-config.php
  (manual on each prod, file is in .gitignore)
- MU-plugin restored: auto-detect backend, versioned keys, auto-invalidation
- Added re-salt fallback via salt_keys(DB_NAME) on muplugins_loaded
- Removed duplicate admin_notice hooks
2026-07-17 20:07:16 +00:00

233 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* MU-Plugin: Object Cache Bootstrap
*
* Поддержка объектного кеша (Memcached/Redis) с изоляцией по сайтам.
*
* ИЗОЛЯЦИЯ КЛЮЧЕЙ (защита от смешивания данных между сайтами):
* WP_CACHE_KEY_SALT задаётся в wp-config.php на основе DB_NAME.
* Это гарантирует уникальные ключи для каждого сайта ДО загрузки
* object-cache.php (drop-in). Если в wp-config.php соль не задана,
* MU-плагин пробует выставить её через fallback + re-salt хаки.
*
* УСТАНОВКА MEMCACHED НА ПРОДЕ:
* 1. Установить PHP-расширение: sudo apt install php-memcache
* 2. wp plugin install memcached
* 3. Скопировать drop-in:
* cp wp-content/plugins/memcached/object-cache.php wp-content/object-cache.php
* 4. Плагин НЕ активировать.
*
* @package Dekart
* @version 2.3
*/
// ============================================================
// 1. КОНСТАНТЫ — fallback, если не заданы в wp-config.php
// ============================================================
// WP_CACHE_KEY_SALT должна быть определена в wp-config.php.
// Если нет — пробуем задать здесь (хуже, но хоть что-то).
if ( ! defined( 'WP_CACHE_KEY_SALT' ) && defined( 'DB_NAME' ) ) {
define( 'WP_CACHE_KEY_SALT', DB_NAME );
}
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 );
}
// Re-salt для Memcached drop-in: WP_CACHE_KEY_SALT в MU-плагине
// слишком поздна (object-cache.php грузится раньше MU).
// Если у object-cache.php есть salt_keys() — вызываем напрямую.
if ( defined( 'DB_NAME' ) ) {
add_action( 'muplugins_loaded', function () {
global $wp_object_cache;
if ( isset( $wp_object_cache ) && method_exists( $wp_object_cache, 'salt_keys' ) ) {
$wp_object_cache->salt_keys( DB_NAME );
}
}, 0 );
}
// ============================================================
// 2. АВТООПРЕДЕЛЕНИЕ BACKEND
// ============================================================
function dekart_detect_cache_backend(): string {
if ( class_exists( 'Memcache' ) || class_exists( 'Memcached' ) ) {
$sock = @fsockopen( '127.0.0.1', 11211, $errno, $errstr, 0.5 );
if ( $sock ) {
fclose( $sock );
return 'memcached';
}
}
if ( class_exists( 'Predis\Client' ) ) {
$sock = @fsockopen( '127.0.0.1', 6379, $errno, $errstr, 0.5 );
if ( $sock ) {
fclose( $sock );
return 'redis';
}
}
if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
return 'dropin-no-connection';
}
return 'none';
}
// ============================================================
// 3. VERSIONED CACHE KEY
// ============================================================
function my_cache_version(): int {
return (int) get_option( 'my_cache_version', 1 );
}
function my_cache_key( string $key ): string {
return 'v:' . my_cache_version() . ':' . $key;
}
// ============================================================
// 4. ИНВАЛИДАЦИЯ КЕША
// ============================================================
function my_cache_bump_version(): void {
static $running = false;
if ( $running ) {
return;
}
$running = true;
wp_cache_flush();
$v = my_cache_version();
update_option( 'my_cache_version', $v + 1, false );
$running = false;
}
add_action( 'update_option', function ( $option ) {
if ( str_starts_with( $option, 'branch_' ) ) {
my_cache_bump_version();
}
}, 10, 1 );
add_action( 'save_post', 'my_cache_bump_version' );
add_action( 'carbon_fields_post_meta_container_saved', 'my_cache_bump_version' );
// ============================================================
// 5. АДМИН-НОТИСЫ
// ============================================================
/**
* Убрать ссылку "Активировать" у плагина Memcached Object Cache.
* Плагин — только источник object-cache.php, активация вызывает фатальную ошибку.
*/
add_filter( 'plugin_action_links_memcached/object-cache.php', function ( $actions ) {
unset( $actions['activate'] );
$actions['info'] = '<span style="color:#999">⚠ установите drop-in вручную</span>';
return $actions;
} );
add_action( 'admin_notices', function () {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$source = WP_CONTENT_DIR . '/plugins/memcached/object-cache.php';
$target = WP_CONTENT_DIR . '/object-cache.php';
if ( ! file_exists( $source ) ) {
return; // плагин не установлен
}
if ( file_exists( $target ) ) {
return; // drop-in уже стоит
}
if ( class_exists( 'Memcache' ) || class_exists( 'Memcached' ) ) {
$conn = @fsockopen( '127.0.0.1', 11211, $errno, $errstr, 0.5 );
if ( $conn ) {
fclose( $conn );
printf(
'<div class="notice notice-info is-dismissible"><p>%s</p></div>',
'<strong>Кеширование:</strong> Memcached доступен. Установите drop-in командой:
<code>cp ' . esc_html( str_replace( ABSPATH, '', $source ) ) . ' ' . esc_html( str_replace( ABSPATH, '', $target ) ) . '</code>
или через WP-CLI: <code>wp dekart cache-memcached</code>'
);
return;
}
printf(
'<div class="notice notice-warning is-dismissible"><p>%s</p></div>',
'<strong>Кеширование:</strong> PHP-расширение Memcache установлено, но сервер Memcached (127.0.0.1:11211) не отвечает.
Запустите Memcached на сервере или обратитесь в хостинг.'
);
return;
}
printf(
'<div class="notice notice-warning is-dismissible"><p>%s</p></div>',
'<strong>Кеширование:</strong> Плагин Memcached Object Cache установлен, но PHP-расширение <code>memcache</code> не найдено.
Установите его на сервере (через панель хостинга: PHP → Расширения → memcache).
<strong>Не активируйте плагин</strong> — он работает как drop-in.'
);
} );
// ============================================================
// 6. WP-CLI
// ============================================================
if ( defined( 'WP_CLI' ) && WP_CLI ) {
WP_CLI::add_command( 'dekart cache-flush', function () {
my_cache_bump_version();
WP_CLI::success( 'Cache flushed + version bumped.' );
} );
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 ( file_exists( $target ) ) {
WP_CLI::success( 'Drop-in already exists at wp-content/object-cache.php' );
return;
}
if ( ! class_exists( 'Memcache' ) && ! class_exists( 'Memcached' ) ) {
WP_CLI::warning( 'PHP memcache extension not found. Install it:' );
WP_CLI::line( ' sudo apt install php-memcache' );
WP_CLI::line( ' sudo systemctl restart php8.3-fpm' );
WP_CLI::line( 'Then run this command again.' );
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 permissions.' );
}
} );
}
// ============================================================
// 7. ЛОГИРОВАНИЕ
// ============================================================
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 );
}