feat: detailed cache diagnostics with file checks

- AJAX returns structured log array (type + text per step)
- Checks: all system files, PHP extension, drop-in status,
  WP_CACHE_KEY_SALT, versioned cache, object cache, transients
- Drop-in auto-copy with success/fail/permission diagnostics
- JS renders log as color-coded list with summary line
- Fix for previous commit: plugin memcached not found typo
This commit is contained in:
Dekart Deploy Bot 2026-07-17 20:52:38 +00:00
parent 60a0847501
commit 7566f4da53

View File

@ -2323,25 +2323,34 @@ function dekart_flush_cache_page() {
?>
<div class="wrap">
<h1>🗄️ Сброс кеша</h1>
<p class="desc" style="margin:16px 0;color:#646970;font-size:13px">Полная очистка объектного кеша (Memcached/Redis), инвалидация versioned cache ключей, сброс транзиентов.<br><br>
Если drop-in Memcached ещё не установлен кнопка автоматически скопирует его из плагина. Активировать плагин Memcached не нужно.</p>
<p class="desc" style="margin:16px 0;color:#646970;font-size:13px">Полная диагностика и очистка: проверка файлов, установка drop-in, сброс кеша и транзиентов.</p>
<button type="button" id="dekart-flush-cache-btn" class="button button-hero button-secondary" style="display:inline-flex;align-items:center;gap:8px">
<span class="dashicons dashicons-update" style="font-size:20px;width:20px;height:20px"></span> Сбросить кэш сейчас
<span class="dashicons dashicons-update" style="font-size:20px;width:20px;height:20px"></span> Выполнить проверку и сброс
</button>
<span id="dekart-flush-cache-status" style="margin-left:16px;display:none"></span>
<div id="dekart-flush-cache-log" style="margin-top:20px;max-width:700px;display:none"></div>
<script>
(function() {
var btn = document.getElementById('dekart-flush-cache-btn');
var status = document.getElementById('dekart-flush-cache-status');
var log = document.getElementById('dekart-flush-cache-log');
if (!btn) return;
function addLog(type, text) {
var icons = { ok: '✅', fail: '❌', warn: '⚠️', info: '', doing: '🔄' };
var colors = { ok: '#00a32a', fail: '#d63638', warn: '#dba617', info: '#2271b1', doing: '#646970' };
var line = document.createElement('div');
line.style.cssText = 'padding:6px 10px;margin:2px 0;border-radius:4px;background:#f6f7f7;font-size:13px;line-height:1.5';
line.innerHTML = (icons[type] || '') + ' <span style="color:' + (colors[type] || '#333') + '">' + text + '</span>';
log.appendChild(line);
}
btn.addEventListener('click', function() {
if (btn.disabled) return;
btn.disabled = true;
btn.innerHTML = '<span style="display:inline-block;animation:spin 1s linear infinite">↻</span> Сброс...';
status.style.display = 'inline';
status.innerHTML = '⏳';
status.style.color = '#646970';
btn.innerHTML = '<span style="display:inline-block;animation:spin 1s linear infinite">↻</span> Выполнение...';
log.style.display = 'block';
log.innerHTML = '';
addLog('doing', 'Запуск диагностики...');
var data = new FormData();
data.set('action', 'dekart_flush_cache');
@ -2350,22 +2359,28 @@ function dekart_flush_cache_page() {
fetch(ajaxurl, { method: 'POST', body: data })
.then(function(r) { return r.json(); })
.then(function(j) {
if (j.success) {
status.innerHTML = '✅ ' + (j.data.message || 'Кэш сброшен');
status.style.color = '#00a32a';
log.innerHTML = '';
if (j.success && j.data && j.data.log) {
j.data.log.forEach(function(item) {
addLog(item.type, item.text);
});
if (j.data.summary) {
var s = document.createElement('div');
s.style.cssText = 'margin-top:12px;padding:10px 14px;border-radius:6px;font-weight:600;font-size:14px;background:' + (j.data.summary.type === 'ok' ? '#edfaef' : '#fcf0f1') + ';color:' + (j.data.summary.type === 'ok' ? '#00a32a' : '#d63638');
s.innerHTML = (j.data.summary.type === 'ok' ? '✅' : '❌') + ' ' + j.data.summary.text;
log.appendChild(s);
}
} else {
status.innerHTML = '❌ ' + (j.data.message || 'Ошибка');
status.style.color = '#d63638';
addLog('fail', j.data && j.data.message ? j.data.message : 'Неизвестная ошибка');
}
})
.catch(function() {
status.innerHTML = '❌ Ошибка соединения';
status.style.color = '#d63638';
log.innerHTML = '';
addLog('fail', 'Ошибка соединения с сервером');
})
.finally(function() {
btn.disabled = false;
btn.innerHTML = '<span class="dashicons dashicons-update" style="font-size:20px;width:20px;height:20px"></span> Сбросить кэш сейчас';
setTimeout(function() { status.style.display = 'none'; }, 5000);
btn.innerHTML = '<span class="dashicons dashicons-update" style="font-size:20px;width:20px;height:20px"></span> Выполнить проверку и сброс';
});
});
})();
@ -2436,45 +2451,105 @@ function dekart_ajax_flush_cache() {
wp_send_json_error( array( 'message' => 'Нет прав' ) );
}
$messages = array();
$log = array();
$errors = 0;
$abspath = defined( 'ABSPATH' ) ? ABSPATH : dirname( __DIR__, 4 ) . '/';
// 0. Установка Memcached drop-in (если ещё не скопирован)
$source = WP_CONTENT_DIR . '/plugins/memcached/object-cache.php';
$target = WP_CONTENT_DIR . '/object-cache.php';
if ( ! file_exists( $target ) ) {
if ( file_exists( $source ) ) {
if ( copy( $source, $target ) ) {
$messages[] = 'drop-in installed';
} else {
$messages[] = '⚠ drop-in copy failed (permissions?)';
}
// ── 1. Проверка файлов ──
$files = array(
'MU-плагин (object-cache-bootstrap)' => WP_CONTENT_DIR . '/mu-plugins/object-cache-bootstrap.php',
'Плагин Memcached (object-cache.php)' => WP_CONTENT_DIR . '/plugins/memcached/object-cache.php',
'Тема (functions.php)' => get_template_directory() . '/functions.php',
'wp-config.php' => $abspath . 'wp-config.php',
);
$log[] = array( 'type' => 'info', 'text' => '🔍 Проверка файлов системы:' );
foreach ( $files as $label => $path ) {
if ( file_exists( $path ) ) {
$size = size_format( filesize( $path ) );
$log[] = array( 'type' => 'ok', 'text' => "{$label} — найден ({$size})" );
} else {
$messages[] = '⚠ plugin memcached not found';
$log[] = array( 'type' => 'warn', 'text' => "{$label} — отсутствует" );
}
}
// 1. Versioned cache bump (MU-плагин)
// ── 2. PHP extension Memcache ──
$log[] = array( 'type' => 'info', 'text' => '🔍 Проверка PHP-расширения:' );
if ( class_exists( 'Memcache' ) || class_exists( 'Memcached' ) ) {
$ext = class_exists( 'Memcached' ) ? 'memcached' : 'memcache';
$log[] = array( 'type' => 'ok', 'text' => "PHP-расширение {$ext} — установлено" );
} else {
$log[] = array( 'type' => 'warn', 'text' => 'PHP-расширение memcache/memcached — не найдено (обратитесь в хостинг)' );
}
// ── 3. Memcached drop-in ──
$source = WP_CONTENT_DIR . '/plugins/memcached/object-cache.php';
$target = WP_CONTENT_DIR . '/object-cache.php';
$log[] = array( 'type' => 'info', 'text' => '🔍 Проверка drop-in Memcached:' );
if ( file_exists( $target ) ) {
$size = size_format( filesize( $target ) );
$log[] = array( 'type' => 'ok', 'text' => "Drop-in уже установлен: wp-content/object-cache.php ({$size})" );
} else {
if ( file_exists( $source ) ) {
$copied = copy( $source, $target );
if ( $copied ) {
$size = size_format( filesize( $target ) );
$log[] = array( 'type' => 'ok', 'text' => "Drop-in скопирован: plugins/memcached/object-cache.php → wp-content/object-cache.php ({$size})" );
} else {
$log[] = array( 'type' => 'fail', 'text' => 'Ошибка копирования! Проверьте права на wp-content/' );
$errors++;
}
} else {
$log[] = array( 'type' => 'warn', 'text' => 'Плагин Memcached не установлен. Установите: wp plugin install memcached' );
}
}
// ── 4. Проверка WP_CACHE_KEY_SALT ──
$log[] = array( 'type' => 'info', 'text' => '🔍 Проверка изоляции ключей кеша:' );
if ( defined( 'WP_CACHE_KEY_SALT' ) && WP_CACHE_KEY_SALT !== '' ) {
$log[] = array( 'type' => 'ok', 'text' => 'WP_CACHE_KEY_SALT = ' . WP_CACHE_KEY_SALT . ' (изоляция активна)' );
} else {
$log[] = array( 'type' => 'fail', 'text' => 'WP_CACHE_KEY_SALT не задан! Добавьте в wp-config.php: define( "WP_CACHE_KEY_SALT", DB_NAME );' );
$errors++;
}
// ── 5. Versioned cache bump (MU-плагин) ──
$log[] = array( 'type' => 'info', 'text' => '🔍 Инвалидация versioned cache:' );
if ( function_exists( 'my_cache_bump_version' ) ) {
my_cache_bump_version();
$messages[] = 'version bumped';
$log[] = array( 'type' => 'ok', 'text' => 'Версия кеша обновлена (my_cache_bump_version)' );
} else {
$log[] = array( 'type' => 'warn', 'text' => 'my_cache_bump_version() не найдена — MU-плагин не активен' );
}
// 2. WP object cache flush (Memcached/Redis drop-in)
// ── 6. WP object cache flush ──
$log[] = array( 'type' => 'info', 'text' => '🔍 Очистка объектного кеша:' );
if ( function_exists( 'wp_cache_flush' ) ) {
wp_cache_flush();
$messages[] = 'wp_cache_flush';
$flushed = wp_cache_flush();
if ( $flushed ) {
$log[] = array( 'type' => 'ok', 'text' => 'wp_cache_flush() — успешно' );
} else {
$log[] = array( 'type' => 'warn', 'text' => 'wp_cache_flush() — не выполнен (кеш не активен?)' );
}
} else {
$log[] = array( 'type' => 'warn', 'text' => 'wp_cache_flush() — функция не найдена (drop-in не установлен)' );
}
// 3. Transients — все сразу
// ── 7. Transients ──
$log[] = array( 'type' => 'info', 'text' => '🔍 Очистка транзиентов:' );
global $wpdb;
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' OR option_name LIKE '_transient_timeout_%'" );
$deleted = $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' OR option_name LIKE '_transient_timeout_%'" );
$log[] = array( 'type' => 'ok', 'text' => "Удалено {$deleted} записей _transient_* из wp_options" );
$messages[] = 'transients clean';
// 4. CSS/JS кеш — сброс через filemtime (версии ассетов обновятся сами)
// ── Итог ──
if ( $errors === 0 ) {
$summary = array( 'type' => 'ok', 'text' => 'Всё выполнено успешно. Ошибок нет.' );
} else {
$summary = array( 'type' => 'fail', 'text' => "Выполнено с {$errors} ошибкой(ами). Проверьте лог выше." );
}
wp_send_json_success( array(
'message' => 'Кэш сброшен: ' . implode( ', ', $messages ),
'actions' => $messages,
'log' => $log,
'summary' => $summary,
) );
}