query("DESCRIBE `$tableName`"); $columns = []; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $columns[$row['Field']] = [ 'type' => $row['Type'], 'null' => $row['Null'], 'key' => $row['Key'], 'default' => $row['Default'], 'extra' => $row['Extra'] ]; } return $columns; } catch (PDOException $e) { return null; } } // Mappings de colonnes connus $columnMappings = [ // Mappings globaux 'global' => [ 'rowid' => 'id', 'active' => 'chk_active', 'date_creat' => 'created_at', 'date_modif' => 'updated_at', ], // Mappings spécifiques par table 'users_entites' => [ 'table_target' => 'entites', 'mappings' => [ 'libelle' => 'encrypted_name', 'tel1' => 'encrypted_phone', 'tel2' => 'encrypted_mobile', 'email' => 'encrypted_email', 'iban' => 'encrypted_iban', 'bic' => 'encrypted_bic', 'cp' => 'code_postal', ] ], 'users' => [ 'mappings' => [ 'libelle' => 'encrypted_name', 'username' => 'encrypted_user_name', 'userpswd' => 'user_pass_hash', 'userpass' => 'user_pass_hash', 'prenom' => 'first_name', 'nom_tournee' => 'sect_name', 'telephone' => 'encrypted_phone', 'mobile' => 'encrypted_mobile', 'email' => 'encrypted_email', 'alert_email' => 'chk_alert_email', ] ], 'ope_pass' => [ 'mappings' => [ 'date_eve' => 'passed_at', 'libelle' => 'encrypted_name', 'email' => 'encrypted_email', 'phone' => 'encrypted_phone', 'recu' => 'nom_recu', ] ], 'medias' => [ 'mappings' => [ 'support_rowid' => 'support_id', ] ], 'x_villes' => [ 'mappings' => [ 'cp' => 'code_postal', ] ], ]; // Tables à vérifier (source => cible) $tablesToVerify = [ 'x_devises' => 'x_devises', 'x_entites_types' => 'x_entites_types', 'x_types_passages' => 'x_types_passages', 'x_types_reglements' => 'x_types_reglements', 'x_users_roles' => 'x_users_roles', 'x_pays' => 'x_pays', 'x_regions' => 'x_regions', 'x_departements' => 'x_departements', 'x_villes' => 'x_villes', 'users_entites' => 'entites', 'users' => 'users', 'operations' => 'operations', 'ope_users' => 'ope_users', 'ope_users_sectors' => 'ope_users_sectors', 'ope_pass' => 'ope_pass', 'ope_pass_histo' => 'ope_pass_histo', 'medias' => 'medias', 'sectors_adresses' => 'sectors_adresses', ]; try { printColor("\n╔══════════════════════════════════════════════════════════════╗", COLOR_BLUE); printColor("║ VÉRIFICATION DES STRUCTURES DE MIGRATION ║", COLOR_BLUE); printColor("╚══════════════════════════════════════════════════════════════╝", COLOR_BLUE); // Connexion aux bases de données printColor("\n[INFO] Connexion aux bases de données...", COLOR_BLUE); $sourceDb = getSourceConnection(); $targetDb = getTargetConnection(); printColor("[OK] Connexions établies", COLOR_GREEN); $totalIssues = 0; $totalWarnings = 0; $totalTables = count($tablesToVerify); foreach ($tablesToVerify as $sourceTable => $targetTable) { printColor("\n" . str_repeat("─", 70), COLOR_BLUE); printColor("📊 Table: $sourceTable → $targetTable", COLOR_BLUE); printColor(str_repeat("─", 70), COLOR_BLUE); // Récupérer les colonnes $sourceCols = getTableColumns($sourceDb, $sourceTable); $targetCols = getTableColumns($targetDb, $targetTable); if ($sourceCols === null) { printColor("❌ ERREUR: Table source '$sourceTable' introuvable", COLOR_RED); $totalIssues++; continue; } if ($targetCols === null) { printColor("❌ ERREUR: Table cible '$targetTable' introuvable", COLOR_RED); $totalIssues++; continue; } // Récupérer les mappings pour cette table $tableMappings = $columnMappings['global']; if (isset($columnMappings[$sourceTable]['mappings'])) { $tableMappings = array_merge($tableMappings, $columnMappings[$sourceTable]['mappings']); } // Vérifier chaque colonne source $unmappedSourceCols = []; $mappedCols = 0; foreach ($sourceCols as $sourceCol => $sourceInfo) { // Chercher la colonne cible $targetCol = $tableMappings[$sourceCol] ?? $sourceCol; if (isset($targetCols[$targetCol])) { $mappedCols++; // Colonne existe et mappée correctement } else { // Vérifier si c'est une colonne qui doit être ignorée $ignoredCols = ['dir0', 'dir1', 'dir2', 'type_fichier', 'position', 'hauteur', 'largeur', 'niveaugris', 'lieudit', 'chk_habitat_vide', 'lot_nb_passages', 'departement', 'fk_user', 'chk_api_adresse', 'num_adherent', 'libelle_naissance', 'josh', 'email_secondaire', 'infos', 'ltt', 'lng', 'sector', 'dept_naissance', 'commune_naissance', 'anciennete', 'fk_categorie', 'fk_sous_categorie', 'adresse_1', 'adresse_2', 'cp', 'ville', 'matricule', 'fk_grade', 'chk_adherent_ud', 'chk_adherent_ur', 'chk_adherent_fns', 'chk_archive', 'chk_double_affectation', 'date_creat', 'appname', 'http_host', 'tva_intra', 'rcs', 'siret', 'ape', 'couleur', 'prefecture', 'fk_titre_gerant', 'gerant_prenom', 'gerant_nom', 'site_url', 'gerant_signature', 'tampon_signature', 'banque_libelle', 'banque_adresse', 'banque_cp', 'banque_ville', 'genbase', 'groupebase', 'userbase', 'passbase', 'demo', 'lib_vert', 'lib_verts', 'lib_orange', 'lib_oranges', 'lib_rouge', 'lib_rouges', 'lib_bleu', 'lib_bleus', 'icon_siege', 'icon_siege_color', 'btn_width', 'nbmembres', 'nbconnex']; if (in_array($sourceCol, $ignoredCols)) { // Colonne volontairement non migrée continue; } $unmappedSourceCols[] = $sourceCol; } } // Vérifier les nouvelles colonnes dans la cible $newTargetCols = []; foreach ($targetCols as $targetCol => $targetInfo) { // Vérifier si cette colonne existe dans la source $sourceCol = array_search($targetCol, $tableMappings); if ($sourceCol === false) { $sourceCol = $targetCol; // Même nom } if (!isset($sourceCols[$sourceCol])) { // Vérifier si c'est une colonne attendue (timestamp auto, etc.) $autoColumns = ['created_at', 'updated_at', 'id']; if (!in_array($targetCol, $autoColumns)) { $newTargetCols[] = $targetCol; } } } // Affichage des résultats printColor("✓ Colonnes source mappées: $mappedCols/" . count($sourceCols), COLOR_GREEN); if (!empty($unmappedSourceCols)) { printColor("⚠ Colonnes source NON mappées:", COLOR_YELLOW); foreach ($unmappedSourceCols as $col) { printColor(" - $col ({$sourceCols[$col]['type']})", COLOR_YELLOW); } $totalWarnings += count($unmappedSourceCols); } if (!empty($newTargetCols)) { printColor("ℹ Nouvelles colonnes dans cible (seront NULL/défaut):", COLOR_YELLOW); foreach ($newTargetCols as $col) { $defaultValue = $targetCols[$col]['default'] ?? 'NULL'; $nullable = $targetCols[$col]['null'] === 'YES' ? '(nullable)' : '(NOT NULL)'; printColor(" - $col ({$targetCols[$col]['type']}) = $defaultValue $nullable", COLOR_YELLOW); } $totalWarnings += count($newTargetCols); } if (empty($unmappedSourceCols) && empty($newTargetCols)) { printColor("✓ Aucun problème détecté", COLOR_GREEN); } } // Résumé final printColor("\n" . str_repeat("═", 70), COLOR_BLUE); printColor("📈 RÉSUMÉ DE LA VÉRIFICATION", COLOR_BLUE); printColor(str_repeat("═", 70), COLOR_BLUE); printColor("Tables vérifiées: $totalTables", COLOR_BLUE); if ($totalIssues > 0) { printColor("❌ Erreurs critiques: $totalIssues", COLOR_RED); } else { printColor("✓ Aucune erreur critique", COLOR_GREEN); } if ($totalWarnings > 0) { printColor("⚠ Avertissements: $totalWarnings", COLOR_YELLOW); printColor(" (colonnes non mappées ou nouvelles colonnes)", COLOR_YELLOW); } else { printColor("✓ Aucun avertissement", COLOR_GREEN); } printColor("\n💡 Recommandations:", COLOR_BLUE); printColor(" - Vérifiez que les colonnes non mappées sont intentionnelles", COLOR_RESET); printColor(" - Les nouvelles colonnes cible utiliseront leurs valeurs par défaut", COLOR_RESET); printColor(" - Consultez README-migration.md pour plus de détails", COLOR_RESET); // Fermer le tunnel SSH closeSshTunnel(); printColor("\n✓ Vérification terminée\n", COLOR_GREEN); } catch (Exception $e) { printColor("\n❌ ERREUR CRITIQUE: " . $e->getMessage(), COLOR_RED); closeSshTunnel(); exit(1); }