En la Unidad 1 quedó pendiente una pregunta: ¿y si tengo diez páginas? Copiar la navbar diez veces obliga a hacer cada cambio diez veces. Con include, la navbar se escribe en un archivo y cada vista la trae con una línea.

Cómo funciona

include "archivo.php"; hace exactamente lo que dice: pega el contenido de ese archivo en ese lugar, y PHP lo ejecuta como si siempre hubiera estado ahí.

Una vista armada con partes

flowchart LR
    subgraph V["index.php"]
        A["include cabecera.php"]
        B["contenido propio<br/>de la portada"]
        C["include pie.php"]
    end
    H["partes/cabecera.php<br/>(head y navbar)"] -.-> A
    P["partes/pie.php<br/>(footer)"] -.-> C
    V --> R["HTML completo<br/>que recibe el navegador"]

Las tres vistas incluyen los mismos dos archivos. Cambiar un enlace de la navbar es cambiar una línea en partes/cabecera.php.

Paso a paso

1. Las carpetas

El mini sitio con partes

mi-sitio/
|-- index.php            <- vista: Inicio
|-- sobre-mi.php         <- vista: Sobre mí
|-- contacto.php         <- vista: Contacto
|-- datos.php            <- arreglos con la información del sitio
|-- partes/
|   |-- cabecera.php     <- <!DOCTYPE>, <head>, Tailwind y la navbar
|   `-- pie.php          <- el <footer> y el cierre de </body></html>
`-- img/

2. La cabecera

Todo lo que se repite antes del contenido de cada vista:

<?php
// partes/cabecera.php
$normal = "hover:text-sky-700";
$activa = "font-bold text-sky-700";
 
$claseInicio = $normal;
$claseSobreMi = $normal;
$claseContacto = $normal;
 
if ($pagina === "inicio") {
    $claseInicio = $activa;
} elseif ($pagina === "sobre-mi") {
    $claseSobreMi = $activa;
} elseif ($pagina === "contacto") {
    $claseContacto = $activa;
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><?= $titulo ?> · Mi sitio</title>
  <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
</head>
<body class="bg-gray-50 text-gray-800">
  <nav class="bg-white shadow">
    <ul class="mx-auto flex max-w-4xl gap-6 px-4 py-3">
      <li><a href="index.php" class="<?= $claseInicio ?>">Inicio</a></li>
      <li><a href="sobre-mi.php" class="<?= $claseSobreMi ?>">Sobre mí</a></li>
      <li><a href="contacto.php" class="<?= $claseContacto ?>">Contacto</a></li>
    </ul>
  </nav>
  <main class="mx-auto max-w-4xl px-4 py-8">

Arriba, en PHP, cada enlace parte con la clase normal y solo el de la página actual cambia a la clase activa. Abajo, el HTML solo imprime la clase que corresponde.

3. El pie

Todo lo que se repite después del contenido:

<?php // partes/pie.php ?>
  </main>
  <footer class="py-6 text-center text-sm text-gray-600">
    © <?= date("Y") ?> · Mi sitio
  </footer>
</body>
</html>

Las etiquetas quedan abiertas en un archivo y se cierran en otro

<body> y <main> se abren en la cabecera y se cierran en el pie. Por separado se ven incompletos, pero PHP los junta antes de enviarlos: el navegador recibe el documento completo.

4. Cada vista

<?php
$titulo = "Inicio";
$pagina = "inicio";
include __DIR__ . "/partes/cabecera.php";
?>
 
<h1 class="text-4xl font-bold text-gray-900">Bienvenido</h1>
<p class="mt-4">Este es el contenido propio de la portada.</p>
 
<?php include __DIR__ . "/partes/pie.php"; ?>

La vista solo tiene su contenido. Las variables $titulo y $pagina se crean antes del include, y la cabecera las usa para el <title> y para marcar la página activa.

El archivo incluido sí ve las variables

Un archivo incluido comparte las variables de quien lo incluye: es como si su código estuviera escrito ahí mismo. Por eso la cabecera puede leer $pagina.

Incorrecto: definir las variables después del include

<?php
include __DIR__ . "/partes/cabecera.php";
$titulo = "Inicio";
Warning: Undefined variable $titulo in /home/ana/mi-sitio/partes/cabecera.php on line 7

PHP lee de arriba hacia abajo: cuando ejecuta la cabecera, $titulo todavía no existe. Fíjate en que el error dice cabecera.php, no index.php.

Rutas con __DIR__

__DIR__ es una constante que vale la carpeta del archivo donde está escrita. __DIR__ . "/partes/pie.php" arma una ruta completa que no depende de desde dónde se levantó el servidor.

EscribesPHP busca el archivo
include "partes/pie.php";En la carpeta desde donde se ejecuta el programa, y luego en la del archivo. Casi siempre funciona, hasta que las vistas están en subcarpetas
include __DIR__ . "/partes/pie.php";Exactamente en partes/, junto al archivo que lo incluye. Siempre

Correcto: DIR en todos los include y require

Es una costumbre que no cuesta nada y evita la tarde perdida buscando por qué el include funciona en index.php pero no en admin/listado.php. Y recuerda que Linux distingue mayúsculas: Partes/Pie.php no es partes/pie.php.

include, require y _once

InstrucciónSi el archivo no existeSi ya se incluyó antes
includeWarning y la página sigueLo vuelve a incluir
requireFatal error y la página se detieneLo vuelve a incluir
include_onceWarning y sigueNo lo incluye de nuevo
require_onceFatal error y se detieneNo lo incluye de nuevo

Correcto: la regla del curso

  • Partes visuales (cabecera, pie, una tarjeta repetida): include. Se pueden incluir varias veces y, si falta una, el resto de la página se ve igual.
  • Archivos con datos (datos.php y, en la Unidad 3, conexion.php): require_once. Sin ellos la página no puede funcionar, así que es mejor que se detenga con un error claro.
<?php
require_once __DIR__ . "/datos.php";
 
$titulo = "Inicio";
$pagina = "inicio";
include __DIR__ . "/partes/cabecera.php";
?>

Los datos en su propio archivo

Los arreglos con la información del sitio también se separan de las vistas:

<?php
// datos.php
$estudiantes = [
    ["id" => 1, "nombre" => "Ana Pérez", "carrera" => "Análisis de Sistemas", "promedio" => 5.8],
    ["id" => 2, "nombre" => "Benjamín Soto", "carrera" => "Electricidad", "promedio" => 4.2],
];
<?php
require_once __DIR__ . "/datos.php";
$titulo = "Inscritos";
$pagina = "inscritos";
include __DIR__ . "/partes/cabecera.php";
?>
 
<ul>
  <?php foreach ($estudiantes as $estudiante): ?>
    <li><?= $estudiante["nombre"] ?></li>
  <?php endforeach; ?>
</ul>
 
<?php include __DIR__ . "/partes/pie.php"; ?>

datos.php es el ensayo de la base de datos

Hoy los registros están escritos a mano en datos.php. En la Unidad 3, ese archivo se reemplaza por una consulta a PostgreSQL que entrega un arreglo con la misma forma, y las vistas no cambian.

Pruébalo

  • Crea partes/cabecera.php y partes/pie.php y úsalos en index.php
  • Inclúyelos también en las otras dos vistas y comprueba que las tres se ven iguales
  • Cambia el nombre de un enlace en la cabecera y mira que cambia en las tres vistas
  • Marca la página activa con $pagina y recorre las tres vistas
  • Error provocado: escribe mal el nombre del archivo en un include y luego en un require, y compara
  • Error provocado: define $titulo después del include y lee en qué archivo dice el error

Errores comunes

SíntomaCausaSolución
include(partes/pie.php): Failed to open stream: No such file or directoryRuta o nombre mal escrito, o mayúsculas distintasUsa __DIR__ y revisa el nombre con ls
Undefined variable $titulo in …/cabecera.phpLa variable se definió después del include, o no se definió en esa vistaDefine las variables antes del include, en todas las vistas
La navbar no marca ninguna páginaEl valor de $pagina no coincide con el que compara la cabeceraMismo texto exacto en la vista y en la cabecera
Al abrir partes/cabecera.php en el navegador se ve una página rotaLas partes no son páginas: no se abren solasAbre siempre la vista (index.php)

Checkpoint

Intenta responder antes de desplegar cada pregunta.