🐘 Curso PHP · Nivel N2 Básico

Bloque 2: Arrays, ficheros y sesiones

En este bloque tu PHP empieza a recordar y persistir: dominarás las colecciones (arrays) que organizan tus datos, la lectura y escritura de ficheros en el servidor, y las sesiones y cookies que dan memoria a tus páginas entre visitas. Terminarás construyendo un pequeño carrito de compra con sesión. ¡Aquí tu web deja de ser «estática» para siempre!

⏱ ≈ 14 horas 📚 5 lecciones ✍️ 18 ejercicios guiados 📝 Examen final superable (≥ 70 %)
Lección 1 · Teoría

Arrays indexados y asociativos

Un array es una variable que guarda una colección ordenada de valores. PHP los crea con corchetes [] (sustituyendo a la antigua array(), aún válida). Hay dos familias:

1.1 · Arrays indexados (listas)

Cada elemento tiene un índice numérico automático que empieza en 0:

listas con índices numéricos
$paises = ["España", "Francia", "Italia"];

echo $paises[0];        // "España"
echo $paises[2];        // "Italia"
echo count($paises);   // 3 — cuántos elementos hay

$paises[] = "Portugal"; // se añade al final
var_dump($paises);

1.2 · Arrays asociativos (mapas clave → valor)

En lugar de números, cada elemento tiene una clave con nombre (key => value). Son perfectos para representar a una persona, un producto, un usuario…:

claves con nombre
$usuario = [
    "nombre" => "Raúl",
    "edad"   => 34,
    "ciudad" => "Madrid",
];

echo $usuario["nombre"];   // "Raúl"
echo $usuario["edad"];     // 34

1.3 · Arrays multidimensionales

Un array cuyos elementos son a su vez arrays: la forma natural de modelar «una lista de usuarios» o «una lista de productos». Los usarás constantemente con bases de datos:

array de arrays
$productos = [
    ["nombre" => "Ratón",  "precio" => 9.99],
    ["nombre" => "Teclado","precio" => 24.50],
    ["nombre" => "Monitor","precio" => 129.00],
];

echo $productos[0]["nombre"];  // "Ratón"
echo $productos[2]["precio"];  // 129
💡 Los arrays asociativos y multidimensionales son el pan de cada día de PHP: representan los registros que llegan de una base de datos, los campos de un formulario ($_POST, $_GET) y las sesiones. Domina esta sintaxis.
Lección 2 · Teoría

Funciones de arrays y ordenación

2.1 · Añadir y quitar elementos

modificar colecciones
$frutas = ["manzana", "pera"];

array_push($frutas, "uva");   // añade al final: [.., "uva"]
$frutas[] = "naranja";      // igual que arriba
array_pop($frutas);            // quita el último → "naranja"
array_shift($frutas);          // quita el primero → "manzana"

in_array("pera", $frutas);   // true — está dentro
array_sum([2, 3, 4]);          // 9 — suma
count([1, 2]);                  // 2 — longitud

2.2 · Recorrer con foreach (ambos formatos)

valores solos y con clave
$usuario = ["nombre" => "Raúl", "edad" => 34];

// solo valores
foreach ($usuario as $valor) {
    echo "$valor ";
}

// clave => valor
foreach ($usuario as $clave => $valor) {
    echo "$clave: $valor\n";
}

2.3 · Ordenar arrays

Existen funciones de ordenación para cada caso; lo importante es recordar cuál trabaja sobre el array in situ (lo modifica) y su familia:

Ordenar por valores o por claves
FunciónQué ordenaDirección
sort()Valores (reindexa)Ascendente
rsort()Valores (reindexa)Descendente
asort()Valores (conserva claves)Ascendente
arsort()Valores (conserva claves)Descendente
ksort()ClavesAscendente
krsort()ClavesDescendente
ordenar y rever
$notas = [8, 3, 9, 5];
sort($notas);             // [3, 5, 8, 9]

$frutas = ["pera", "uva", "manzana"];
sort($frutas);            // ['manzana', 'pera', 'uva'] — alfabético

$n = [1..5];               // PHP 8.1+: range literal 1..5 = [1,2,3,4,5]
$rev = array_reverse($n); // [5,4,3,2,1]
⚠️ sort() y rsort() reindexan: si usas claves con nombre en un array asociativo, se las pierde. Emplea asort()/arsort() para conservarlas.
Lección 3 · Teoría

Ficheros: leer y escribir

Con PHP puedes leer y escribir archivos en el servidor: guardar datos de un formulario, contadores de visitas, logs, configuraciones… Estas funciones lo hacen muy sencillo.

3.1 · Escribir un fichero: file_put_contents

grabar texto en un archivo
// Crea o sobrescribe el archivo con el contenido dado
file_put_contents("datos.txt", "Hola, fichero!\n");
file_put_contents("datos.txt", "Más texto\n", FILE_APPEND); // añade al final

3.2 · Leer un fichero: file_get_contents y file

dos formas de leer
// Todo el contenido en un string
$contenido = file_get_contents("datos.txt");
echo $contenido;

// Cada línea en un elemento de array
$lineas = file("datos.txt");   // ["Hola...","Más texto\n"]

// Comprobar que existe
if (file_exists("datos.txt")) {
    echo "El archivo existe.\n";
} else {
    echo "El archivo no existe.\n";
}

3.3 · Serializar datos: cuando guardas arrays/objetos

Para guardar un array o un objeto en un fichero y poder recuperarlo tal cual, se usa serialize()/unserialize(), o el superobjeto json_encode()/ json_decode() (el más recomendado para intercambiar con otras herramientas):

guardar y recuperar arrays
$usuario = ["nombre" => "Raúl", "edad" => 34];

// Guardar como JSON
file_put_contents("usuario.json", json_encode($usuario));

// Recuperar
$json = file_get_contents("usuario.json");
$datos = json_decode($json, true); // true → array asociativo
echo $datos["nombre"];
⚠️ Seguridad: al guardar en ficheros toma control de la ruta. Nunca laves directamente nombres de archivo a partir de datos de usuario sin sanitizarlos (ver Bloque 3: inyección y rutas). Por ahora trabaja en la propia carpeta del proyecto.
Lección 4 · Teoría

Sesiones, cookies y autenticación

HTTP es un protocolo sin memoria: cada petición es independiente. Para que una web «recuerde» quién eres o qué tienes en el carrito, usamos cookies (datos en el navegador) y sesiones (datos guardados en el servidor, asociados a un identificador enviado por cookie).

4.1 · Sesiones: la memoria del servidor

iniciar y guardar en la sesión
// SIEMPRE al principio, antes de CUALQUIER salida HTML
session_start();

// Guardar datos en la sesión
$_SESSION["usuario"] = "raul";
$_SESSION["carrito"] = ["raton" => 2, "monitor" => 1];

// Leer
echo $_SESSION["usuario"];   // raul

4.2 · Borrar y destruir la sesión

cerrar sesión de forma segura
session_start();

// Vaciar las variables
$_SESSION = [];

// Borrar la cookie de sesión del navegador
if (ini_get("session.use_cookies")) {
    $params = session_get_cookie_params();
    setcookie(session_name(), "", time() - 42000, $params["path"]);
}

session_destroy();   // destruye los datos en el servidor

4.3 · Cookies con setcookie

recordar una preferencia en el navegador
// setcookie(nombre, valor, expiración); tiempo en segundos
setcookie("idioma", "es", time() + 3600 * 24 * 30); // 30 días

// La cookie solo está disponible en la SIGUIENTE carga
echo $_COOKIE["idioma"] ?? "es por defecto";
⚠️ setcookie() y session_start() deben ejecutarse antes de enviar cualquier HTML (incluso un espacio en blanco antes de <?php) porque modifican las cabeceras HTTP. Si ves el aviso «headers already sent», es por eso.

4.4 · Autenticación básica en memoria

Un patrón de login mínimo: comprobar credenciales y guardar la decisión en sesión. En el Bloque 3 lo harás contra una base de datos y con contraseñas con hash (nunca en claro):

login simplificado
session_start();

// Usuarios «de ejemplo» (¡en el Bloque 3 vendrán de la BD con hash!)
$usuarios = [
    "raul"    => "secreto123",
    "alba"    => "otrapass",
];

$enviado = $_SERVER["REQUEST_METHOD"] === "POST";
$nombre = $_POST["usuario"] ?? "";
$clave  = $_POST["clave"] ?? "";

if ($enviado && isset($usuarios[$nombre]) &&
    $usuarios[$nombre] === $clave) {
    $_SESSION["logueado"] = true;
    $_SESSION["usuario"] = $nombre;
    header("Location: area-privada.php"); // redirigir tras login
    exit;
}

// Comprobar en páginas protegidas
if (empty($_SESSION["logueado"])) {
    header("Location: login.php");
    exit;
}
💡 $_POST, $_GET, $_SESSION, $_COOKIE, $_SERVER… son los llamados superglobales: arrays asociativos automáticos que PHP prepara en cada petición. Son la puerta de entrada de los datos del usuario (¡y de su ataque favorito, la inyección, que verás en el Bloque 3!).
Lección 5 · Teoría

Incluir plantillas y modularizar

Para no repetir la cabecera, el menú y el pie en cada página, PHP permite incluir otros archivos PHP. La diferencia entre include y require: si el archivo falta, require provoca un error fatal que detiene todo, mientras que include solo lanza un aviso y continúa. Hay variantes _once para evitar cargarlos dos veces.

include vs require
InstrucciónSi el archivo no existeÚsala para…
includeAviso y siguePlantillas no críticas
requireError fatal y paraCódigo imprescindible
include_once / require_once-Cuando puede cargarse en varios puntos

5.1 · Dividir la interfaz en plantillas

Creamos plantillas reutilizables y cada página las «monta»:

header.php (plantilla)
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title><?= $titulo ?></title>
</head>
<body>
    <nav><a href="index.php">Inicio</a></nav>
footer.php (plantilla)
    <footer>&copy; <?= date("Y") ?> TécnicoWeb</footer>
</body>
</html>
index.php (monta las plantillas)
<?php
    $titulo = "Inicio";
    require "header.php";   // si falta, error fatal: es vital
?>

<main>
    <h1>Bienvenido</h1>
    <p>Contenido propio de esta página.</p>
</main>

<?php
    include "footer.php";  // no crítico: include basta
?>
💡 Con require las variables declaradas en el script que incluye quedan disponibles dentro del archivo incluido (por eso $titulo llega a header.php). Es una forma rudimentaria de «plantillas» que perfeccionarás con POO y Composer en el Bloque 3.
Práctica guiada

✍️ Los 18 ejercicios del bloque

Crea la carpeta php-b2/ dentro de htdocs y abre cada archivo a través de localhost/php-b2/…. Los ejercicios de formularios y sesiones necesitan una petición real al servidor (no bastan en la consola).

Sección A · Arrays

1 Lista de frutas

Crea un array indexado $frutas con al menos 4 frutas. Muestra la primera, la última (usa count() para el índice final) y el total con count().

Ver solución
$frutas = ["manzana", "pera", "uva", "naranja"];

echo "Primera: {$frutas[0]}\n";
echo "Última: {$frutas[count($frutas) - 1]}\n";
echo "Total: " . count($frutas) . "\n";

2 Ficha de usuario

Crea un array asociativo $usuario con nombre, edad y ciudad. Muestra cada valor por separado y después recórrelo con foreach ($usuario as $clave => $valor) mostrando clave: valor en cada línea.

Ver solución
$usuario = [
    "nombre" => "Raúl",
    "edad"   => 34,
    "ciudad" => "Madrid",
];

foreach ($usuario as $clave => $valor) {
    echo "$clave: $valor\n";
}

3 Suma de notas con array_sum

Ten el array $notas = [8, 9, 6, 10, 5]. Calcula y muestra la suma con array_sum(), el total de notas con count() y la media (divisiones). Imprime la media con dos decimales usando number_format().

Ver solución
$notas = [8, 9, 6, 10, 5];

$suma  = array_sum($notas);
$total = count($notas);
$media = $suma / $total;

echo "Suma: $suma\n";
echo "Media: " . number_format($media, 2) . "\n";

4 Lista de productos

Crea un array multidimensional $productos con 3 productos, cada uno con nombre y precio. Recórrelo con foreach mostrando «nombre — precio €» por línea. Añade un producto nuevo usando array_push() y muestra de nuevo la lista.

Ver solución
$productos = [
    ["nombre" => "Ratón",  "precio" => 9.99],
    ["nombre" => "Teclado","precio" => 24.50],
    ["nombre" => "Monitor","precio" => 129.00],
];

foreach ($productos as $p) {
    echo $p["nombre"] . " — " . $p["precio"] . " €\n";
}

array_push($productos, ["nombre" => "Webcam", "precio" => 39.90]);
foreach ($productos as $p) {
    echo $p["nombre"] . " — " . $p["precio"] . " €\n";
}

5 ¿Está en la lista?

Con el array $paises = ["España", "Francia", "Italia"], comprueba con in_array() si «España» y «Japón» están en la lista y muestra un mensaje en cada caso con un ternario o if/else.

Ver solución
$paises = ["España", "Francia", "Italia"];

echo in_array("España", $paises) ? "España: sí\n" : "España: no\n";
echo in_array("Japón", $paises) ? "Japón: sí\n" : "Japón: no\n";

6 Ordenar y dar la vuelta

Con $notas = [8, 3, 9, 5]: muéstralas ordenadas de menor a mayor con sort(), usa rsort() para de mayor a menor y array_reverse() para invertir. Comenta qué cambia en cada paso.

Ver solución
$notas = [8, 3, 9, 5];

sort($notas);  var_dump($notas); // [3,5,8,9]
rsort($notas); var_dump($notas); // [9,8,5,3]

$invertidas = array_reverse($notas);
var_dump($invertidas); // [3,5,8,9] (no modifica el original)

Sección B · Ficheros

7 Guardar un mensaje

Usa file_put_contents() para guardar en mensajes.txt una línea con tu nombre y la fecha actual con date("Y-m-d H:i"). Ejecuta el script dos veces y luego lee y muestra el contenido con file_get_contents().

Ver solución
$nombre = "Raúl";
$mensaje = "$nombre — " . date("Y-m-d H:i") . "\n";

file_put_contents("mensajes.txt", $mensaje, FILE_APPEND); // añade (no sobrescribe)

$contenido = file_get_contents("mensajes.txt");
echo "Contenido:\n$contenido";

8 Contador de visitas

Crea contador.php que lea un número de visitas.txt, le sume 1, lo guarde y muestre «Has visitado esta página N veces.». Pista: lee con file_get_contents(), suma, guarda con file_put_contents(). La primera vez el archivo no existe, usa file_exists() para empezar en 0.

Ver solución
$archivo = "visitas.txt";

$visitas = file_exists($archivo) ?
    (int)file_get_contents($archivo) : 0;

$visitas++;

file_put_contents($archivo, $visitas);

echo "Has visitado esta página $visitas veces.\n";

9 Guardar un array en JSON

Ten un array asociativo con tu ficha. Guárdalo en ficha.json con json_encode() + file_put_contents(), luego léelo y recupéralo con json_decode(..., true). Muestra el nombre recuperado y el JSON en bruto.

Ver solución
$ficha = ["nombre" => "Raúl", "edad" => 34, "ciudad" => "Madrid"];

file_put_contents("ficha.json", json_encode($ficha));

$datos = json_decode(file_get_contents("ficha.json"), true);

echo "Nombre: " . $datos["nombre"] . "\n";
echo "JSON: " . file_get_contents("ficha.json") . "\n";

10 Leer línea a línea

Crea un archivo frutas.txt a mano (o con PHP) con 3 frutas, una por línea. Luego con file() léelo en un array y recórrelo con foreach mostrando cada fruta con su número de línea (1-based).

Ver solución
// creamos el archivo
file_put_contents("frutas.txt", "manzana\npera\nuva\n");

$lineas = file("frutas.txt");

foreach ($lineas as $indice => $fruta) {
    echo ($indice + 1) . ": " . trim($fruta) . "\n";
}

Sección C · Sesiones, cookies y login

11 Sesión con tu nombre

Crea sesion.php que llame a session_start() al principio, guarde en $_SESSION["nombre"] tu nombre y lo muestre. Crea leer.php (también con session_start()) que muestre $_SESSION["nombre"]. Visita primero sesion.php y luego leer.php: deberías ver tu nombre en ambos.

Ver solución
// sesion.php
session_start();
$_SESSION["nombre"] = "Raúl";
echo "Guardado: " . $_SESSION["nombre"] . "\n";

// leer.php
session_start();
echo "Hola, " . ($_SESSION["nombre"] ?? "visitante") . "\n";

12 Preferencia en cookie

Crea cookie.php que establezca con setcookie() una cookie idioma con valor "es" que dure 30 días. En la misma página muestra la cookie si ya existe (usa $_COOKIE["idioma"] ?? "sin preferencia"). Recarga varias veces y comenta cuándo aparece.

Ver solución
$idioma = $_COOKIE["idioma"] ?? "";

if ($idioma === "") {
    setcookie("idioma", "es", time() + 3600 * 24 * 30);
    echo "Cookie creada. Recarga para verla.\n";
} else {
    echo "Tu idioma: $idioma\n";
}

13 Login en memoria

Crea login.php (con session_start()) con un array de usuarios, un formulario muy simple y la lógica del patrón visto en la lección 4.4. Si las credenciales coinciden, guarda $_SESSION["logueado"] = true y muestra «Bienvenido». Si no, muestra un error.

Ver solución
session_start();

$usuarios = ["raul" => "secreto123", "alba" => "otrapass"];

$mensaje = "";
$nombre = $_POST["usuario"] ?? "";
$clave  = $_POST["clave"] ?? "";

if ($nombre !== "") {
    if (isset($usuarios[$nombre]) && $usuarios[$nombre] === $clave) {
        $_SESSION["logueado"] = true;
        $_SESSION["usuario"] = $nombre;
        $mensaje = "Bienvenido, $nombre";
    } else {
        $mensaje = "Credenciales incorrectas.";
    }
}
?>
<form method="post">
    <input name="usuario" placeholder="usuario">
    <input name="clave" type="password" placeholder="clave">
    <button>Entrar</button>
</form>
<p><?= $mensaje ?></p>

14 Página protegida

Con sesion.php del ejercicio 12 que marca protección, crea privada.php que compruebe $_SESSION["logueado"]; si no está, muestre «Acceso denegado», y si está muestre «Área privada». Prueba con y sin haber «logueado».

Ver solución
session_start();

if (empty($_SESSION["logueado"])) {
    echo "Acceso denegado.\n";
} else {
    echo "Área privada. Bienvenido, " . $_SESSION["usuario"] . "\n";
}

15 Cerrar sesión

Crea logout.php que vacíe $_SESSION, borre la cookie de sesión y destruya la sesión, mostrando «Sesión cerrada». Enlázalo desde la página privada del ejercicio anterior.

Ver solución
session_start();

$_SESSION = [];
$params = session_get_cookie_params();
setcookie(session_name(), "", time() - 42000, $params["path"]);
session_destroy();

echo "Sesión cerrada.\n";

Sección D · Plantillas y modularización

16 Header y footer reutilizables

Crea header.php y footer.php (como en la lección 5) y dos páginas inicio.php y acerca.php que los incluyan con require/ include, diferenciándose solo en el contenido central. Comprueba que el menú aparece en ambas sin repetirlo.

Ver solución
// header.php
<!DOCTYPE html>
<html lang="es">
<head><meta charset="UTF-8"><title><?= $titulo ?></title></head>
<body><nav><a href="inicio.php">Inicio</a> <a href="acerca.php">Acerca</a></nav>

// footer.php
<footer>&copy; <?= date("Y") ?> TécnicoWeb</footer>
</body></html>

// inicio.php
<?php $titulo = "Inicio"; require "header.php"; ?>
<h1>Bienvenido a mi web</h1>
<?php include "footer.php"; ?>

17 include vs require

Crea un script que intente include "no-existe.php" y muestre «Sigo vivo» después, y otro que haga require "no-existe.php" y muestre «No llego aquí». Compara los errores y explica en un comentario cuándo usarías cada uno.

Ver solución
// con_include.php → muestra aviso y continúa
include "no-existe.php";
echo "Sigo vivo tras include.\n";

// con_require.php → error fatal y se detiene
require "no-existe.php";
echo "No llego aquí.\n"; // no se mostrará

18 🏆 Proyecto integrador: «carrito.php»

Monta un mini carrito de compra con sesión que acumule productos entre recargas:

  • Declara un $productos multidimensional (nombre + precio) con 3+ productos.
  • Llama a session_start() al principio.
  • Usa al menos una función de arrays: count(), in_array(), array_sum(), array_push()…
  • Guarda y acumula el carrito en $_SESSION["carrito"] (un array asociativo con cantidades).
  • Recorre el carrito con foreach mostrando cada producto y su total.
  • Calcula el total general acumulando con un bucle o array_sum().
  • Lee/escribe al menos una vez un fichero (p. ej. un log de ventas con file_put_contents(..., FILE_APPEND)).

Este ejercicio es el reto del laboratorio del examen: la validación automática comprobará estos criterios sobre el código dentro de las etiquetas <?php ... ?>.

Evaluación del bloque

📝 Examen del Bloque 2

Dos partes: un test teórico autoevaluable (mínimo 70 %) y un reto práctico de autocorrección con checklist.

Parte A · Test teórico (aprueba con 11/15)

1 · ¿Cómo se accede al primer elemento de un array indexado $frutas?
2 · ¿Qué sintaxis crea un array asociativo?
3 · ¿Qué función devuelve cuántos elementos tiene un array?
4 · ¿Qué hace in_array("pera", $frutas)?
5 · ¿Qué bucle es el natural para recorrer un array en PHP?
6 · ¿Qué función ordena conservando las claves de un array asociativo?
7 · ¿Qué función escribe un string en un fichero?
8 · ¿Cómo conviertes un array a JSON para guardarlo?
9 · ¿Qué superglobal contiene los datos enviados por un formulario con method="post"?
10 · ¿Qué instrucción debes llamar para poder usar $_SESSION?
11 · ¿Dónde se guardan los datos de una sesión?
12 · ¿Qué función crea una cookie en el navegador?
13 · ¿Cuál es la diferencia entre include y require?
14 · ¿Qué devuelve json_decode($json, true) cuando el JSON es un objeto?
15 · ¿Para qué sirve FILE_APPEND en file_put_contents()?

Parte B · Reto práctico final

Construye el ejercicio 18 («carrito.php») sin mirar soluciones y valida tú mismo/a estos criterios sobre el contenido de tus etiquetas <?php ... ?>. Mínimo para aprobar: 6 de 7 casillas.

Cumplidos: 0 / 7

🎉 ¿Superaste las dos partes? Enhorabuena: has aprobado el Bloque 2 de PHP. Ya organizas datos en arrays, persistes con ficheros y das memoria a tus páginas con sesiones y cookies. En el Bloque 3 llegarán la POO y las bases de datos seguras: tus aplicaciones empezarán a guardar y proteger datos reales.