PHP

PHP

Tutorial PHP: Manejo de archivos

PHP: Aprender a manejar archivos de manera segura y eficiente. Descubre cómo leer, escribir, procesar y subir archivos en este completo tutorial.

Aprende PHP GRATIS y certifícate

Lectura y escritura de archivos

El manejo de archivos es esencial en PHP para almacenar y recuperar información de manera persistente. Para leer el contenido completo de un archivo, podemos utilizar la función file_get_contents($nombreArchivo), que devuelve todo el contenido como una cadena de texto.

<?php
$contenido = file_get_contents('datos.txt');
echo $contenido . "\n";

Si necesitamos procesar el archivo línea por línea, es más eficiente usar fopen() para abrir el archivo y luego recorrerlo con fgets():

<?php
$archivo = fopen('datos.txt', 'r');
while (($linea = fgets($archivo)) !== false) {
    echo $linea . "\n";
}
fclose($archivo);

Es importante cerrar el archivo después de terminar la lectura para liberar recursos. Para escribir datos en un archivo, la función file_put_contents($nombreArchivo, $datos) es muy útil, ya que escribe la información proporcionada en el archivo especificado.

<?php
$datos = "Nueva línea de texto\n";
file_put_contents('datos.txt', $datos);

Si deseamos añadir contenido sin sobrescribir el archivo existente, podemos utilizar el tercer parámetro FILE_APPEND en file_put_contents():

<?php
$datos = "Otra línea adicional\n";
file_put_contents('datos.txt', $datos, FILE_APPEND);

Otra forma de escribir en un archivo es mediante fopen() con el modo adecuado. El modo 'w' crea el archivo o lo sobrescribe, mientras que 'a' añade contenido al final:

<?php
$archivo = fopen('datos.txt', 'a');
fwrite($archivo, "Contenido agregado\n");
fclose($archivo);

Al manipular archivos, es recomendable manejar posibles errores utilizando bloques try-catch para garantizar la robustez de la aplicación:

<?php
try {
    $archivo = fopen('datos.txt', 'r');
    if (!$archivo) {
        throw new Exception("No se pudo abrir el archivo\n");
    }
    // Procesar el archivo
    fclose($archivo);
} catch (Exception $e) {
    echo 'Error: ' . $e->getMessage() . "\n";
}

PHP ofrece la clase SplFileObject para un enfoque orientado a objetos en el manejo de archivos, proporcionando una interfaz más modular y flexible:

<?php
$archivo = new SplFileObject('datos.txt', 'r');
foreach ($archivo as $linea) {
    echo $linea . "\n";
}

Con SplFileObject, podemos utilizar métodos como fwrite() para escribir en archivos de manera eficiente:

<?php
$archivo = new SplFileObject('datos.txt', 'w');
$archivo->fwrite("Escribiendo con SplFileObject\n");

Para prevenir problemas de concurrencia al acceder a archivos desde múltiples procesos, es aconsejable utilizar bloqueos con flock():

<?php
$archivo = fopen('datos.txt', 'c');
if (flock($archivo, LOCK_EX)) {
    fwrite($archivo, "Escritura segura\n");
    flock($archivo, LOCK_UN);
} else {
    echo "No se pudo obtener el bloqueo\n";
}
fclose($archivo);

El manejo adecuado de archivos es crucial para desarrollar aplicaciones PHP seguras y eficientes. Al utilizar las funciones y clases apropiadas, podemos optimizar el rendimiento y garantizar la integridad de los datos.

Manejo de directorios

El manejo de directorios en PHP es fundamental para organizar y manipular estructuras de archivos en aplicaciones complejas. PHP proporciona una serie de funciones y clases que permiten interactuar con los sistemas de archivos de manera eficiente y segura.

Para crear un directorio, se utiliza la función mkdir($ruta, $permisos, $recursivo), donde $ruta es la ubicación del nuevo directorio, $permisos define los permisos de acceso y $recursivo permite crear directorios anidados:

<?php
$ruta = 'nuevos/archivos';
if (!is_dir($ruta)) {
    mkdir($ruta, 0755, true);
    echo "Directorio creado exitosamente\n";
} else {
    echo "El directorio ya existe\n";
}

Es importante verificar si el directorio ya existe utilizando is_dir($ruta) para evitar errores. Los permisos se especifican en notación octal, donde 0755 es un valor común que otorga permisos de lectura y ejecución al grupo y a otros.

Para listar el contenido de un directorio, la función scandir($ruta) devuelve un array con los nombres de los archivos y subdirectorios:

<?php
$ruta = '.';
$archivos = scandir($ruta);
foreach ($archivos as $archivo) {
    echo $archivo . "\n";
}

Esta función incluye los elementos especiales . y .., que representan el directorio actual y el padre, respectivamente. Si se desea filtrar estos elementos, se puede hacer lo siguiente:

<?php
$archivos = array_diff(scandir($ruta), array('.', '..'));
foreach ($archivos as $archivo) {
    echo $archivo . "\n";
}

Para una exploración más avanzada, PHP ofrece la clase DirectoryIterator, que permite iterar sobre los elementos de un directorio y obtener información detallada:

<?php
$ruta = '.';
$iterator = new DirectoryIterator($ruta);
foreach ($iterator as $archivo) {
    if (!$archivo->isDot()) {
        echo ($archivo->isDir() ? '[Directorio] ' : '[Archivo] ') . $archivo->getFilename() . "\n";
    }
}

La clase DirectoryIterator proporciona métodos como isDir(), isFile(), getSize(), entre otros, que son útiles para obtener detalles de cada elemento.

Para eliminar un directorio vacío, se utiliza la función rmdir($ruta):

<?php
$ruta = 'nuevos/archivos';
if (is_dir($ruta)) {
    rmdir($ruta);
    echo "Directorio eliminado\n";
} else {
    echo "El directorio no existe\n";
}

Si el directorio no está vacío, es necesario eliminar primero su contenido. Una forma de hacerlo es mediante una función recursiva:

<?php
function eliminarDirectorio($ruta)
{
    if (!file_exists($ruta)) {
        return;
    }
    if (is_file($ruta)) {
        unlink($ruta);
    } elseif (is_dir($ruta)) {
        $archivos = array_diff(scandir($ruta), array('.', '..'));
        foreach ($archivos as $archivo) {
            eliminarDirectorio($ruta . DIRECTORY_SEPARATOR . $archivo);
        }
        rmdir($ruta);
    }
}

$ruta = 'nuevos/archivos';
eliminarDirectorio($ruta);
echo "Directorio y su contenido eliminados\n";

La constante DIRECTORY_SEPARATOR garantiza la compatibilidad con diferentes sistemas operativos.

Para renombrar o mover un directorio, se utiliza la función rename($origen, $destino):

<?php
$origen = 'nuevos/archivos';
$destino = 'archivos_backup';
if (is_dir($origen)) {
    rename($origen, $destino);
    echo "Directorio renombrado/movido a $destino\n";
} else {
    echo "El directorio $origen no existe\n";
}

Es fundamental manejar los errores y verificar la existencia de los directorios antes de realizar operaciones para evitar excepciones no controladas.

El cambio de permisos se puede realizar con chmod($ruta, $permisos), permitiendo modificar los permisos de un directorio existente:

<?php
$ruta = 'archivos_backup';
if (is_dir($ruta)) {
    chmod($ruta, 0700);
    echo "Permisos del directorio actualizados\n";
} else {
    echo "El directorio no existe\n";
}

Para obtener información detallada sobre un directorio o archivo, la función stat($ruta) proporciona datos como tamaño, fechas de acceso y modificación:

<?php
$ruta = 'archivos_backup';
if (file_exists($ruta)) {
    $info = stat($ruta);
    echo "Tamaño: " . $info['size'] . " bytes\n";
    echo "Último acceso: " . date('Y-m-d H:i:s', $info['atime']) . "\n";
}

Cuando se trabaja con directorios, es esencial considerar aspectos de seguridad, especialmente si las rutas provienen de entradas de usuario. Es recomendable validar y sanitizar las rutas utilizando funciones como realpath($ruta) y evitando caracteres especiales o secuencias como ../ que puedan conducir a directorios no deseados.

Además, para aplicaciones más complejas, la recursión y el uso de iteradores específicos, como RecursiveDirectoryIterator y RecursiveIteratorIterator, permiten recorrer estructuras de directorios profundamente anidadas:

<?php
$ruta = '.';
$iterador = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($ruta, RecursiveDirectoryIterator::SKIP_DOTS),
    RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterador as $archivo) {
    $espacios = str_repeat('  ', $iterador->getDepth());
    echo $espacios . ($archivo->isDir() ? '[Dir] ' : '[File] ') . $archivo->getFilename() . "\n";
}

Esta técnica es útil para generar listados completos de directorios y archivos en sistemas de archivos complejos.

El manejo eficiente y seguro de directorios es clave en el desarrollo de aplicaciones PHP robustas, permitiendo una mejor organización y manipulación de los recursos del sistema.

Subida de archivos mediante formularios

La subida de archivos mediante formularios es una funcionalidad esencial en muchas aplicaciones web desarrolladas con PHP. Para permitir que los usuarios carguen archivos al servidor, es necesario configurar correctamente tanto el formulario HTML como el script PHP que procesará la solicitud.

El formulario HTML debe incluir el atributo enctype="multipart/form-data" para permitir la transmisión de datos binarios. Además, es imprescindible utilizar el método POST para enviar los datos:

<form action="upload.php" method="post" enctype="multipart/form-data">
    <label for="archivo">Seleccione un archivo:</label>
    <input type="file" name="archivo" id="archivo" />
    <input type="submit" value="Subir archivo" />
</form>

En este formulario, el campo <input type="file" /> permite al usuario seleccionar el archivo que desea subir. El atributo name="archivo" es crucial, ya que PHP utilizará este nombre para acceder al archivo en el arreglo superglobal $_FILES.

En el script upload.php, se debe manejar la recepción del archivo y su posterior validación antes de almacenarlo en una ubicación segura en el servidor:

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_FILES['archivo']) && $_FILES['archivo']['error'] === UPLOAD_ERR_OK) {
        $nombreTmp = $_FILES['archivo']['tmp_name'];
        $nombreArchivo = basename($_FILES['archivo']['name']);
        $rutaDestino = 'uploads' . DIRECTORY_SEPARATOR . $nombreArchivo;

        // Validación del tamaño del archivo
        $tamanoArchivo = $_FILES['archivo']['size'];
        if ($tamanoArchivo > 5000000) { // 5 MB
            echo "El archivo excede el tamaño máximo permitido.\n";
            exit;
        }

        // Validación del tipo de archivo
        $tipoArchivo = mime_content_type($nombreTmp);
        $tiposPermitidos = ['image/jpeg', 'image/png', 'application/pdf'];
        if (!in_array($tipoArchivo, $tiposPermitidos)) {
            echo "El tipo de archivo no está permitido.\n";
            exit;
        }

        // Sanitización del nombre del archivo para evitar problemas de seguridad
        $nombreArchivoSeguro = preg_replace('/[^A-Za-z0-9_\-\.]/', '_', $nombreArchivo);
        $rutaDestino = 'uploads' . DIRECTORY_SEPARATOR . $nombreArchivoSeguro;

        // Mover el archivo a la ubicación final
        if (move_uploaded_file($nombreTmp, $rutaDestino)) {
            echo "El archivo se ha subido correctamente.\n";
        } else {
            echo "Ocurrió un error al mover el archivo.\n";
        }
    } else {
        echo "No se ha enviado ningún archivo o hubo un error en la subida.\n";
    }
}

En este código, se verifica que el método de solicitud sea POST y que no haya errores en la subida del archivo. La constante UPLOAD_ERR_OK indica que la subida se realizó sin problemas. Se utiliza la función basename() para obtener el nombre del archivo y DIRECTORY_SEPARATOR para asegurar la compatibilidad entre sistemas operativos.

Es fundamental validar tanto el tamaño como el tipo del archivo para proteger el servidor de archivos potencialmente dañinos. La función mime_content_type() obtiene el tipo MIME del archivo, y se compara con una lista de tipos permitidos. Además, se sanitiza el nombre del archivo para evitar caracteres no deseados y prevenir ataques de travesía de directorios.

Para manejar errores durante la subida, PHP proporciona códigos de error en $_FILES['archivo']['error']. Es recomendable manejar cada posible error para brindar retroalimentación al usuario:

<?php
$error = $_FILES['archivo']['error'];
if ($error !== UPLOAD_ERR_OK) {
    switch ($error) {
        case UPLOAD_ERR_INI_SIZE:
        case UPLOAD_ERR_FORM_SIZE:
            echo "El archivo es demasiado grande.\n";
            break;
        case UPLOAD_ERR_PARTIAL:
            echo "El archivo se subió parcialmente.\n";
            break;
        case UPLOAD_ERR_NO_FILE:
            echo "No se seleccionó ningún archivo.\n";
            break;
        default:
            echo "Ocurrió un error desconocido en la subida.\n";
            break;
    }
    exit;
}

Para permitir la subida múltiple de archivos, se debe modificar el formulario HTML agregando el atributo multiple y ajustar los nombres de los campos:

<form action="upload.php" method="post" enctype="multipart/form-data">
    <label for="archivos">Seleccione archivos:</label>
    <input type="file" name="archivos[]" id="archivos" multiple />
    <input type="submit" value="Subir archivos" />
</form>

En el script PHP, se puede recorrer el arreglo $_FILES['archivos']['name'] para procesar cada archivo:

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_FILES['archivos'])) {
        $archivos = $_FILES['archivos'];
        $cantidadArchivos = count($archivos['name']);
        for ($i = 0; $i < $cantidadArchivos; $i++) {
            if ($archivos['error'][$i] === UPLOAD_ERR_OK) {
                $nombreTmp = $archivos['tmp_name'][$i];
                $nombreArchivo = basename($archivos['name'][$i]);
                $rutaDestino = 'uploads' . DIRECTORY_SEPARATOR . $nombreArchivo;

                // Validaciones similares al ejemplo anterior

                if (move_uploaded_file($nombreTmp, $rutaDestino)) {
                    echo "El archivo $nombreArchivo se ha subido correctamente.\n";
                } else {
                    echo "Error al mover el archivo $nombreArchivo.\n";
                }
            } else {
                echo "Error en la subida del archivo " . $archivos['name'][$i] . ".\n";
            }
        }
    } else {
        echo "No se han seleccionado archivos.\n";
    }
}

Es esencial implementar medidas de seguridad adicionales, como establecer un directorio específico para los archivos subidos con permisos de acceso adecuados, y evitar ejecutar cualquier código que pueda estar contenido en los archivos subidos.

En el archivo de configuración php.ini, se pueden ajustar los parámetros relacionados con la subida de archivos, como upload_max_filesize y post_max_size:

upload_max_filesize = 10M
post_max_size = 12M

Estos ajustes definen el tamaño máximo permitido para los archivos subidos y el tamaño máximo de los datos enviados vía POST, respectivamente.

Además, es recomendable utilizar funciones como finfo_file() para una validación más estricta del tipo de archivo, y almacenar los archivos con nombres únicos para evitar colisiones y problemas de seguridad:

<?php
$extension = pathinfo($nombreArchivo, PATHINFO_EXTENSION);
$nombreArchivoSeguro = uniqid('archivo_', true) . '.' . $extension;
$rutaDestino = 'uploads' . DIRECTORY_SEPARATOR . $nombreArchivoSeguro;

De esta manera, se asigna un nombre único a cada archivo subido, previniendo posibles sobrescrituras y mejorando la integridad del sistema de archivos.

Es importante recordar que el procesamiento de archivos subidos debe realizarse con rigurosas prácticas de seguridad para proteger la aplicación y el servidor de posibles amenazas.

Permisos y seguridad en archivos

El control de permisos y la seguridad en el manejo de archivos son aspectos críticos en el desarrollo de aplicaciones PHP robustas. Una gestión inapropiada de los permisos puede conducir a vulnerabilidades que comprometan la integridad y confidencialidad de los datos. En sistemas Unix y similares, los permisos de archivos se definen para tres categorías: el usuario propietario, el grupo y otros. Cada categoría tiene permisos de lectura (r), escritura (w) y ejecución (x), representados en notación octal como 4, 2 y 1 respectivamente. Por ejemplo, un permiso 0755 otorga permisos completos al usuario y permisos de lectura y ejecución al grupo y a otros. Para modificar los permisos de un archivo o directorio en PHP, se utiliza la función chmod($ruta, $permisos):

<?php
$ruta = 'archivo.txt';
if (file_exists($ruta)) {
    chmod($ruta, 0644);
    echo "Permisos actualizados a 0644\n";
} else {
    echo "El archivo no existe\n";
}

En este ejemplo, se establecen permisos de lectura y escritura para el usuario, y solo lectura para el grupo y otros. Asignar el propietario y el grupo correctos a los archivos es esencial para asegurar que solo los usuarios autorizados puedan acceder o modificar los archivos. PHP proporciona las funciones chown($ruta, $usuario) y chgrp($ruta, $grupo) para este propósito:

<?php
$ruta = 'archivo.txt';
$usuario = 'www-data';
$grupo = 'www-data';

if (file_exists($ruta)) {
    chown($ruta, $usuario);
    chgrp($ruta, $grupo);
    echo "Propietario y grupo actualizados\n";
} else {
    echo "El archivo no existe\n";
}

Es importante señalar que estas funciones requieren permisos administrativos y que el script PHP debe ejecutarse con los privilegios adecuados. Al trabajar con archivos, es fundamental validar y sanitizar todas las entradas provenientes del usuario. Esto evita que usuarios malintencionados exploten vulnerabilidades como ataques de travesía de directorios (directory traversal), que intentan acceder a archivos fuera del directorio permitido. Para prevenir este tipo de ataques, se puede utilizar la función realpath($ruta) que devuelve la ruta canonical absoluta y permite verificar que esté dentro de un directorio permitido:

<?php
$rutaBase = '/var/www/html/uploads';
$archivo = $_GET['archivo'];
$rutaCompleta = realpath($rutaBase . DIRECTORY_SEPARATOR . $archivo);

if ($rutaCompleta !== false && strpos($rutaCompleta, $rutaBase) === 0) {
    echo "Accediendo al archivo: $rutaCompleta\n";
    // Procesar el archivo
} else {
    echo "Acceso denegado\n";
}

En este código, se verifica que la ruta completa esté dentro del directorio base esperado, evitando accesos no autorizados. Al manejar archivos subidos por usuarios, es crucial evitar que puedan ejecutar código malicioso en el servidor. Una práctica recomendada es almacenar los archivos subidos en un directorio fuera del document root, de manera que no sean accesibles directamente desde el navegador. Además, establecer permisos restrictivos para estos archivos previene su ejecución:

<?php
$rutaDestino = '/var/www/uploads/' . $nombreArchivoSeguro;

// Establecer permisos restrictivos
move_uploaded_file($archivoTmp, $rutaDestino);
chmod($rutaDestino, 0600);

Con permisos 0600, solo el usuario propietario puede leer y escribir el archivo, y nadie tiene permisos de ejecución. La función umask() establece una máscara de permisos que define los permisos predeterminados para archivos y directorios creados durante la ejecución del script:

<?php
umask(0027); // Permisos predeterminados serán 0640 para archivos y 0750 para directorios

mkdir('directorio_seguro');
touch('archivo_seguro.txt');

Aquí, los archivos y directorios creados heredarán permisos que restringen el acceso a otros usuarios, mejorando la seguridad. Cuando se permite a los usuarios especificar archivos a abrir o descargar, es esencial limitar las opciones a un conjunto predefinido o validar estrictamente sus entradas:

<?php
$archivosPermitidos = ['documento1.pdf', 'informe2.pdf'];
$archivoSolicitado = $_GET['archivo'];

if (in_array($archivoSolicitado, $archivosPermitidos)) {
    $ruta = '/var/www/html/descargas/' . $archivoSolicitado;
    // Enviar el archivo al usuario
} else {
    echo "Archivo no permitido\n";
}

Esta técnica asegura que solo se pueda acceder a archivos explícitamente autorizados. Los enlaces simbólicos pueden introducir riesgos si no se controlan adecuadamente. Para evitar que un enlace simbólico apunte a ubicaciones no deseadas, se puede verificar si una ruta es un enlace simbólico utilizando is_link($ruta) y resolver su destino real con readlink($ruta):

<?php
$ruta = '/var/www/html/uploads/archivo.txt';

if (is_link($ruta)) {
    echo "El archivo es un enlace simbólico\n";
    // Opcionalmente, rechazar o manejar el enlace
} else {
    echo "El archivo es regular\n";
}

A nivel de servidor, es recomendable aplicar políticas de seguridad que refuercen la aplicación. Algunas prácticas incluyen desactivar la opción allow_url_fopen en php.ini para evitar inclusiones de archivos remotos, configurar el open_basedir para restringir el acceso a un directorio específico, y utilizar herramientas de detección de intrusiones como ModSecurity. Es fundamental manejar adecuadamente los errores al trabajar con archivos, evitando exponer información sensible en mensajes de error. En entornos de producción, se debe configurar display_errors a Off y registrar los errores en un archivo de log seguro:

<?php
ini_set('display_errors', 'Off');
ini_set('log_errors', 'On');
ini_set('error_log', '/var/log/php_errors.log');

Además, es importante seguir prácticas recomendadas como evitar el uso de permisos 777, no confiar en las extensiones de archivo para determinar su tipo, implementar controles de acceso y mantener el software del servidor actualizado.

Streams y manejo avanzado de archivos

Los streams en PHP proporcionan una forma flexible y poderosa de manejar operaciones de entrada y salida. Un stream es un recurso que permite leer o escribir datos de manera secuencial, sin importar su origen o destino, ya sea un archivo, una red, una cadena de caracteres o incluso memoria.

PHP ofrece una variedad de envoltorios (wrappers) de stream que permiten interactuar con diferentes protocolos y recursos. Algunos de los envoltorios predefinidos incluyen file://, http://, php://, ftp://, entre otros. Estos envoltorios permiten abstraer la forma en que se accede a los datos, facilitando la manipulación de recursos de forma uniforme.

Para abrir un stream y leer datos, se puede utilizar la función fopen() con un envoltorio específico:

<?php
$handle = fopen('http://www.example.com/', 'r');
$contenido = stream_get_contents($handle);
fclose($handle);

En este ejemplo, se accede al contenido de una URL utilizando el envoltorio http://. La función stream_get_contents() lee todo el contenido del stream.

Los contextos de stream permiten establecer opciones y parámetros adicionales para una conexión de stream. Se pueden especificar opciones como cabeceras HTTP, tiempo de espera, métodos de autenticación, entre otros. Para crear un contexto, se utiliza la función stream_context_create():

<?php
$opciones = [
    'http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/json\r\n",
        'content' => json_encode(['clave' => 'valor']),
        'timeout' => 30
    ]
];

$contexto = stream_context_create($opciones);
$resultado = file_get_contents('http://api.ejemplo.com/endpoint', false, $contexto);
echo $resultado . "\n";

En este código, se realiza una solicitud HTTP POST enviando datos en formato JSON, utilizando un contexto que especifica las opciones necesarias.

Los filtros de stream permiten modificar los datos a medida que son leídos o escritos a través de un stream. PHP proporciona filtros predefinidos para tareas comunes como la compresión, codificación y conversión de caracteres. Para adjuntar un filtro a un stream, se utiliza la función stream_filter_append() o stream_filter_prepend():

<?php
$handle = fopen('archivo.txt', 'r');
stream_filter_append($handle, 'string.toupper');

while (($linea = fgets($handle)) !== false) {
    echo $linea . "\n";
}

fclose($handle);

En este ejemplo, el filtro string.toupper convierte el texto leído a mayúsculas antes de ser procesado.

Es posible crear filtros personalizados definiendo una clase que extienda de php_user_filter. Después, se puede registrar el filtro con stream_filter_register():

<?php
class RemoverVocalesFilter extends php_user_filter
{
    public function filter($in, $out, &$consumed, $cerrado): int
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = str_replace(['a', 'e', 'i', 'o', 'u'], '', $bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

stream_filter_register('remover_vocales', 'RemoverVocalesFilter');

$handle = fopen('archivo.txt', 'r');
stream_filter_append($handle, 'remover_vocales');

while (($linea = fgets($handle)) !== false) {
    echo $linea . "\n";
}

fclose($handle);

En este código, se crea un filtro que elimina las vocales de los datos leídos, demostrando cómo se pueden manipular los datos en tiempo real.

Los envoltorios personalizados permiten definir nuevas formas de interactuar con protocolos o recursos. Para crear un envoltorio personalizado, se define una clase que implemente determinados métodos y se registra con stream_wrapper_register():

<?php
class EnvoltorioEjemplo
{
    public function stream_open($path, $mode, $options, &$opened_path)
    {
        // Código para abrir el stream
    }

    public function stream_read($count)
    {
        // Código para leer del stream
    }

    public function stream_write($data)
    {
        // Código para escribir en el stream
    }

    // Otros métodos requeridos
}

stream_wrapper_register('ejemplo', 'EnvoltorioEjemplo');

$archivo = fopen('ejemplo://recurso', 'r');
// Operaciones con el stream personalizado
fclose($archivo);

Crear envoltorios personalizados permite extender la funcionalidad de PHP para adaptarse a necesidades específicas, como acceder a sistemas de archivos remotos o formatos de datos propietarios.

Los contextos también pueden utilizarse con funciones como fopen(), file_get_contents() y file_put_contents() para especificar opciones avanzadas. Por ejemplo, para conectarse a un servidor FTP con autenticación:

<?php
$opciones = [
    'ftp' => [
        'overwrite' => true
    ]
];

$contexto = stream_context_create($opciones);
$handle = fopen('ftp://usuario:contraseña@servidor.com/archivo.txt', 'w', false, $contexto);

fwrite($handle, "Contenido del archivo\n");
fclose($handle);

En este caso, se utiliza el envoltorio ftp:// con credenciales de usuario y se establece la opción de sobrescribir archivos existentes.

Para manejar archivos de gran tamaño sin cargar todo el contenido en memoria, es aconsejable utilizar streams y procesar los datos en bloques. Esto es especialmente útil al trabajar con archivos de video, audio o grandes colecciones de datos.

<?php
$handleLectura = fopen('archivo_grande.dat', 'rb');
$handleEscritura = fopen('copia.dat', 'wb');

$tamanoBloque = 4096; // 4 KB
while (!feof($handleLectura)) {
    $datos = fread($handleLectura, $tamanoBloque);
    fwrite($handleEscritura, $datos);
}

fclose($handleLectura);
fclose($handleEscritura);

Este código copia un archivo grande sin cargarlo completamente en memoria, leyendo y escribiendo en bloques de 4 KB.

PHP también soporta streams fijos en memoria utilizando el envoltorio php://memory y streams temporales usando php://temp. Estos permiten manejar datos en memoria sin necesidad de archivos temporales físicos.

<?php
$stream = fopen('php://memory', 'rw');
fwrite($stream, "Datos en memoria\n");
rewind($stream);
echo stream_get_contents($stream) . "\n";
fclose($stream);

El uso de php://memory es útil para operaciones que requieren un buffer en memoria, como generar contenido dinámico antes de enviarlo al navegador o manipular datos intermedios.

Para realizar operaciones de I/O no bloqueantes, se pueden utilizar funciones como stream_set_blocking() o stream_select(). Esto es relevante en aplicaciones que necesitan manejar múltiples streams simultáneamente sin esperar a que una operación termine antes de iniciar otra.

<?php
$streamsLectura = [$stream1, $stream2];
$streamsEscritura = null;
$streamsExcepcion = null;
$tiempoEspera = 5; // segundos

$recursoDisponible = stream_select($streamsLectura, $streamsEscritura, $streamsExcepcion, $tiempoEspera);

if ($recursoDisponible > 0) {
    foreach ($streamsLectura as $stream) {
        $datos = fread($stream, 8192);
        // Procesar los datos
    }
}

Este enfoque es esencial en aplicaciones concurrentes o en servidores que manejan múltiples conexiones simultáneamente.

Los metadatos de un stream se pueden obtener utilizando la función stream_get_meta_data(), que devuelve información detallada sobre el stream, como los modos de acceso, opciones del wrapper, estado de las operaciones, entre otros.

<?php
$handle = fopen('archivo.txt', 'r');
$metadata = stream_get_meta_data($handle);

echo "Modo de acceso: " . $metadata['mode'] . "\n";
echo "URI: " . $metadata['uri'] . "\n";

fclose($handle);

Conocer los metadatos es útil para depurar aplicaciones y entender cómo están configurados los streams.

Además, los streams en PHP soportan notificaciones y eventos a través de la función stream_notification_callback(). Esto permite recibir información sobre el progreso de operaciones como descargas o transferencias de datos.

<?php
function miNotificacion($codigoNotificacion, $severity, $mensaje, $codigoMensaje, $bytesTransferidos, $bytesMaximos)
{
    switch ($codigoNotificacion) {
        case STREAM_NOTIFY_CONNECT:
            echo "Conexión establecida\n";
            break;
        case STREAM_NOTIFY_PROGRESS:
            if ($bytesMaximos > 0) {
                $porcentaje = ($bytesTransferidos / $bytesMaximos) * 100;
                echo "Progreso: $porcentaje%\n";
            }
            break;
    }
}

$opciones = [
    'notification' => 'miNotificacion'
];

$contexto = stream_context_create([], $opciones);
$archivo = fopen('http://www.example.com/granarchivo.dat', 'r', false, $contexto);

// Procesar el archivo
fclose($archivo);

El uso de notificaciones es especialmente útil en interfaces de usuario donde se requiere mostrar el estado de una operación en curso.

Finalmente, el manejo avanzado de archivos y streams en PHP permite desarrollar aplicaciones más eficientes, con mejor rendimiento y mayor flexibilidad. Al comprender y utilizar estas herramientas, es posible abordar problemas complejos de entrada y salida, optimizar el uso de recursos y ofrecer soluciones adaptadas a necesidades específicas.

Para seguir leyendo hazte Plus

¿Ya eres Plus? Accede a la app

20 % DE DESCUENTO

Plan mensual

19.00 /mes

15.20 € /mes

Precio normal mensual: 19 €
58 % DE DESCUENTO

Plan anual

10.00 /mes

8.00 € /mes

Ahorras 132 € al año
Precio normal anual: 120 €
Aprende PHP GRATIS online

Todas las lecciones de PHP

Accede a todas las lecciones de PHP y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Php

PHP

Introducción Y Entorno

Instalación Y Primer Programa De Php

PHP

Introducción Y Entorno

Tipos De Datos, Variables Y Constantes

PHP

Sintaxis

Operadores Y Expresiones

PHP

Sintaxis

Estructuras De Control

PHP

Sintaxis

Funciones Y Llamada De Funciones

PHP

Sintaxis

Cadenas De Texto Y Manipulación

PHP

Sintaxis

Manejo De Números

PHP

Sintaxis

Manejo De Fechas Y Tiempo

PHP

Sintaxis

Manejo De Arrays

PHP

Sintaxis

Introducción A La Poo En Php

PHP

Programación Orientada A Objetos

Clases Y Objetos

PHP

Programación Orientada A Objetos

Constructores Y Destructores

PHP

Programación Orientada A Objetos

Herencia

PHP

Programación Orientada A Objetos

Encapsulación

PHP

Programación Orientada A Objetos

Polimorfismo

PHP

Programación Orientada A Objetos

Interfaces

PHP

Programación Orientada A Objetos

Traits

PHP

Programación Orientada A Objetos

Namespaces

PHP

Programación Orientada A Objetos

Autoloading De Clases

PHP

Programación Orientada A Objetos

Manejo De Errores Y Excepciones

PHP

Programación Orientada A Objetos

Manejo De Archivos

PHP

Programación Orientada A Objetos

Patrones De Diseño

PHP

Programación Orientada A Objetos

Introducción A Los Formularios En Php

PHP

Formularios

Procesamiento De Datos De Formularios

PHP

Formularios

Manejo De Archivos En Formularios

PHP

Formularios

Redirecciones Y Retroalimentación Al Usuario

PHP

Formularios

Formularios Dinámicos Y Separación De Lógica

PHP

Formularios

Introducción A La Persistencia En Php

PHP

Persistencia

Conexión A Bases De Datos

PHP

Persistencia

Consultas Y Operaciones Crud

PHP

Persistencia

Gestión De Transacciones

PHP

Persistencia

Manejo De Errores Y Excepciones En Base De Datos

PHP

Persistencia

Patrones De Acceso A Datos

PHP

Persistencia

Concepto De Sesiones En Php

PHP

Sesiones Y Cookies

Configuración De Sesiones

PHP

Sesiones Y Cookies

Cookies

PHP

Sesiones Y Cookies

Manejo Avanzado De Sesiones Y Cookies

PHP

Sesiones Y Cookies

Principales Vulnerabilidades En Php

PHP

Seguridad

Seguridad En Formularios Y Entrada De Datos

PHP

Seguridad

Protección Frente A Inyección Sql

PHP

Seguridad

Gestión De Contraseñas Y Cifrado

PHP

Seguridad

Seguridad En Sesiones Y Cookies

PHP

Seguridad

Configuraciones De Php Para Seguridad

PHP

Seguridad

Introducción Al Testing En Php

PHP

Testing

Phpunit

PHP

Testing

Cobertura De Código En Testing

PHP

Testing

Test Doubles (Mocks, Stubs, Fakes, Spies)

PHP

Testing

Pruebas De Integración Y Funcionales

PHP

Testing

Accede GRATIS a PHP y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  1. Leer y escribir archivos usando funciones PHP.
  2. Manejar directorios y validar su existencia.
  3. Implementar seguridad al trabajar con permiso de archivos.
  4. Subir archivos con formularios y manejar errores de subida.