Files
roi-theme/Shared/Infrastructure/Validators/TemplateCallsValidator.php
FrankZamora 90863cd8f5 fix(structure): Correct case-sensitivity for Linux compatibility
Rename folders to match PHP PSR-4 autoloading conventions:
- schemas → Schemas
- shared → Shared
- Wordpress → WordPress (in all locations)

Fixes deployment issues on Linux servers where filesystem is case-sensitive.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 22:53:34 -06:00

226 lines
7.8 KiB
PHP

<?php
declare(strict_types=1);
namespace ROITheme\Shared\Infrastructure\Validators;
/**
* Validador de llamadas a roi_render_component() en templates
*
* Verifica que:
* - Los nombres de componentes usen kebab-case (guiones, no underscores)
* - Los componentes llamados estén registrados en functions-addon.php
* - No haya llamadas con nombres inválidos
*/
final class TemplateCallsValidator implements PhaseValidatorInterface
{
public function getPhaseNumber(): int|string
{
return 'templates';
}
public function getPhaseDescription(): string
{
return 'Template Calls (roi_render_component)';
}
public function validate(string $componentName, string $themePath): ValidationResult
{
$result = new ValidationResult();
$result->addInfo("Validando llamadas a roi_render_component() para: {$componentName}");
// 1. Obtener componentes registrados en functions-addon.php
$registeredComponents = $this->getRegisteredComponents($themePath);
$result->addInfo("Componentes registrados: " . count($registeredComponents));
// 2. Buscar todas las llamadas en templates
$templateFiles = $this->getTemplateFiles($themePath);
$allCalls = [];
$invalidCalls = [];
$unregisteredCalls = [];
foreach ($templateFiles as $file) {
$calls = $this->findRenderCalls($file);
foreach ($calls as $call) {
$allCalls[] = [
'file' => basename($file),
'line' => $call['line'],
'component' => $call['component']
];
// Verificar si usa underscore en lugar de guión
if (strpos($call['component'], '_') !== false) {
$invalidCalls[] = $call + ['file' => basename($file)];
}
// Verificar si está registrado
if (!in_array($call['component'], $registeredComponents)) {
// Solo marcar como no registrado si no tiene underscore
// (los de underscore ya se marcan como inválidos)
if (strpos($call['component'], '_') === false) {
$unregisteredCalls[] = $call + ['file' => basename($file)];
}
}
}
}
// 3. Verificar específicamente el componente que se está validando
$componentFound = false;
$componentCallsCorrect = true;
foreach ($allCalls as $call) {
// Buscar variantes del componente (con guión o underscore)
$kebabName = $componentName;
$snakeName = str_replace('-', '_', $componentName);
if ($call['component'] === $kebabName) {
$componentFound = true;
$result->addInfo("✓ Componente '{$componentName}' llamado correctamente en {$call['file']}:{$call['line']}");
} elseif ($call['component'] === $snakeName) {
$componentFound = true;
$componentCallsCorrect = false;
$result->addError(
"Llamada incorrecta en {$call['file']}:{$call['line']}: " .
"usa '{$snakeName}' (underscore) en lugar de '{$kebabName}' (kebab-case)"
);
}
}
// 4. Reportar errores generales de formato
foreach ($invalidCalls as $call) {
// Solo reportar si no es el componente actual (ya se reportó arriba)
$snakeName = str_replace('-', '_', $componentName);
if ($call['component'] !== $snakeName) {
$suggestedName = str_replace('_', '-', $call['component']);
$result->addWarning(
"Llamada con underscore en {$call['file']}:{$call['line']}: " .
"'{$call['component']}' debería ser '{$suggestedName}'"
);
}
}
// 5. Reportar componentes no registrados
foreach ($unregisteredCalls as $call) {
$result->addWarning(
"Componente no registrado en {$call['file']}:{$call['line']}: '{$call['component']}'"
);
}
// 6. Advertir si el componente no se llama en ningún template
if (!$componentFound) {
$result->addWarning(
"El componente '{$componentName}' no se llama en ningún template. " .
"Verifica que esté incluido en single.php, header.php, footer.php u otro template."
);
}
// Estadísticas
$result->setStat('Templates escaneados', count($templateFiles));
$result->setStat('Llamadas totales encontradas', count($allCalls));
$result->setStat('Componentes registrados', count($registeredComponents));
if (!empty($invalidCalls)) {
$result->setStat('Llamadas con underscore (error)', count($invalidCalls));
}
return $result;
}
/**
* Obtiene la lista de componentes registrados en functions-addon.php
*/
private function getRegisteredComponents(string $themePath): array
{
$functionsFile = $themePath . '/functions-addon.php';
$components = [];
if (!file_exists($functionsFile)) {
return $components;
}
$content = file_get_contents($functionsFile);
// Buscar patrones: case 'nombre-componente':
if (preg_match_all("/case\s+'([a-z0-9-]+)':/", $content, $matches)) {
$components = $matches[1];
}
return array_unique($components);
}
/**
* Obtiene los archivos de template PHP del tema
*/
private function getTemplateFiles(string $themePath): array
{
$templates = [];
// Archivos principales de template
$mainTemplates = [
'header.php',
'footer.php',
'single.php',
'page.php',
'index.php',
'archive.php',
'search.php',
'sidebar.php',
'404.php',
'front-page.php',
'home.php',
];
foreach ($mainTemplates as $template) {
$path = $themePath . '/' . $template;
if (file_exists($path)) {
$templates[] = $path;
}
}
// Buscar en template-parts/
$templatePartsDir = $themePath . '/template-parts';
if (is_dir($templatePartsDir)) {
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($templatePartsDir)
);
foreach ($iterator as $file) {
if ($file->isFile() && $file->getExtension() === 'php') {
$templates[] = $file->getPathname();
}
}
}
// Buscar templates personalizados (page-*.php, single-*.php)
$customTemplates = glob($themePath . '/{page-*.php,single-*.php}', GLOB_BRACE);
if ($customTemplates) {
$templates = array_merge($templates, $customTemplates);
}
return $templates;
}
/**
* Busca llamadas a roi_render_component() en un archivo
*/
private function findRenderCalls(string $filePath): array
{
$calls = [];
$content = file_get_contents($filePath);
$lines = explode("\n", $content);
foreach ($lines as $lineNum => $line) {
// Buscar: roi_render_component('nombre') o roi_render_component("nombre")
if (preg_match_all("/roi_render_component\s*\(\s*['\"]([^'\"]+)['\"]\s*\)/", $line, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$calls[] = [
'line' => $lineNum + 1,
'component' => $match[1]
];
}
}
}
return $calls;
}
}