UT 03 - PROGRAMACIÓN EN PHP INTEGRADO EN HTML

RA Y CONTENIDOS

RESULTADO DE APRENDIZAJE

Escribe bloques de sentencias embebidos en lenguajes de marcas, seleccionando y utilizando las estructuras de programación.

Criterios de evaluación
Se han utilizado mecanismos de decisión en la creación de bloques de sentencias.
Se han utilizado bucles y se ha verificado su funcionamiento.
Se han utilizado “arrays” para almacenar y recuperar conjuntos de datos.
Se han creado y utilizado funciones.
Se han utilizado formularios web para interactuar con el usuario del navegador web.
Se han empleado métodos para recuperar la información introducida en el formulario.
Se han añadido comentarios al código.

CONTENIDOS

  • Tomas de decisión.
  • Bucles.
  • Tipos de datos compuestos.
  • Arrays.
  • Funciones.
  • Recuperación y utilización de información proveniente del cliente web.
  • Formularios y validación.
  • Procesamiento de la información introducida en un formulario.
  • Comentarios de cliente y de servidor.

1. ESTRUCTURAS DE CONTROL EMBEBIDAS

1.1. Sintaxis del Bloque PHP en HTML

Para embeber código PHP dentro de un documento HTML se utilizan las etiquetas de apertura y cierre <?php y ?>.

Además, PHP ofrece la etiqueta corta de impresión (short echo tag) <?= ... ?>, que equivale exactamente a <?php echo ...; ?> y resulta ideal para volcar variables o expresiones directamente en la plantilla HTML.

<!-- Bloque PHP estándar -->
<?php $titulo = "Bienvenido a mi sitio web"; ?>

<!-- Tag corto de impresión (Short Echo Tag) -->
<h1><?= $titulo ?></h1>

1.2. Sintaxis Alternativa para Estructuras de Control Embebidas

Cuando se mezcla código PHP con HTML, el uso de llaves ({ y }) puede dificultar la lectura y el mantenimiento. PHP proporciona una sintaxis alternativa diseñada específicamente para plantillas HTML, que reemplaza la llave de apertura por dos puntos (:) y la llave de cierre por una palabra clave explícita (endif;, endswitch;, endwhile;, endfor;, endforeach;).

<?php if ($rol === 'admin'): ?>
    <div class="panel admin">Acceso total al panel de administración.</div>
<?php elseif ($rol === 'editor'): ?>
    <div class="panel editor">Acceso a crear, editar y publicar contenido.</div>
<?php else: ?>
    <div class="panel usuario">Acceso de solo lectura (usuario invitado).</div>
<?php endif; ?>

1.3. Estructuras Condicionales

Permiten bifurcar la ejecución en función de expresiones booleanas. Las estructuras condicionales de PHP son if, if-else, if-elseif, switch y match.

La sentencia condicional más sencilla es la estructura if:

if (condición)
    instrucción;

Cuando se utiliza un if se puede añadir un else. Las instrucciones dentro del else solo se ejecutan cuando la condición del if no se cumple.

Si se anidan varias sentencias condicionales, se puede usar elseif, equivalente a else if.

El siguiente ejemplo muestra una serie de condiciones anidadas:

<?php
$rol = 'editor';

if ($rol === 'admin') {
    $mensaje = "Acceso total al panel de administración.";
} elseif ($rol === 'editor') {
    $mensaje = "Acceso a crear, editar y publicar contenido.";
} elseif ($rol === 'autor') {
    $mensaje = "Acceso único a redactar borradores propios.";
} else {
    $mensaje = "Acceso de solo lectura (usuario invitado).";
}

echo $mensaje;
?>

Se ejecuta la primera condición que se cumpla y el resto se ignoran. Si no se cumple ninguna, se ejecuta el else final (si existe).

Para agrupar varios if sobre una misma variable puede ser útil la estructura switch. Según el valor de $var, se ejecutará un case u otro. La sección default se ejecuta cuando no coincide ningún case.

switch ($var) {
    case 1:
        // OPERACIONES
        break;
    case 2:
        // OPERACIONES
        break;
    default:
        echo "Opción no válida";
}

Olvidar el break

Si en un switch se omite el break, PHP continuará ejecutando los bloques de los casos siguientes aunque no coincidan, hasta encontrar un break o el final de la estructura (comportamiento conocido como fallthrough).

El ejemplo siguiente es equivalente al anterior usando switch:

<?php
$rol = 'editor';

switch ($rol) {
    case 'admin':
        $mensaje = "Acceso total al panel de administración.";
        break;

    case 'editor':
        $mensaje = "Acceso a crear, editar y publicar contenido.";
        break;

    case 'autor':
        $mensaje = "Acceso único a redactar borradores propios.";
        break;

    default:
        $mensaje = "Acceso de solo lectura (usuario invitado).";
        break;
}

echo $mensaje;
?>

La debilidad que puede presentar switch es el uso de la comparación débil: en PHP, switch compara con ==. Por ejemplo, case 0: coincidirá con la cadena "hola" debido a la conversión implícita de tipos. Para solventar este problema, PHP 8 introdujo match.

La expresión match es una alternativa más segura y expresiva a la instrucción switch. Se diferencia en que es una expresión (devuelve un valor directamente), utiliza comparación estricta (===) para evitar coerción de tipos, y no requiere sentencias break.

$resultado = match ($opcion) {
    1 => "Opción 1 seleccionada",
    2 => "Opción 2 seleccionada",
    default => "Opción no válida",
};
echo $resultado;

La versión usando match quedaría así:

<?php
$rol = 'editor';

$mensaje = match ($rol) {
    'admin'  => "Acceso total al panel de administración.",
    'editor' => "Acceso a crear, editar y publicar contenido.",
    'autor'  => "Acceso único a redactar borradores propios.",
    default  => "Acceso de solo lectura (usuario invitado).",
};

echo $mensaje;
?>

EN RESUMEN: ¿CUÁNDO USAR CADA ESTRUCTURA?

  • if - elseif - else: Se recomienda usar cuando las condiciones involucran rangos ($edad >= 18), operadores lógicos complejos (&&, ||) o evaluaciones booleanas.
  • match: Se recomienda usar como la elección estándar en desarrollo moderno con PHP 8+ cuando se compara una variable contra un conjunto de valores fijos o enumeraciones (enums).
  • switch: Se utilizará solo en proyectos antiguos o código legacy que necesite mantener compatibilidad con PHP 7.x o inferior.

1.4. Estructuras de Repetición (Bucles)

Permiten ejecutar repetidamente un bloque de código mientras se cumpla una condición.

<?php
// Bucle while
$i = 1;
while ($i <= 3) {
    echo "While: $i<br>";
    $i++;
}

// Bucle do-while (se ejecuta al menos una vez)
$j = 1;
do {
    echo "Do-While: $j<br>";
    $j++;
} while ($j <= 3);

// Bucle for
for ($k = 1; $k <= 3; $k++) {
    echo "For: $k<br>";
}

// Bucle foreach (específico para recorrer arrays)
$colores = ["Rojo", "Verde", "Azul"];
foreach ($colores as $clave => $valor) {
    echo "Índice $clave: $valor<br>";
}

1.5. Otras Estructuras de Control e Inclusión de Ficheros

  • break: Interrumpe la ejecución del bucle o switch actual.
  • continue: Salta la iteración actual del bucle y pasa a la evaluación de la siguiente.
  • Inclusión de ficheros externos:
Instrucción Descripción
include 'fichero.php' Incluye y evalúa el fichero. Si falla, emite un Warning y el script continúa.
require 'fichero.php' Incluye y evalúa el fichero. Si falla, emite un Fatal Error y detiene el script.
include_once 'fichero.php' Igual que include, pero evita inclusiones duplicadas si ya fue incluido.
require_once 'fichero.php' Igual que require, garantizando una única inclusión del fichero.

2. ARRAYS

Los arrays en PHP son estructuras de datos ordenados que asocian claves con valores. Las claves pueden ser enteros o cadenas.

2.1. Concepto y Tipos de Arrays

// Array Indexado (claves numéricas automáticas)
$frutas = ["Manzana", "Plátano", "Naranja"];
echo $frutas[0]; // Manzana
$frutas[] = "Uva"; // Añade al final

Ejemplo de Array Asociativo (claves de texto):

$persona = [
    "nombre" => "Juan",
    "edad" => 25,
    "email" => "juan@example.com"
];
echo $persona["nombre"]; // Juan

Ejemplo de Array Multidimensional:

$matriz = [
    ["A", "B"],
    ["C", "D"]
];
echo $matriz[1][0]; // C

2.2. Funciones Principales para la Manipulación de Arrays

Función Descripción
count($array) Devuelve el número total de elementos del array.
in_array($valor, $array) Comprueba si un valor existe en el array.
array_key_exists($clave, $array) Verifica si una clave o índice existe en el array.
array_push($array, $elem) Añade uno o más elementos al final.
array_pop($array) Extrae y elimina el último elemento.
sort($array) / rsort($array) Ordena el array por valor de forma ascendente / descendente.
ksort($array) / krsort($array) Ordena un array asociativo por sus claves.

2.3. Bucles Embebidos (foreach con Sintaxis Alternativa)

<?php 
$productos = ["Teclado", "Ratón", "Monitor"];
?>

<h3>Catálogo de Productos</h3>
<ul>
    <?php foreach ($productos as $producto): ?>
        <li><?= $producto ?></li>
    <?php endforeach; ?>
</ul>

3. FUNCIONES

3.1. Declaración de Funciones

Una función es un bloque de código estructurado reutilizable, que puede ser invocado dentro de nuestro script.

<?php
// Función con tipos declarados y valor por defecto (PHP 7+)
function calcularIVA(float $precio, float $iva = 0.21): float {
    return $precio * (1 + $iva);
}

echo calcularIVA(100);       // 121
echo calcularIVA(100, 0.10);  // 110

3.2. Callbacks, Funciones Anónimas y Arrow Functions

// Función anónima (Closure)
$multiplicar = function($a, $b) {
    return $a * $b;
};

// Arrow Function (PHP 7.4+)
$cuadrado = fn($x) => $x * $x;

echo $cuadrado(4); // 16

4. FORMULARIOS

El protocolo HTTP es sin estado (stateless). Para interactuar con los usuarios, las aplicaciones web emplean formularios HTML que envían información al servidor mediante peticiones HTTP.

PHP captura automáticamente los parámetros enviados mediante arrays superglobales:

  • $_GET: Almacena los parámetros pasados en la URL (Query String). Adecuado para búsquedas, paginación y datos no sensibles.
  • $_POST: Almacena los parámetros enviados en el cuerpo (body) de la petición HTTP. Adecuado para modificación de datos, contraseñas y operaciones sensibles.
  • $_REQUEST: Unión de $_GET, $_POST y $_COOKIE. Se recomienda evitar su uso por motivos de seguridad y claridad.

Contenido del archivo "formulario.html":

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Contacto</title>
</head>
<body>
    <form action="procesar.php" method="POST">
        <label for="nombre">Nombre:</label>
        <input type="text" id="nombre" name="nombre" required>

        <label for="email">Email:</label>
        <input type="email" id="email" name="email" required>

        <button type="submit">Enviar</button>
    </form>
</body>
</html>

Contenido del archivo "procesar.php":

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nombre = $_POST['nombre'] ?? '';
    $email  = $_POST['email'] ?? '';

    echo "Datos recibidos correctamente:<br>";
    echo "Nombre: " . htmlspecialchars($nombre) . "<br>";
    echo "Email: " . htmlspecialchars($email) . "<br>";
} else {
    // Redirección con el patrón PRG (Post / Redirect / Get)
    header("Location: formulario.html");
    exit(); // Es fundamental llamar a exit() para detener la ejecución inmediatamente tras la redirección
}

IDENTIFICA EL MÉTODO HTTP Y APLICA EL PATRÓN PRG

Es una buena práctica comprobar el verbo HTTP utilizado en la petición mediante $_SERVER['REQUEST_METHOD'] === 'POST' antes de procesar los datos. Si la petición no entra por POST, redirigimos al usuario y cortamos inmediatamente la ejecución con exit() o die().

4.1. Recogida y Tipos de Datos en Formularios

Los formularios pueden enviar desde valores de texto simples hasta colecciones de datos mediante checkboxes o menús de selección múltiple.

<?php
// Recogida de Checkboxes como Array
$intereses = $_POST['intereses'] ?? []; // Ejemplo: <input type="checkbox" name="intereses[]" value="php">

if (is_array($intereses)) {
    foreach ($intereses as $interes) {
        echo "Interés seleccionado: " . htmlspecialchars($interes) . "<br>";
    }
}
?>

4.2. Sanitización y Validación de Datos de Entrada

Nunca se debe confiar en la información suministrada por el usuario. La sanitización limpia los datos para prevenir inyecciones (como XSS), mientras que la validación comprueba que los datos cumplan con el formato y rango esperados.

PHP ofrece la extensión filter con funciones integradas como filter_input() y filter_var().

4.2.1. Ejemplos con filter_input() y filter_var()

<?php
$errores = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 1. Sanitización de texto
    $nombre = filter_input(INPUT_POST, 'nombre', FILTER_SANITIZE_SPECIAL_CHARS);

    // 2. Validación de Email
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    if (!$email) {
        $errores[] = "El correo electrónico introducido no es válido.";
    }

    // 3. Validación de Entero con Rango
    $edad = filter_input(
        INPUT_POST, 
        'edad', 
        FILTER_VALIDATE_INT, 
        [
            "options" => [
                "min_range" => 18, 
                "max_range" => 99
            ]
        ]
    );

    if ($edad === false) {
        $errores[] = "La edad debe ser un número entero entre 18 y 99.";
    }

    if (empty($errores)) {
        echo "Procesamiento exitoso para $nombre ($email), Edad: $edad";
    }
}
?>

4.2.2. Prevención de XSS con htmlspecialchars()

Cuando mostremos en pantalla datos introducidos por el usuario, debemos escapar caracteres especiales HTML (<, >, &, ", ') para evitar ejecuciones maliciosas de scripts en el navegador del usuario:

// Convierte caracteres especiales en entidades HTML (ej. '<script>' -> '&lt;script&gt;')
$comentarioSeguro = htmlspecialchars($_POST['comentario'], ENT_QUOTES, 'UTF-8');

4.3. Subida de Archivos al Servidor

Para permitir el envío de archivos desde un formulario HTML, es imprescindible utilizar el método POST y añadir el atributo enctype="multipart/form-data".

4.3.1. La Superglobal $_FILES

Cuando se sube un archivo, PHP puebla el array bidimensional $_FILES con los siguientes campos:

  • $_FILES['archivo']['name']: Nombre original del fichero en la máquina cliente.
  • $_FILES['archivo']['type']: Tipo MIME proporcionado por el navegador (no fiable).
  • $_FILES['archivo']['size']: Tamaño del fichero en bytes.
  • $_FILES['archivo']['tmp_name']: Ruta temporal donde se almacenó el fichero en el servidor.
  • $_FILES['archivo']['error']: Código de error asociado a la subida (UPLOAD_ERR_OK, UPLOAD_ERR_INI_SIZE, etc.).

4.3.2. Script Completo de Subida Segura

<?php
$directorioSubida = __DIR__ . '/uploads/';
$tamanoMaximo     = 2 * 1024 * 1024; // 2 Megabytes
$tiposPermitidos  = ['image/jpeg', 'image/png', 'application/pdf'];

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['documento'])) {
    $archivo = $_FILES['documento'];

    // 1. Verificar si ocurrió un error en la subida
    if ($archivo['error'] !== UPLOAD_ERR_OK) {
        die("Error en la subida del archivo. Código: " . $archivo['error']);
    }

    // 2. Validar tamaño
    if ($archivo['size'] > $tamanoMaximo) {
        die("El archivo excede el tamaño máximo permitido (2 MB).");
    }

    // 3. Validar tipo MIME real utilizando finfo
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeTipoReal = finfo_file($finfo, $archivo['tmp_name']);
    finfo_close($finfo);

    if (!in_array($mimeTipoReal, $tiposPermitidos, true)) {
        die("Formato de archivo no permitido: $mimeTipoReal");
    }

    // 4. Generar un nombre único e impredecible para evitar sobrescrituras y ataques
    $extension = pathinfo($archivo['name'], PATHINFO_EXTENSION);
    $nombreNuevo = bin2hex(random_bytes(16)) . '.' . $extension;
    $rutaDestino = $directorioSubida . $nombreNuevo;

    // 5. Mover el archivo desde la carpeta temporal al destino definitivo
    if (move_uploaded_file($archivo['tmp_name'], $rutaDestino)) {
        echo "Archivo subido con éxito: " . htmlspecialchars($nombreNuevo);
    } else {
        echo "Ocurrió un error al guardar el archivo.";
    }
}
?>

5. COMENTARIOS

5.1. Comentarios de Servidor (PHP) vs. Comentarios de Cliente (HTML)

Es fundamental distinguir entre los comentarios que son procesados en el servidor web y los que son interpretados por el navegador del cliente:

  • Comentarios de Servidor (PHP): Son interpretados por el motor de PHP al procesar el script en el servidor. Nunca son enviados al cliente ni se muestran en el código fuente renderizado por el navegador (Ver código fuente).
  • De línea: comenzando por // o por #.
  • De bloque: encerrados entre /* y */.

  • Comentarios de Cliente (HTML): Tienen la sintaxis <!-- Comentario -->. PHP no los interpreta (salvo que estén dentro de un bloque PHP); se envían intactos al navegador cliente y sí son visibles si el usuario consulta el código fuente de la página web.

<?php
// Comentario de línea en PHP (invisible para el cliente)
$a = 0; // Comentario al final de sentencia

/* Comentario de bloque
   exclusivo para desarrolladores del servidor
*/

# Comentario de una sola línea (estilo Shell/Perl)
?>

<!-- Comentario HTML (Enviado al navegador; visible en "Ver código fuente") -->

COMENTARIOS ESPECIALES (TODO)

Algunos IDEs (como VS Code, PhpStorm o NetBeans) registran los comentarios de servidor que comienzan con TODO como tareas pendientes en un panel específico, facilitando el seguimiento del trabajo en equipo.