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