Manejar rutas de ficheros de forma portable y segura en PHP requiere cuatro funciones: dirname() para subir en el árbol de directorios, basename() para obtener el nombre del fichero, pathinfo() para descomponer una ruta en sus partes, y realpath() para resolver rutas absolutas y detectar ataques de path traversal.
dirname(): obtener el directorio padre
<?php
$ruta = '/var/www/app/src/Controllers/UserController.php';
echo dirname($ruta); // /var/www/app/src/Controllers
echo dirname($ruta, 2); // /var/www/app/src (subir 2 niveles)
echo dirname($ruta, 3); // /var/www/app
echo dirname($ruta, 4); // /var/www
echo dirname($ruta, 5); // /var
// Uso habitual en autoloaders y configuraciones
define('BASE_DIR', dirname(__DIR__)); // directorio padre del script actual
define('CONFIG_DIR', dirname(__DIR__) . '/config');
define('STORAGE_DIR', dirname(__DIR__, 2) . '/storage');
?>
basename(): obtener el nombre del fichero
<?php
$ruta = '/var/www/app/uploads/imagen-portada.jpg';
echo basename($ruta); // imagen-portada.jpg
echo basename($ruta, '.jpg'); // imagen-portada (sin extensión)
// Con directorios
echo basename('/var/www/app/src'); // src
// Uso en uploads: generar nombre único manteniendo la extensión original
$original = $_FILES['foto']['name'] ?? '';
$extension = strtolower(pathinfo($original, PATHINFO_EXTENSION));
$nombre = uniqid('img_', true) . '.' . $extension;
?>
pathinfo(): descomponer una ruta en sus partes
<?php $ruta = '/var/www/app/uploads/informe-2026.pdf'; // Descomposición completa: devuelve array $partes = pathinfo($ruta); echo $partes['dirname']; // /var/www/app/uploads echo $partes['basename']; // informe-2026.pdf echo $partes['filename']; // informe-2026 echo $partes['extension']; // pdf // Obtener solo una parte (más eficiente) echo pathinfo($ruta, PATHINFO_DIRNAME); // /var/www/app/uploads echo pathinfo($ruta, PATHINFO_BASENAME); // informe-2026.pdf echo pathinfo($ruta, PATHINFO_FILENAME); // informe-2026 echo pathinfo($ruta, PATHINFO_EXTENSION); // pdf ?>
realpath(): resolver rutas absolutas
realpath() resuelve ., .., enlaces simbólicos y convierte una ruta relativa en absoluta. Devuelve false si el fichero no existe:
<?php
// Resolver rutas con puntos
echo realpath('/var/www/../www/app'); // /var/www/app
echo realpath('/var/www/app/./src'); // /var/www/app/src
echo realpath('/no/existe'); // false
// Ruta relativa a absoluta
chdir('/var/www/app');
echo realpath('src/Controllers'); // /var/www/app/src/Controllers
// Resolver enlace simbólico
echo realpath('/var/www/html'); // /var/www/app/public (si hay symlink)
?>
Protección contra ataques de path traversal
Path traversal es un ataque donde el usuario proporciona rutas con ../ para acceder a ficheros fuera del directorio permitido. realpath() es la forma más sencilla de detectarlo:
<?php
function rutaSegura(string $nombreFichero, string $directorioBase): string
{
// Eliminar caracteres nulos (bypass conocido)
$nombreFichero = str_replace(" ", '', $nombreFichero);
// Resolver la ruta completa
$rutaCompleta = realpath($directorioBase . '/' . $nombreFichero);
if ($rutaCompleta === false) {
throw new RuntimeException('El fichero no existe');
}
// Verificar que la ruta resuelta está dentro del directorio base
$directorioBase = realpath($directorioBase);
if (!str_starts_with($rutaCompleta, $directorioBase . '/')) {
throw new RuntimeException('Acceso denegado: ruta fuera del directorio permitido');
}
return $rutaCompleta;
}
// Ejemplo de uso
$base = '/var/www/app/uploads';
// Petición legítima
$segura = rutaSegura('documentos/informe.pdf', $base);
// /var/www/app/uploads/documentos/informe.pdf ?
// Intento de path traversal
rutaSegura('../../../etc/passwd', $base);
// RuntimeException: Acceso denegado ?
?>
Combinar las cuatro funciones
<?php
// Cambiar la extensión de un fichero manteniendo el directorio
function cambiarExtension(string $ruta, string $nuevaExt): string
{
$dir = dirname($ruta);
$nombre = pathinfo($ruta, PATHINFO_FILENAME);
$nuevaExt = ltrim($nuevaExt, '.');
return "$dir/$nombre.$nuevaExt";
}
echo cambiarExtension('/var/www/app/img/foto.jpg', 'webp');
// /var/www/app/img/foto.webp
// Convertir ruta relativa a absoluta y verificar que existe en el proyecto
function resolverRutaProyecto(string $relativa): string
{
$base = realpath(dirname(__DIR__));
$candidata = $base . '/' . ltrim($relativa, '/');
$real = realpath($candidata);
if ($real === false || !str_starts_with($real, $base)) {
throw new RuntimeException("Ruta no válida: $relativa");
}
return $real;
}
?>
La documentación de realpath() y la de pathinfo() incluyen notas sobre el comportamiento en Windows (separadores de ruta) y con enlaces simbólicos.
