9. Ficheros

Es posible importar o llamar el código desde otro archivo con PHP. De esta manera conseguiremos dividir nuestras página dinámicas en fragmentos más pequeños y fáciles de gestionar evitando el famoso código Spaghetti (escribir todo nuestro código en un solo fichero con un interminable número de líneas). Para esta buena práctica disponemos en nuestro bat-cinturón de hasta 4 herramientas diferentes.

Función Descripción Caso de error Ejemplo
include '' Incluye el fichero en cada ocasión. Da una advertenia, pero continua la ejecución. include 'tu_archivo.php'
include_once '' Incluye el fichero en una ocasión. Da una advertenia, pero continua la ejecución. include_once 'tu_archivo.php'
require '' Incluye el fichero en cada ocasión. Para la ejecución (Error fatal). require 'tu_archivo.php'
require_once '' Incluye el fichero en una ocasión. Para la ejecución (Error fatal). require_once 'tu_archivo.php'

Veamos con un ejemplo como funciona. Voy a tener un archivo llamado header.php con el siguiente contenido.

<html>
    <head>
    </head>
    <body>

Otro con el nombre footer.php.

        <footer>Soy yo</footer>
    </body>
</html>

Ahora creo un fichero nuevo.

<?php include 'header.php'; ?>
<h1>Inicio</h1>
<?php include 'footer.php'; ?>

Me daria el siguiente HTML.

<html>
    <head>
    </head>
    <body>
        <h1>Inicio</h1>
        <footer>Soy yo</footer>
    </body>
</html>

Potente, ¿verdad? Evitaremos repartir partes de nuestro código que sean muy reiterativas.

En esta ocasión voy a repetir el include de footer.

Ahora creo un fichero nuevo.

<?php include 'header.php'; ?>
<h1>Inicio</h1>
<?php include 'footer.php'; ?>
<?php include 'footer.php'; ?>

Lo que me generaría un pie duplicado.

<html>
    <head>
    </head>
    <body>
        <h1>Inicio</h1>
        <footer>Soy yo</footer>
    </body>
</html>
        <footer>Soy yo</footer>
    </body>
</html>

Lo podemos prevenir con include_once.

<?php include 'header.php'; ?>
<h1>Inicio</h1>
<?php include_once 'footer.php'; ?>
<?php include_once 'footer.php'; ?>

Si ya ha sido llamado, lo ingora.

<html>
    <head>
    </head>
    <body>
        <h1>Inicio</h1>
        <footer>Soy yo</footer>
    </body>
</html>

Subir un archivo

Un fichero es un elemento en binario que no es ni un número o ni un texto: imagen, video, música, doc, iso... Nosotros somos incapaces de leerlo a no ser que tengamos un ojo biónico y un chip en el cerebro. Si careces de estos dos requisitos solo podrás subirlo, por medio de un formulario, y almacenarlo en una carpeta.

Se debe tratar de una forma especial. Necesitaremos usar siempre el method POST y añadir enctype="multipart/form-data". Por último usar el input de tipo archivo (file).

<!-- Formulario -->
<form method="post" enctype="multipart/form-data">
    <p>
        <!-- Campo imagen -->
        <input type="file" name="fichero_usuario">
    </p>
    <p>
        <!-- Botón submit -->
        <input type="submit" value="Enviar">
    </p>
</form>

Cuando nuestro formulario sea enviado el archivo estará almacenado en una variable llamada $_FILES. La cual es un array con toda la información que vas a necesitar.

Nombre Ejemplo de contenido Descripción
$_FILES['fichero_usuario']['name'] 'foto_en_la_playa.jpg' Nombre del archivo
$_FILES['fichero_usuario']['type'] 'image/png' MIME (formato del archivo)
$_FILES['fichero_usuario']['size'] 3232424 Tamaño en bytes (5MB -> 5 x 1024 x 1024 bytes)
$_FILES['fichero_usuario']['error'] 0 Código de error. El 0 es que todo ha ido bien, los otros puedes encontrarlos aquí
$_FILES['fichero_usuario']['tmp_name'] 213 Nombre temporal

Ahora solo tendremos que moverlo de la carpeta temporal a la definitiva, usando el método move_uploaded_file().

move_uploaded_file($_FILES['fichero_usuario']['tmp_name'], $fichero_subido);

Aquí puedes ver un ejemplo completo.

<html>
    <body>
        <?php
            //======================================================================
            // PROCESAR IMAGEN
            //======================================================================
            // Comprobamos si nos llega los datos por POST
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
                // Definir directorio donde se guardará
                $dir_subida = './subidos/';
                // Definir la ruta final del archivo
                $fichero_subido = $dir_subida . basename($_FILES['fichero_usuario']['name']);
                // Mueve el archivo de la carpeta temporal a la ruta definida
                if (move_uploaded_file($_FILES['fichero_usuario']['tmp_name'], $fichero_subido)) {
                    // Mensaje de confirmación donde todo ha ido bien
                    echo '<p>Se subió perfectamente.</p>';
                    // Muestra la imagen que acaba de ser subida
                    echo '<p><img width="500" src="' . $fichero_subido . '"></p>';
                } else {
                    // Mensaje de error: ¿Límite de tamaño? ¿Ataque?
                    echo '<p>¡Ups! Algo ha pasado.</p>';
                }
            }
        ?>
        <!-- Formulario -->
        <form method="post" enctype="multipart/form-data">
            <p>
                <!-- Campo imagen -->
                <input type="file" name="fichero_usuario">
            </p>
            <p>
                <!-- Botón submit -->
                <input type="submit" value="Enviar">
            </p>
        </form>
    </body>
</html>

Multiarchivo (subir varios archivos)

Es posible subir varios archivos bajo el mismo nombre. Solo habrá que añadir unos corchetes ([]) después del name como si fuera un array.

<!-- Formulario -->
<form method="post" enctype="multipart/form-data">
    <p>
        <!-- Campos de imágenes -->
        <input type="file" name="imagen[]">
        <input type="file" name="imagen[]">
        <input type="file" name="imagen[]">
    </p>
    <p>
        <!-- Botón submit -->
        <input type="submit" value="Enviar">
    </p>
</form>

Al recibir los datos tendremos que iterar la variable, igual que un array. Es recomendable comprobar en cada caso que el archivo se ha subido correctamente.

// Comprobamos si nos llega los datos por POST
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Iteramos todos los archivos
    foreach ($_FILES["imagen"]["error"] as $posicion => $error) {
        // Comprobamos si se ha subido correctamente
        if ($error == UPLOAD_ERR_OK) {
            // Definir directorio donde se guardará
            $dir_subida = './subidos/';
            // Definir la ruta final del archivo
            $fichero_subido = $dir_subida . basename($_FILES['imagen']['name'][$posicion]);
            // Mueve el archivo de la carpeta temporal a la ruta definida
            if (move_uploaded_file($_FILES['imagen']['tmp_name'][$posicion], $fichero_subido)) {
                // Mensaje de confirmación donde todo ha ido bien
                echo '<p>Se subió perfectamente' . $_FILES['imagen']['name'][$posicion] . '.</p>';
                // Muestra la imagen que acaba de ser subida
                echo '<p><img width="500" src="' . $fichero_subido . '"></p>';
            } else {
                // Mensaje de error: ¿Límite de tamaño? ¿Ataque?
                echo '<p>¡Ups! Algo ha pasado.</p>';
            }
        }
    }
}

Junto quedaría de la siguiente forma.

<html>
    <body>
        <?php
            //======================================================================
            // PROCESAR IMAGENES
            //======================================================================
            // Comprobamos si nos llega los datos por POST
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
                // Iteramos todos los archivos
                foreach ($_FILES["imagen"]["error"] as $posicion => $error) {
                    // Comprobamos si se ha subido correctamente
                    if ($error == UPLOAD_ERR_OK) {
                        // Definir directorio donde se guardará
                        $dir_subida = './subidos/';
                        // Definir la ruta final del archivo
                        $fichero_subido = $dir_subida . basename($_FILES['imagen']['name'][$posicion]);
                        // Mueve el archivo de la carpeta temporal a la ruta definida
                        if (move_uploaded_file($_FILES['imagen']['tmp_name'][$posicion], $fichero_subido)) {
                            // Mensaje de confirmación donde todo ha ido bien
                            echo '<p>Se subió perfectamente' . $_FILES['imagen']['name'][$posicion] . '.</p>';
                            // Muestra la imagen que acaba de ser subida
                            echo '<p><img width="500" src="' . $fichero_subido . '"></p>';
                        } else {
                            // Mensaje de error: ¿Límite de tamaño? ¿Ataque?
                            echo '<p>¡Ups! Algo ha pasado.</p>';
                        }
                    }
                }
            }
        ?>
        <!-- Formulario -->
        <form method="post" enctype="multipart/form-data">
            <p>
                <!-- Campos de imágenes -->
                <input type="file" name="imagen[]">
                <input type="file" name="imagen[]">
                <input type="file" name="imagen[]">
            </p>
            <p>
                <!-- Botón submit -->
                <input type="submit" value="Enviar">
            </p>
        </form>
    </body>
</html>

Borrar archivos

Para eliminar un archivo debemos usar el método unlink.

unlink('archivo');

Tan solo hay que dar la ruta que deseamos.

unlink('subidos/coche_rojo.jpg');

Evitar que se sobrescriba

¿Qué pasa si subimos dos archivos con el mismo nombre? Pues que el anterior desaparecería, se sobrescribiría por tener el mismo nombre y guardarse en el mismo lugar. Debemos garantiza que el archivo posee un nombre único.

Un truco para solucionarlo generando un hash, o una secuencia alfanumérica única por cada archivo que sustituya al nombre. Un algoritmo muy popular es SHA-1.

En criptografía, SHA-1 (Secure Hash Algorithm 1) es una función hash criptográfica que ha sido rota, pero que sigue siendo ampliamente utilizada para generar un número hexadecimal de 40 dígitos. Su autor fue la Agencia de Seguridad Nacional de Estados Unidos, siendo usando para el procesamiento de información de Estados Unidos.

Si quisiéramos obtener un hash de un texto, deberíamos usar sha1().

echo sha1('texto');
// ea631551f5569f612dd702b900c596c2a99c0dfd

Para archivos disponemos de una función determinada llamada hash_file(). En este ejemplo usamos el algoritmo SHA-256, más seguro que SHA-1.

echo hash_file('sha256', $_FILES['fichero_usuario']['tmp_name']);
// f8c3bf62a9aa3e6fc1619c250e48abe7519373d3edf41be62eb5dc45199af2ef

Si tuvieramos un archivo llamado reloj.jpg.

$fichero_subido = $dir_subida . hash_file('sha256', $_FILES['fichero_usuario']['tmp_name']) . basename($_FILES['fichero_usuario']['name']);
echo $fichero_subido;
// subidos/f8c3bf62a9aa3e6fc1619c250e48abe7519373d3edf41be62eb5dc45199af2efreloj.jpg

Por muchos archivos reloj.jpg que suban nunca se sobrescribirán, a no ser que a nivel binario sean exactamente iguales (lo cual tampoco sería un problema porque sería el mismo fichero).

Tamaño máximo

Si quieres limitar el tamaño de todos los archivos puedes hacerlo añadiendo un input especial.

<input type="hidden" name="MAX_FILE_SIZE" value="20000" />

El value se mide en bytes.

Si un archivo supera nuestra frontera dará un error, pero nunca llegará a subirse.

Otra forma de cambiarlo, en este caso permanente, es modificando unas variables de PHP en su archivo de configuración.

sudo nano /etc/php/{versión}/cli/php.ini

Edita las siguientes variables si quieres limitarlo a 100Mb.

upload_max_filesize=100Mb
post_max_size=100Mb

Igualmente valida el tamaño con PHP, es fácil manipular el límite dentro del navegador. Recuerda: nunca te fíes del usuario.

Procesamiento de imágenes

PHP no esta solamente limitado a la generación de HTML y mover archivos, también puede procesar imágenes. Existen una multitud de posibilidades.

  • Redimensionar (utilizado para crear miniaturas).
  • Recortar.
  • Aplicar filtros de color.
  • Crear imágenes (como suena).
  • Añadir marcas de agua.
  • Cambiar de formato.

Para crear una minuatura podrías hacerlo usando la libreria nativa Imagick.

Primero tendremos que instalarla en el sistema. Con Ubuntu o Debian es muy sencillo.

sudo apt install php-imagick

Aahora ya puedes trabajar con ella. En el siguiente ejemplo se captura imagen.jpg y se redimensiona a 100px de ancho. Por último se guarda con el nombre de miniatura.jpg.

$imagen = new Imagick('imagen.jpg');

// Si se proporciona 0 como parámetro de ancho o alto,
// se mantiene la proporción de aspecto
$imagen->thumbnailImage(100, 0);

// La guarda
file_put_contents('miniatura.jpg', $imagen);

Dispone de diversas herramientas para manipular formatos tan conocidos como: JPEG, GIF, PNG y WebP (entre otros). Puedes ver más en la documentación.

Ejemplo completo

Puedes ver un ejemplo que recoge todos los casos anteriores.

  • Valida que se ha adjuntado una imagen en el formulario.
  • Valida que sea una imagen en jpg o png.
  • Valida que no supere cierto tamaño. En este caso 2Mb.
  • Crea una miniatura. En este caso de 100px de ancho.
  • Cambia el nombre aleatorio para evitar posibles conflictos con otros archivos.
  • Muestra la miniatura en el HTML.
<?php

//======================================================================
// VARIABLES
//======================================================================
$errorAvatar = 0;
$avatar = null;
$rutaThumbnail = null;

// Definir directorio donde se guardará
define('PATH_AVATAR', './subidos/');
define('PATH_AVATAR_THUMBNAIL', './subidos/thumbnails/');

// Tamanyo max avatar: 2 Mb
define('MAX_SIZE_AVATAR_MB', 2);
define('MAX_SIZE_AVATAR', MAX_SIZE_AVATAR_MB * 1024 * 1024);
// En /etc/php/7.4/cli/php.ini edita las siguientes variables
// upload_max_filesize=100Mb
// post_max_size=100Mb

// Anchura miniatura
define('WIDTH_THUMBNAIL', 100);

//======================================================================
// PROCESAR FORMULARIO
//======================================================================

// Comprobamos si nos llega los datos por POST
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES)) {

    //-----------------------------------------------------
    //  Recoger avatar
    //-----------------------------------------------------

    // Verifica si existe el directorio, y en caso contrario lo crea
    if (!is_dir(PATH_AVATAR_THUMBNAIL)) {
        mkdir(PATH_AVATAR_THUMBNAIL, 0775, true);
    }
    // Definir la ruta final del archivo
    $nombreFoto = hash_file('sha256', $_FILES['avatar']['tmp_name']) . basename($_FILES['avatar']['name']);
    $ficheroSubido = PATH_AVATAR . $nombreFoto;

    //-----------------------------------------------------
    //  Control de errores
    //-----------------------------------------------------

    // Tamanyo maximo
    if ($_FILES['avatar']['size'] > MAX_SIZE_AVATAR) {
        $errorAvatar = 1;
    }

    // Solo JPG y PNG
    if ($_FILES['avatar']['type'] !== 'image/png' && $_FILES['avatar']['type'] !== 'image/jpeg') {
        $errorAvatar = 2;
    }

    // Obligatorio
    if ($_FILES['avatar']['size'] === 0) {
        $errorAvatar = 3;
    }

    //-----------------------------------------------------
    //  Procesar imagen
    //-----------------------------------------------------

    if ($errorAvatar === 0) {
        if (move_uploaded_file($_FILES['avatar']['tmp_name'], $ficheroSubido)) {
            // Mueve el archivo de la carpeta temporal a la ruta definida
            $avatar = $ficheroSubido;

            // Creamos una miniatura
            // No olvides instalarlo con: sudo apt install php-imagick
            $imagen = new Imagick($avatar);

            // Si se proporciona 0 como parámetro de ancho o alto,
            // se mantiene la proporción de aspecto
            $imagen->thumbnailImage(WIDTH_THUMBNAIL, 0);
            $rutaThumbnail = PATH_AVATAR_THUMBNAIL . $nombreFoto;
            file_put_contents($rutaThumbnail, $imagen);
        }
    }
}

?>
<!doctype html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Perfil</title>
</head>
<body>
    <?php if (isset($rutaThumbnail)): ?>
        <img src="<?= $rutaThumbnail; ?>" alt="mi avatar" width="<?= WIDTH_THUMBNAIL ?>">
    <?php endif; ?>
    <form method="post" enctype="multipart/form-data">
        <p>
            <label>
                Foto:
                <input type="file" name="avatar">
            </label>
        </p>
        <?php if ($errorAvatar === 1): ?>
        <p style="color: red">
            Tamaño demasiado grande, intente que tenga menos de <?= MAX_SIZE_AVATAR_MB ?>Mb
        </p>
        <?php elseif ($errorAvatar === 2): ?>
        <p style="color: red">
            Solo admitido imagenes en JPG o PNG.
        </p>
        <?php elseif ($errorAvatar === 3): ?>
        <p style="color: red">
            Debes incluir una imagen
        </p>
        <?php endif; ?>
        <p>
            <input type="submit" value="Guardar">
        </p>
    </form>
</body>
</html>

Validación y Seguridad

Subir archivos sin validarlos correctamente es una de las vulnerabilidades más peligrosas en una aplicación web. Un atacante podría subir código malicioso (por ejemplo un archivo PHP disfrazado de imagen) y ejecutarlo en tu servidor. Por eso es fundamental validar siempre los archivos que subes.

Regla de oro: Nunca confíes en los datos que vienen del cliente. Un usuario puede modificar fácilmente el tipo MIME desde el navegador.

Validar el tipo MIME real

El tipo MIME que viene en $_FILES['archivo']['type'] puede ser falsificado por el cliente. Para validar correctamente debemos usar funciones de PHP que analicen el contenido real del archivo.

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $archivoTemporal = $_FILES['imagen']['tmp_name'];

    // Obtener el tipo MIME real del archivo
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeReal = finfo_file($finfo, $archivoTemporal);
    finfo_close($finfo);

    // Lista blanca de tipos permitidos
    $tiposPermitidos = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];

    if (!in_array($mimeReal, $tiposPermitidos)) {
        die('Error: Solo se permiten imágenes (JPG, PNG, GIF, WEBP)');
    }

    echo "Tipo MIME válido: $mimeReal";
}
?>
<form method="post" enctype="multipart/form-data">
    <input type="file" name="imagen">
    <input type="submit" value="Subir">
</form>

Validar tamaño del archivo

Limita el tamaño máximo para evitar que alguien sature tu servidor con archivos enormes.

<?php
// Tamaño máximo: 5MB
$tamañoMaximo = 5 * 1024 * 1024; // 5MB en bytes

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    if ($_FILES['imagen']['size'] > $tamañoMaximo) {
        die('Error: El archivo excede el tamaño máximo de 5MB');
    }

    if ($_FILES['imagen']['size'] === 0) {
        die('Error: El archivo está vacío');
    }

    echo 'Tamaño válido: ' . round($_FILES['imagen']['size'] / 1024, 2) . ' KB';
}
?>

Sanitizar el nombre del archivo

Los nombres de archivo pueden contener caracteres peligrosos o rutas relativas (../../../etc/passwd). Siempre debes generar un nombre seguro.

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Obtener la extensión real
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeReal = finfo_file($finfo, $_FILES['imagen']['tmp_name']);
    finfo_close($finfo);

    // Mapear MIME a extensión
    $extensiones = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/gif' => 'gif',
        'image/webp' => 'webp'
    ];

    $extension = $extensiones[$mimeReal] ?? 'bin';

    // Generar nombre único y seguro
    $nombreSeguro = uniqid('img_', true) . '.' . $extension;

    $rutaDestino = './subidos/' . $nombreSeguro;

    if (move_uploaded_file($_FILES['imagen']['tmp_name'], $rutaDestino)) {
        echo "Archivo subido correctamente: $nombreSeguro";
    }
}
?>

¿Por qué no usar el nombre original? Porque podría contener caracteres especiales, espacios, rutas relativas o extensiones peligrosas. Un atacante podría subir shell.php.jpg esperando que tu servidor lo ejecute como PHP.

Almacenar fuera del webroot

La forma más segura de almacenar archivos subidos es guardarlos fuera de la carpeta pública de tu servidor web. Así, aunque alguien suba un archivo PHP malicioso, el servidor web no podrá ejecutarlo.

<?php
// Directorio fuera del webroot (no accesible directamente desde el navegador)
$directorioSeguro = '../uploads_privados/';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $nombreSeguro = uniqid('doc_', true) . '.pdf';
    $rutaCompleta = $directorioSeguro . $nombreSeguro;

    if (move_uploaded_file($_FILES['documento']['tmp_name'], $rutaCompleta)) {
        // Guardar el nombre en la base de datos para recuperarlo después
        echo "Documento guardado de forma segura";
    }
}
?>

Para servir el archivo después, crea un script que lo lea y lo envíe con los headers correctos:

<?php
// download.php
$archivoId = $_GET['id'] ?? '';

// Aquí validarías que el usuario tiene permiso para descargar este archivo
// y obtendrías el nombre real desde la base de datos

$rutaArchivo = '../uploads_privados/' . $archivoId;

if (file_exists($rutaArchivo)) {
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="documento.pdf"');
    readfile($rutaArchivo);
    exit;
}
?>

Ejemplo completo y seguro

Aquí tienes un ejemplo que combina todas las mejores prácticas:

<?php
// Configuración
$directorioSubidas = './subidos/';
$tamañoMaximo = 5 * 1024 * 1024; // 5MB
$tiposPermitidos = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$extensiones = [
    'image/jpeg' => 'jpg',
    'image/png' => 'png',
    'image/gif' => 'gif',
    'image/webp' => 'webp'
];

$error = '';
$exito = '';

if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['imagen'])) {

    // 1. Verificar que no hubo errores en la subida
    if ($_FILES['imagen']['error'] !== UPLOAD_ERR_OK) {
        $error = 'Error al subir el archivo';
    }
    // 2. Validar tamaño
    elseif ($_FILES['imagen']['size'] === 0) {
        $error = 'El archivo está vacío';
    }
    elseif ($_FILES['imagen']['size'] > $tamañoMaximo) {
        $error = 'El archivo excede el tamaño máximo de 5MB';
    }
    else {
        // 3. Validar tipo MIME real
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mimeReal = finfo_file($finfo, $_FILES['imagen']['tmp_name']);
        finfo_close($finfo);

        if (!in_array($mimeReal, $tiposPermitidos)) {
            $error = 'Solo se permiten imágenes (JPG, PNG, GIF, WEBP)';
        }
        else {
            // 4. Generar nombre seguro
            $extension = $extensiones[$mimeReal];
            $nombreSeguro = uniqid('img_', true) . '.' . $extension;
            $rutaDestino = $directorioSubidas . $nombreSeguro;

            // 5. Mover el archivo
            if (move_uploaded_file($_FILES['imagen']['tmp_name'], $rutaDestino)) {
                $exito = "Imagen subida correctamente: $nombreSeguro";
            } else {
                $error = 'Error al mover el archivo';
            }
        }
    }
}
?>
<!doctype html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Subida segura de imágenes</title>
</head>
<body>
    <h1>Subir imagen (con validación segura)</h1>

    <?php if ($error): ?>
        <p style="color: red; font-weight: bold;"><?= htmlspecialchars($error) ?></p>
    <?php endif; ?>

    <?php if ($exito): ?>
        <p style="color: green; font-weight: bold;"><?= htmlspecialchars($exito) ?></p>
        <img src="<?= htmlspecialchars($directorioSubidas . basename($nombreSeguro)) ?>" width="400" alt="Imagen subida">
    <?php endif; ?>

    <form method="post" enctype="multipart/form-data">
        <label>
            Selecciona una imagen (JPG, PNG, GIF, WEBP, máx 5MB):
            <input type="file" name="imagen" accept="image/*" required>
        </label>
        <button type="submit">Subir imagen</button>
    </form>
</body>
</html>

Recuerda: La seguridad es como las capas de una cebolla. Cada capa de validación suma. Nunca confíes solo en validaciones del lado del cliente (HTML5 accept, JavaScript), siempre valida en el servidor.

Actividad 1
  • Crea una web donde se pida un usuario y una contraseña.
  • Si es correcto debe llegar a una página protegida por sesión.
  • Añade un botón para cerrar la sesión.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

Comments

There are no comments yet.

Visitors in real time

You are alone: 🐱