'localhost',
'database' => 'preciosunitarios_wp',
'username' => 'preciosunitarios_wp',
'password' => 'Kq#Gk%yEt+PWpVe&HZ',
'charset' => 'utf8mb4'
];
$mode = 'scan';
foreach ($argv as $arg) {
if (strpos($arg, '--mode=') === 0) {
$mode = substr($arg, 7);
}
}
echo "==============================================\n";
echo " CORRECTOR DE LISTAS - WordPress Posts\n";
echo " Base de datos: {$db_config['database']}\n";
echo " Tabla: wp_posts (post_content)\n";
echo " Modo: $mode\n";
echo " Fecha: " . date('Y-m-d H:i:s') . "\n";
echo "==============================================\n\n";
function connectDatabase(array $config): ?mysqli {
$conn = new mysqli($config['host'], $config['username'], $config['password'], $config['database']);
if ($conn->connect_error) {
echo "Error de conexión: " . $conn->connect_error . "\n";
return null;
}
$conn->set_charset($config['charset']);
return $conn;
}
function detectIssues(string $html): array {
$issues = [];
if (empty(trim($html))) return $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[] = [
'list_type' => $listTag,
'invalid_child' => $tagName
];
}
}
}
}
}
return $issues;
}
function fixMalformedLists(string $html): array {
$result = ['fixed' => false, 'html' => $html, 'changes' => 0, 'details' => []];
if (empty(trim($html))) return $result;
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;
}
// EJECUCIÓN PRINCIPAL
$conn = connectDatabase($db_config);
if (!$conn) {
exit(1);
}
echo "✓ Conexión establecida\n\n";
// Solo posts publicados con contenido
$countQuery = "SELECT COUNT(*) as total FROM wp_posts
WHERE post_status = 'publish'
AND post_type IN ('post', 'page')
AND post_content IS NOT NULL
AND post_content != ''";
$result = $conn->query($countQuery);
$total = $result->fetch_assoc()['total'];
echo "Total de posts/páginas publicados: $total\n\n";
if ($mode === 'scan') {
echo "MODO: ESCANEO (solo detección)\n";
echo "─────────────────────────────────\n\n";
$batch_size = 100;
$offset = 0;
$affected = 0;
$total_issues = 0;
while ($offset < $total) {
$query = "SELECT ID, post_title, post_content, guid FROM wp_posts
WHERE post_status = 'publish'
AND post_type IN ('post', 'page')
AND post_content IS NOT NULL
AND post_content != ''
ORDER BY ID LIMIT $batch_size OFFSET $offset";
$result = $conn->query($query);
while ($row = $result->fetch_assoc()) {
$issues = detectIssues($row['post_content']);
if (!empty($issues)) {
$affected++;
$total_issues += count($issues);
if ($affected <= 20) {
echo "[ID: {$row['ID']}] " . count($issues) . " problema(s)\n";
echo "Título: " . substr($row['post_title'], 0, 60) . "\n";
foreach (array_slice($issues, 0, 2) as $issue) {
echo " - <{$issue['list_type']}> contiene <{$issue['invalid_child']}>\n";
}
echo "\n";
}
}
}
$offset += $batch_size;
if ($offset % 1000 == 0) {
echo "Procesados: $offset/$total...\n";
}
}
echo "─────────────────────────────────\n";
echo "RESUMEN:\n";
echo " Posts afectados: $affected\n";
echo " Total incidencias: $total_issues\n";
} elseif ($mode === 'test') {
echo "MODO: PRUEBA (sin guardar)\n";
echo "─────────────────────────────────\n\n";
$query = "SELECT ID, post_title, post_content FROM wp_posts
WHERE post_status = 'publish'
AND post_type IN ('post', 'page')
AND post_content IS NOT NULL
AND post_content != ''
ORDER BY ID LIMIT 200";
$result = $conn->query($query);
$tested = 0;
while ($row = $result->fetch_assoc()) {
$issues = detectIssues($row['post_content']);
if (!empty($issues) && $tested < 5) {
$tested++;
echo "POST ID: {$row['ID']}\n";
echo "Título: {$row['post_title']}\n";
echo "Problemas detectados: " . count($issues) . "\n\n";
$fixResult = fixMalformedLists($row['post_content']);
$issuesAfter = detectIssues($fixResult['html']);
echo "ANTES: " . count($issues) . " problemas\n";
echo "DESPUÉS: " . count($issuesAfter) . " problemas\n";
echo "Cambios: {$fixResult['changes']}\n";
// Verificar integridad
$before_ul = substr_count($row['post_content'], ': $before_ul → $after_ul " . ($before_ul === $after_ul ? "✓" : "⚠️") . "\n";
echo "Tags - : $before_li → $after_li " . ($before_li === $after_li ? "✓" : "⚠️") . "\n";
if (count($issuesAfter) === 0) {
echo "✅ CORRECCIÓN EXITOSA\n";
} else {
echo "⚠️ REQUIERE REVISIÓN\n";
}
echo "─────────────────────────────────\n\n";
}
}
} elseif ($mode === 'fix') {
echo "MODO: CORRECCIÓN (GUARDANDO CAMBIOS)\n";
echo "─────────────────────────────────\n\n";
$batch_size = 50;
$offset = 0;
$fixed_count = 0;
$error_count = 0;
while ($offset < $total) {
$query = "SELECT ID, post_content FROM wp_posts
WHERE post_status = 'publish'
AND post_type IN ('post', 'page')
AND post_content IS NOT NULL
AND post_content != ''
ORDER BY ID LIMIT $batch_size OFFSET $offset";
$result = $conn->query($query);
while ($row = $result->fetch_assoc()) {
$issues = detectIssues($row['post_content']);
if (!empty($issues)) {
$fixResult = fixMalformedLists($row['post_content']);
if ($fixResult['fixed']) {
$stmt = $conn->prepare("UPDATE wp_posts SET post_content = ? WHERE ID = ?");
$stmt->bind_param("si", $fixResult['html'], $row['ID']);
if ($stmt->execute()) {
$fixed_count++;
echo "[ID: {$row['ID']}] ✓ Corregido ({$fixResult['changes']} cambios)\n";
} else {
$error_count++;
echo "[ID: {$row['ID']}] ✗ Error al guardar\n";
}
$stmt->close();
}
}
}
$offset += $batch_size;
if ($offset % 500 == 0) {
echo "Procesados: $offset/$total (corregidos: $fixed_count)\n";
}
}
echo "\n─────────────────────────────────\n";
echo "RESUMEN:\n";
echo " Posts corregidos: $fixed_count\n";
echo " Errores: $error_count\n";
}
$conn->close();
echo "\n✓ Proceso completado.\n";