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>
226 lines
7.8 KiB
PHP
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;
|
|
}
|
|
}
|