43 ejercicios de PHP de los módulos de Desarrollo Web en Entorno Servidor. PHP 8 de los temas de desarrollo web en servidor: arrays y funciones, formularios, sesiones, POO, PDO con bases de datos y APIs REST.
Cada ejercicio trae enunciado, pistas y pruebas automáticas: escribes el código aquí mismo, lo ejecutas y ves al momento si es correcto. Tu progreso se guarda en el tema del que sale cada ejercicio.
DWES · Arquitecturas y Tecnologías del Servidor ver el tema
Analiza una petición HTTP
Medio
La entrada es una petición HTTP tal y como llega al servidor: la línea de petición, las cabeceras, una línea en blanco y, a veces, un cuerpo en formato de formulario (application/x-www-form-urlencoded). Muestra «Método: …», «Ruta: …» (sin la query string), una…
Construye la respuesta
Medio
Cada línea de la entrada describe una respuesta con «código;tipo;cuerpo». Escribe la respuesta HTTP completa: la línea de estado «HTTP/1.1 código texto» (200 OK, 201 Created, 204 No Content, 301 Moved Permanently, 404 Not Found, 500 Internal Server Error), «Co…
Un enrutador con parámetros
Difícil
Así deciden los frameworks qué código ejecutar para cada URL. La entrada empieza con rutas «MÉTODO patrón acción» (el patrón puede tener parámetros como /productos/{id}), una línea «---» y después peticiones «MÉTODO url». Para cada petición escribe la acción y…
PHP genera HTML. Lee un número de la entrada y escribe su tabla de multiplicar del 1 al 10 como una tabla HTML, con una fila por línea y dos espacios de sangría: « <tr><td>7 x 1</td><td>7</td></tr>». Si la entrada no es un entero entre 1 y 100, escribe «<p>Núm…
Encuentra el fallo: la frase que empieza por hola
Fácil
El programa dice qué frases contienen la palabra «hola». Con «hola mundo» responde que no la contiene, aunque es lo primero que aparece. Corrige la comprobación.
Encuentra el fallo: el saludo que suma
Fácil
El programa debería mostrar «Hola, Ana. Tienes 3 mensajes.», pero termina con un error. Corrígelo.
Las palabras más repetidas
Medio
Lee un texto (puede tener varias líneas) y muestra las tres palabras más frecuentes con el formato «palabra: veces», en minúsculas. Una palabra es una secuencia de letras (incluidas las tildes y la ñ). Si dos palabras se repiten lo mismo, va primero la que sea…
Ticket de compra
Medio
Cada línea de la entrada es «producto;precio;cantidad». Escribe una línea por producto con el formato «Teclado x 2 = 49,98 €» y al final la base, el IVA y el total: «Base: 62,48 €», «IVA (21%): 13,12 €», «Total: 75,60 €». Las cantidades usan coma decimal y pun…
Convertir y validar los datos de un formulario
Medio
Todo lo que llega de un formulario es texto, y convertirlo bien es la primera defensa. Cada línea de la entrada es «campo=valor». Valida y convierte según el campo: edad (entero de 0 a 120), email (dirección válida), nota (decimal de 0 a 10; admite coma o punt…
Encuentra el fallo: la subida de precios que no sube
Medio
El programa debería subir todos los precios un 10 % (redondeado a dos decimales) y listarlos, pero salen los precios de antes. Corrígelo.
Calendario del mes en HTML
Difícil
La entrada es un mes con el formato AAAA-MM. Genera su calendario como tabla HTML con la semana empezando en lunes: primero «<table>», una fila de cabecera « <tr><th>L</th><th>M</th><th>X</th><th>J</th><th>V</th><th>S</th><th>D</th></tr>», después una fila por…
Tu propio motor de plantillas
Muy difícil
Twig o Blade separan el HTML del código con un lenguaje de plantillas. Construye uno pequeño. La primera línea de la entrada son los datos en JSON y el resto, la plantilla. Soporta: {{ nombre }} y {{ usuario.email }} (valor escapado con htmlspecialchars; si no…
DWES · Formularios, Sesiones y Seguridad ver el tema
Comentarios sin XSS
Fácil
Cada línea es un comentario «autor|texto» que han escrito los usuarios. El código ya los muestra como <li>, pero es vulnerable: un usuario podría colar HTML o JavaScript. Corrígelo para que los caracteres especiales se escapen (< > & " ').
Validar los datos de un formulario
Medio
La entrada es el cuerpo de un formulario enviado por POST (nombre=Ana&email=…&edad=…&web=…). Valida y escribe un error por línea, en este orden: «nombre: obligatorio» o «nombre: máximo 50 caracteres»; «email: no válido»; «edad: debe estar entre 18 y 120» (ente…
Un carrito guardado en la sesión
Medio
Simulamos $_SESSION con un array. Cada línea es una acción del usuario: «anadir id cantidad», «quitar id», «vaciar» o «ver». El catálogo tiene precios y stock. Al añadir, la cantidad debe ser un entero positivo y el total de ese producto en el carrito no puede…
Encuentra el fallo: el comentario que ejecuta código
Medio
La función pinta un comentario de un formulario. Si alguien escribe una etiqueta <script>, se mete en la página tal cual (XSS). Corrige la función para que el autor y el texto se muestren escapados.
Inicio de sesión con bloqueo
Difícil
Las contraseñas se guardan cifradas con password_hash. Cada línea de la entrada es un intento «usuario contraseña» (la contraseña es lo que va después del primer espacio). Escribe «Bienvenido/a, usuario» si es correcto o «Usuario o contraseña incorrectos» si n…
Cookies firmadas
Difícil
El usuario puede cambiar sus cookies, pero sí podemos detectar si lo ha hecho. Firmaremos cada valor con un HMAC: la cookie guarda «valor.firma», donde firma es hash_hmac('sha256', valor, clave secreta). Cada línea es «firmar texto» (escribe la cookie resultan…
Control de acceso por roles y propiedad
Muy difícil
La entrada tiene tres bloques separados por «---». Primero, los roles: «rol NOMBRE: permiso1, permiso2» o «rol NOMBRE hereda PADRE: permisos» (un rol tiene sus permisos y todos los de sus antepasados). Después, los usuarios: «NOMBRE: ROL». Por último, las peti…
Completa la clase Producto: el constructor usa promoción de propiedades (string $nombre, float $precio, int $stock = 0) y lanza InvalidArgumentException con «El nombre es obligatorio», «El precio no puede ser negativo» o «El stock no puede ser negativo». preci…
Nóminas con herencia e interfaces
Medio
Completa la jerarquía. Empleado es abstracta: guarda el nombre y obliga a implementar salario(). Fijo cobra su sueldo base más 50 € por cada trienio (3 años completos de antigüedad); PorHoras cobra horas × precio, y las que pasen de 160 a precio × 1,5. Comerci…
Encuentra el fallo: los intereses que no llegan
Medio
CuentaAhorro hereda de Cuenta y añade intereses, pero tras ingresar 1000 € y aplicar un 5 % el saldo sigue en 1000 € y aparecen avisos. Corrígelo cambiando lo mínimo.
Un motor de plantillas mínimo
Difícil
Separar la presentación de la lógica es la base de MVC. Completa render($plantilla, $datos): sustituye cada {{ clave }} por el valor de $datos escapado con htmlspecialchars, y {{ clave|raw }} por el valor sin escapar. Las claves pueden tener puntos para entrar…
Una cadena de middlewares
Difícil
Los frameworks procesan cada petición con una cadena de middlewares: cada uno puede hacer algo antes, pasar la petición al siguiente con $siguiente($peticion) y hacer algo después, o cortar la cadena devolviendo su propia respuesta. Completa ejecutar(), que en…
Un contenedor de dependencias con autowiring
Muy difícil
Laravel y Symfony crean los objetos por ti: miran el constructor de una clase, ven qué necesita y lo crean primero. Completa la clase Contenedor. get($clase) devuelve siempre la misma instancia de cada clase (singleton). Para crearla, usa ReflectionClass para …
Encuentra el fallo: el listado de un solo producto
Fácil
La consulta devuelve tres productos ordenados por precio, pero el programa solo muestra el primero. Corrígelo para listarlos todos.
Buscar sin inyección SQL
Medio
El programa lee una categoría y lista sus productos ordenados por precio («Teclado - 24,99 €»), o «Sin resultados». Funciona, pero construye la consulta pegando el texto del usuario: prueba a buscar ' OR '1'='1 y verás todos los productos. Reescríbelo con una …
Paginar resultados con LIMIT
Medio
La tabla alumnos tiene 23 filas. La entrada es «página porPágina» (por ejemplo «2 5»). Muestra los nombres de esa página ordenados por id, uno por línea, y después «Página 2 de 5». Si la página o el tamaño no son enteros positivos, o la página no existe, muest…
Filtros dinámicos sin inyección
Medio
Cada línea de la entrada es un filtro opcional «clave=valor»: categoria, min (precio mínimo), max (precio máximo), texto (el nombre lo contiene) y orden (precio, precio_desc o nombre; cualquier otro valor se ignora y se ordena por nombre). Construye una única …
Transferencias con transacciones
Difícil
Cada línea es una transferencia «origen destino importe». Para cada una escribe «Cuenta inexistente», «Importe no válido» (menor o igual que 0 o no numérico), «Saldo insuficiente» o «Transferencia realizada», en ese orden de comprobación. Las dos actualizacion…
Un informe sin el problema N+1
Difícil
El programa muestra, para un año dado, cuántos pedidos ha hecho cada cliente y cuánto ha gastado, ordenado de mayor a menor gasto (a igual gasto, por nombre). Deben salir todos los clientes, también los que no han comprado ese año («0 pedidos, 0,00 €»). El cód…
Importar un CSV: todo o nada
Muy difícil
La entrada es un CSV con cabecera «sku,nombre,precio,stock». Procesa las filas en orden dentro de una transacción: si el SKU ya existe en la tabla, actualiza nombre, precio y stock; si no, lo inserta. Valida cada fila antes de guardarla: 4 campos («número de c…
La entrada es la respuesta JSON de una API de repositorios: un array de objetos con name, language (puede ser null) y stars. Muestra los tres con más estrellas («1. nombre (★ 1.234)», con punto de miles), después «Total de estrellas: N» y «Lenguajes: …» con lo…
Caché HTTP con ETag
Medio
Para no enviar una y otra vez los mismos datos, una API puede responder con una ETag (una huella del contenido). El cliente la guarda y, en la siguiente petición, la envía en la cabecera If-None-Match; si el recurso no ha cambiado, el servidor responde 304 sin…
Una API REST de libros
Difícil
Cada línea de la entrada es una petición «MÉTODO RUTA [cuerpo JSON]». Escribe para cada una el código de estado y, si hay cuerpo, un espacio y la respuesta en JSON (json_encode con JSON_UNESCAPED_UNICODE). GET /api/libros → 200 con la lista; GET /api/libros/{i…
Tokens JWT firmados a mano
Difícil
Un JWT tiene tres partes en base64url separadas por puntos: cabecera, datos (payload) y firma HMAC-SHA256 de «cabecera.payload» con una clave secreta. Completa crear(array $datos): string y verificar(string $token, int $ahora): ?array. verificar devuelve los d…
Limitar las peticiones: 429 Too Many Requests
Muy difícil
Toda API pública limita cuántas peticiones acepta de cada cliente. Implementa una ventana deslizante: un cliente puede hacer como máximo 3 peticiones en cualquier intervalo de 10 segundos. Cada línea de la entrada es «segundo cliente» (los segundos no bajan). …
DWES · Aplicaciones Dinámicas e Híbridas ver el tema
El endpoint de un autocompletado
Medio
Un campo de búsqueda de municipios pide sugerencias al servidor mientras el usuario escribe. Cada línea de la entrada es la query string que llega (q=…). Responde con un array JSON (una línea por petición, sin escapar las tildes) de como mucho 5 objetos {"nomb…
Verificar la firma de un webhook
Difícil
Cuando un pago se completa, la pasarela avisa a tu servidor con una petición (un webhook). Cualquiera podría enviar una petición falsa a esa URL, así que la pasarela la firma: la cabecera Firma es «t=segundos,v1=hmac», donde hmac es hash_hmac('sha256', "$t.$cu…
Un cortacircuitos para un servicio externo
Muy difícil
Si la API de envíos se cae, cada petición a tu tienda esperaría el tiempo máximo antes de fallar, y la tienda entera se volvería lenta. El patrón cortacircuitos (circuit breaker) lo evita. Empieza cerrado (las llamadas pasan). Tras 3 fallos seguidos se abre: d…
DWES · Ejercicios de Desarrollo Web en Entorno Servidor ver el tema
Informe a partir de un CSV
Medio
La entrada es un CSV con cabecera «fecha,comercial,importe» (los campos pueden ir entre comillas). Suma el importe por comercial y muestra una línea por comercial ordenada de mayor a menor importe («Ana: 3.200,50 €»), después «Total: …» y, si alguna fila no te…
API de tareas con base de datos
Difícil
Une una API REST con PDO. La tabla tareas (id, titulo, hecha) empieza vacía. Cada línea es «MÉTODO RUTA [JSON]» y la respuesta, «código JSON». POST /tareas {"titulo": …} → 201 con la tarea ({"id":1,"titulo":…,"hecha":false}) o 422 {"error":"El título es obliga…
Carrito con cupones
Difícil
La entrada tiene líneas «producto;precio;cantidad» y, opcionalmente, líneas «CUPON código». El envío cuesta 4,95 € si el subtotal es menor de 50 €. Cupones: DESC10 (10 % del subtotal), MENOS5 (5 € si el subtotal es de al menos 30 €) y ENVIOGRATIS (sin gastos d…