backup: estado antes de limpieza de defaults

This commit is contained in:
FrankZamora
2025-11-13 21:45:11 -06:00
parent d73e0dc9cd
commit 0038ad502c
38 changed files with 9495 additions and 512 deletions

View File

@@ -0,0 +1,382 @@
<?php
/**
* Theme Options Migrator Class
*
* Migra configuraciones de wp_options a tabla personalizada wp_apus_theme_components
*
* @package Apus_Theme
* @since 2.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
class APUS_Theme_Options_Migrator {
/**
* DB Manager instance
*/
private $db_manager;
/**
* Nombre de la opción en wp_options
*/
const OLD_OPTION_NAME = 'apus_theme_options';
/**
* Nombre del componente en la nueva tabla
*/
const COMPONENT_NAME = 'theme';
/**
* Constructor
*/
public function __construct() {
$this->db_manager = new APUS_DB_Manager();
}
/**
* Mapeo de tipos de datos para cada configuración
*
* @return array Mapeo config_key => data_type
*/
private function get_data_types_map() {
return array(
// Integers (IDs y contadores)
'site_logo' => 'integer',
'site_favicon' => 'integer',
'excerpt_length' => 'integer',
'archive_posts_per_page' => 'integer',
'related_posts_count' => 'integer',
'related_posts_columns' => 'integer',
// Booleans (enable_*, show_*, performance_*)
'enable_breadcrumbs' => 'boolean',
'show_featured_image_single' => 'boolean',
'show_author_box' => 'boolean',
'enable_comments_posts' => 'boolean',
'enable_comments_pages' => 'boolean',
'show_post_meta' => 'boolean',
'show_post_tags' => 'boolean',
'show_post_categories' => 'boolean',
'enable_lazy_loading' => 'boolean',
'performance_remove_emoji' => 'boolean',
'performance_remove_embeds' => 'boolean',
'performance_remove_dashicons' => 'boolean',
'performance_defer_js' => 'boolean',
'performance_minify_html' => 'boolean',
'performance_disable_gutenberg' => 'boolean',
'enable_related_posts' => 'boolean',
// Strings (todo lo demás: URLs, textos cortos, formatos, CSS/JS)
// No es necesario especificarlos, 'string' es el default
);
}
/**
* Determinar tipo de dato para una configuración
*
* @param string $config_key Nombre de la configuración
* @param mixed $config_value Valor de la configuración
* @return string Tipo de dato (string, boolean, integer, json)
*/
private function determine_data_type($config_key, $config_value) {
$types_map = $this->get_data_types_map();
// Si está en el mapa explícito, usar ese tipo
if (isset($types_map[$config_key])) {
return $types_map[$config_key];
}
// Detección automática por valor
if (is_array($config_value)) {
return 'json';
}
if (is_bool($config_value)) {
return 'boolean';
}
if (is_int($config_value)) {
return 'integer';
}
// Default: string (incluye textos largos, URLs, etc.)
return 'string';
}
/**
* Normalizar valor según tipo de dato
*
* @param mixed $value Valor a normalizar
* @param string $data_type Tipo de dato
* @return mixed Valor normalizado
*/
private function normalize_value($value, $data_type) {
switch ($data_type) {
case 'boolean':
// Convertir a booleano real (maneja strings '0', '1', etc.)
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false;
case 'integer':
return (int) $value;
case 'json':
// Si ya es array, dejarlo así (DB Manager lo codificará)
return is_array($value) ? $value : json_decode($value, true);
case 'string':
default:
return (string) $value;
}
}
/**
* Verificar si ya se realizó la migración
*
* @return bool True si ya está migrado, false si no
*/
public function is_migrated() {
// La migración se considera completa si:
// 1. No existe la opción antigua en wp_options
// 2. Y existen configuraciones en la tabla nueva
$old_options = get_option(self::OLD_OPTION_NAME, false);
$new_config = $this->db_manager->get_config(self::COMPONENT_NAME);
// Si no hay opción antigua Y hay configuraciones nuevas = migrado
return ($old_options === false && !empty($new_config));
}
/**
* Ejecutar migración completa
*
* @return array Resultado de la migración con éxito, mensaje y detalles
*/
public function migrate() {
// 1. Verificar si ya se migró
if ($this->is_migrated()) {
return array(
'success' => false,
'message' => 'La migración ya fue realizada anteriormente',
'already_migrated' => true
);
}
// 2. Obtener configuraciones actuales de wp_options
$old_options = get_option(self::OLD_OPTION_NAME, array());
if (empty($old_options)) {
return array(
'success' => false,
'message' => 'No hay opciones para migrar en wp_options'
);
}
// 3. Crear backup antes de migrar
$backup_result = $this->create_backup($old_options);
if (!$backup_result['success']) {
return $backup_result;
}
$backup_name = $backup_result['backup_name'];
// 4. Migrar cada configuración
$total = count($old_options);
$migrated = 0;
$errors = array();
foreach ($old_options as $config_key => $config_value) {
// Determinar tipo de dato
$data_type = $this->determine_data_type($config_key, $config_value);
// Normalizar valor
$normalized_value = $this->normalize_value($config_value, $data_type);
// Guardar en tabla personalizada
$result = $this->db_manager->save_config(
self::COMPONENT_NAME,
$config_key,
$normalized_value,
$data_type,
APUS_ADMIN_PANEL_VERSION
);
if ($result !== false) {
$migrated++;
} else {
$errors[] = $config_key;
}
}
// 5. Verificar resultado de la migración
if ($migrated === $total) {
// Éxito total
// Eliminar opción antigua de wp_options
delete_option(self::OLD_OPTION_NAME);
return array(
'success' => true,
'message' => sprintf('Migradas %d configuraciones correctamente', $migrated),
'migrated' => $migrated,
'total' => $total,
'backup_name' => $backup_name
);
} else {
// Migración parcial o con errores
return array(
'success' => false,
'message' => sprintf('Solo se migraron %d de %d configuraciones', $migrated, $total),
'migrated' => $migrated,
'total' => $total,
'errors' => $errors,
'backup_name' => $backup_name
);
}
}
/**
* Crear backup de las opciones actuales
*
* @param array $options Opciones a respaldar
* @return array Resultado con success y backup_name
*/
private function create_backup($options) {
$backup_name = self::OLD_OPTION_NAME . '_backup_' . date('Y-m-d_H-i-s');
$result = update_option($backup_name, $options, false); // No autoload
if ($result) {
return array(
'success' => true,
'backup_name' => $backup_name
);
} else {
return array(
'success' => false,
'message' => 'No se pudo crear el backup de seguridad'
);
}
}
/**
* Rollback de migración (revertir a estado anterior)
*
* @param string $backup_name Nombre del backup a restaurar
* @return array Resultado del rollback
*/
public function rollback($backup_name = null) {
// Si no se especifica backup, buscar el más reciente
if ($backup_name === null) {
$backup_name = $this->find_latest_backup();
}
if ($backup_name === null) {
return array(
'success' => false,
'message' => 'No se encontró backup para restaurar'
);
}
// Obtener backup
$backup = get_option($backup_name, false);
if ($backup === false) {
return array(
'success' => false,
'message' => sprintf('Backup "%s" no encontrado', $backup_name)
);
}
// Restaurar en wp_options
$restored = update_option(self::OLD_OPTION_NAME, $backup);
if ($restored) {
// Eliminar configuraciones de la tabla personalizada
$this->db_manager->delete_config(self::COMPONENT_NAME);
return array(
'success' => true,
'message' => 'Rollback completado exitosamente',
'backup_used' => $backup_name
);
} else {
return array(
'success' => false,
'message' => 'No se pudo restaurar el backup'
);
}
}
/**
* Buscar el backup más reciente
*
* @return string|null Nombre del backup más reciente o null
*/
private function find_latest_backup() {
global $wpdb;
// Buscar opciones que empiecen con el patrón de backup
$pattern = self::OLD_OPTION_NAME . '_backup_%';
$backup_name = $wpdb->get_var($wpdb->prepare(
"SELECT option_name FROM {$wpdb->options}
WHERE option_name LIKE %s
ORDER BY option_id DESC
LIMIT 1",
$pattern
));
return $backup_name;
}
/**
* Listar todos los backups disponibles
*
* @return array Lista de nombres de backups
*/
public function list_backups() {
global $wpdb;
$pattern = self::OLD_OPTION_NAME . '_backup_%';
$backups = $wpdb->get_col($wpdb->prepare(
"SELECT option_name FROM {$wpdb->options}
WHERE option_name LIKE %s
ORDER BY option_id DESC",
$pattern
));
return $backups;
}
/**
* Eliminar un backup específico
*
* @param string $backup_name Nombre del backup a eliminar
* @return bool True si se eliminó, false si no
*/
public function delete_backup($backup_name) {
return delete_option($backup_name);
}
/**
* Obtener estadísticas de la migración
*
* @return array Estadísticas
*/
public function get_migration_stats() {
$old_options = get_option(self::OLD_OPTION_NAME, array());
$new_config = $this->db_manager->get_config(self::COMPONENT_NAME);
$backups = $this->list_backups();
return array(
'is_migrated' => $this->is_migrated(),
'old_options_count' => count($old_options),
'new_config_count' => count($new_config),
'backups_count' => count($backups),
'backups' => $backups
);
}
}