'localhost', 'database' => 'preciosunitarios_seo', 'username' => 'preciosunitarios_seo', 'password' => 'ACl%EEFd=V-Yvb??', 'charset' => 'utf8mb4' ]; $output_dir = '/tmp/list-fix-validation'; $sample_size = 5; echo "==============================================\n"; echo " VALIDADOR DE CORRECCIONES\n"; echo " Fecha: " . date('Y-m-d H:i:s') . "\n"; echo "==============================================\n\n"; // Crear directorio de salida if (!is_dir($output_dir)) { mkdir($output_dir, 0755, true); } // Limpiar archivos anteriores array_map('unlink', glob("$output_dir/*.html")); /** * Detectar problemas en HTML */ function detectIssues(string $html): array { $issues = []; libxml_use_internal_errors(true); $doc = new DOMDocument('1.0', 'UTF-8'); $wrapped = '
' . $html . '
'; $doc->loadHTML('' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors(); $validChildren = ['li', 'script', 'template']; foreach (['ul', 'ol'] as $listTag) { foreach ($doc->getElementsByTagName($listTag) as $list) { foreach ($list->childNodes as $child) { if ($child->nodeType === XML_ELEMENT_NODE) { $tagName = strtolower($child->nodeName); if (!in_array($tagName, $validChildren)) { $issues[] = "<$listTag> contiene <$tagName>"; } } } } } return $issues; } /** * Corregir listas mal formadas */ function fixMalformedLists(string $html): array { $result = ['fixed' => false, 'html' => $html, 'changes' => 0, 'details' => []]; libxml_use_internal_errors(true); $doc = new DOMDocument('1.0', 'UTF-8'); $wrapped = '
' . $html . '
'; $doc->loadHTML('' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors(); $lists = []; foreach ($doc->getElementsByTagName('ul') as $ul) { $lists[] = $ul; } foreach ($doc->getElementsByTagName('ol') as $ol) { $lists[] = $ol; } $changes = 0; $validChildren = ['li', 'script', 'template']; foreach ($lists as $list) { $nodesToProcess = []; foreach ($list->childNodes as $child) { if ($child->nodeType === XML_ELEMENT_NODE) { $tagName = strtolower($child->nodeName); if (!in_array($tagName, $validChildren)) { $nodesToProcess[] = $child; } } } foreach ($nodesToProcess as $node) { $tagName = strtolower($node->nodeName); $prevLi = null; $prev = $node->previousSibling; while ($prev) { if ($prev->nodeType === XML_ELEMENT_NODE && strtolower($prev->nodeName) === 'li') { $prevLi = $prev; break; } $prev = $prev->previousSibling; } if ($prevLi) { $prevLi->appendChild($node); $result['details'][] = "Movido <$tagName> dentro del
  • anterior"; $changes++; } else { $newLi = $doc->createElement('li'); $list->insertBefore($newLi, $node); $newLi->appendChild($node); $result['details'][] = "Envuelto <$tagName> en nuevo
  • "; $changes++; } } } if ($changes > 0) { $wrapper = $doc->getElementById('temp-wrapper'); if ($wrapper) { $innerHTML = ''; foreach ($wrapper->childNodes as $child) { $innerHTML .= $doc->saveHTML($child); } $result['html'] = $innerHTML; $result['fixed'] = true; $result['changes'] = $changes; } } return $result; } /** * Generar HTML wrapper para visualización */ function wrapForVisualization(string $content, string $title, string $status): string { $statusColor = $status === 'error' ? '#dc3545' : '#28a745'; return << $title
    $status
    $content
    HTML; } // Conectar a DB $conn = new mysqli($db_config['host'], $db_config['username'], $db_config['password'], $db_config['database']); $conn->set_charset($db_config['charset']); if ($conn->connect_error) { die("Error de conexión: " . $conn->connect_error); } echo "✓ Conexión establecida\n\n"; // Buscar posts con problemas $query = "SELECT id, page, html FROM datos_seo_pagina WHERE html IS NOT NULL AND html != '' ORDER BY id LIMIT 500"; $result = $conn->query($query); $samples = []; while ($row = $result->fetch_assoc()) { $issues = detectIssues($row['html']); if (!empty($issues) && count($samples) < $sample_size) { $samples[] = $row; } } echo "Encontrados " . count($samples) . " posts con problemas para validar\n\n"; $comparison_data = []; foreach ($samples as $idx => $post) { $id = $post['id']; $url = $post['page']; $html_before = $post['html']; echo "─────────────────────────────────\n"; echo "POST $id: $url\n"; // Detectar problemas antes $issues_before = detectIssues($html_before); echo " Problemas ANTES: " . count($issues_before) . "\n"; // Aplicar corrección $fixResult = fixMalformedLists($html_before); $html_after = $fixResult['html']; // Detectar problemas después $issues_after = detectIssues($html_after); echo " Problemas DESPUÉS: " . count($issues_after) . "\n"; echo " Cambios aplicados: " . $fixResult['changes'] . "\n"; // Guardar archivos HTML $file_before = "$output_dir/post_{$id}_BEFORE.html"; $file_after = "$output_dir/post_{$id}_AFTER.html"; file_put_contents($file_before, wrapForVisualization( $html_before, "Post $id - ANTES (con errores)", "ANTES: " . count($issues_before) . " problemas de listas" )); file_put_contents($file_after, wrapForVisualization( $html_after, "Post $id - DESPUÉS (corregido)", "DESPUÉS: " . count($issues_after) . " problemas - " . $fixResult['changes'] . " correcciones aplicadas" )); echo " ✓ Archivos generados:\n"; echo " - $file_before\n"; echo " - $file_after\n"; // Guardar datos para reporte $comparison_data[] = [ 'id' => $id, 'url' => $url, 'issues_before' => count($issues_before), 'issues_after' => count($issues_after), 'changes' => $fixResult['changes'], 'file_before' => "post_{$id}_BEFORE.html", 'file_after' => "post_{$id}_AFTER.html" ]; } // Generar reporte comparativo $report_html = << Reporte de Validación - Corrección de Listas

    Reporte de Validación - Corrección de Listas HTML

    Instrucciones:
    1. Abre cada par de archivos (ANTES/DESPUÉS) en el navegador
    2. Verifica que el contenido se muestre correctamente
    3. Las listas (fondo amarillo) deben contener solo items (fondo verde)
    4. Si todo se ve bien, la corrección es segura
    HTML; foreach ($comparison_data as $data) { $status_class = $data['issues_after'] == 0 ? 'success' : ($data['issues_after'] < $data['issues_before'] ? 'warning' : 'error'); $report_html .= << HTML; } $report_html .= <<
    ID URL Problemas Antes Problemas Después Cambios Archivos
    {$data['id']} {$data['url']} {$data['issues_before']} {$data['issues_after']} {$data['changes']} ANTES | DESPUÉS

    Generado: {$_SERVER['REQUEST_TIME_FLOAT']}

    HTML; $report_file = "$output_dir/comparison_report.html"; file_put_contents($report_file, $report_html); echo "\n─────────────────────────────────\n"; echo "REPORTE GENERADO:\n"; echo " $report_file\n\n"; echo "Para revisar, descarga el directorio:\n"; echo " scp -r VPSContabo:$output_dir ./validation/\n\n"; $conn->close(); echo "✓ Validación completada.\n";