Cómo migré esta web de HTML estático a PHP con OOP
2026-06-30
Esta web empezó como un HTML estático servido por nginx. Funcionaba bien, pero cada vez que quería añadir un proyecto nuevo tenía que copiar y pegar el mismo bloque HTML. Decidí migrarla a PHP con OOP para centralizar los datos y separar contenido de presentación.
Estructura final
Las clases definen el modelo de datos:
class Project {
public function __construct(
public readonly string $name,
public readonly string $url,
public readonly string $description,
public readonly array $tags,
public readonly ?string $logoSrc = null,
public readonly string $github = '',
public readonly int $stars = 0,
) {}
}Los datos se declaran como arrays tipados con named arguments (PHP 8+):
$projects = [
new Project(
name: '100Bocas',
url: 'https://100bocas.cabrasky.net',
description: 'App de pedidos colaborativos en tiempo real.',
tags: ['React', 'FastAPI', 'WebSockets'],
github: 'cabrasky/100bocas-pedidos',
),
new Project(
name: 'Yambo',
url: 'https://yambo.cabrasky.net',
description: 'Web de la Asociación Juvenil Yambo.',
tags: ['React', 'Remix', 'Tailwind', 'Java', 'Spring Boot'],
github: 'cabrasky/yambo',
),
];Los render helpers generan el HTML:
function renderProjectCard(Project $p): string {
$nameSafe = htmlspecialchars($p->name);
$urlSafe = htmlspecialchars($p->url);
$descSafe = htmlspecialchars($p->description);
return <<<HTML
<article class="project-card">
<h3><a href="{$urlSafe}">{$nameSafe}</a></h3>
<p>{$descSafe}</p>
</article>
HTML;
}Las GitHub Stars se obtienen via API con caché de 1 hora:
function fetchGithubStars(string $repo): int {
$cacheFile = __DIR__ . '/cache/github-stars.json';
$cacheTtl = 3600;
if (file_exists($cacheFile)) {
$cache = json_decode(file_get_contents($cacheFile), true) ?: [];
if (isset($cache[$repo]) && $cache[$repo]['time'] > time() - $cacheTtl) {
return $cache[$repo]['stars'];
}
}
$json = @file_get_contents("https://api.github.com/repos/{$repo}");
$stars = json_decode($json, true)['stargazers_count'] ?? 0;
$cache[$repo] = ['stars' => $stars, 'time' => time()];
file_put_contents($cacheFile, json_encode($cache));
return $stars;
}Lo que aprendí
Separar datos de presentación hace que el código sea mucho más mantenible. Añadir un proyecto nuevo ahora es tan simple como crear un new Project(...) en el array. El siguiente paso será añadir un sistema de temas claro/oscuro y quizás internacionalización.