<?php
function safeJoin(string $base, string ...$parts): string {
$base = rtrim(realpath($base) ?: $base, DIRECTORY_SEPARATOR);
$joined = $base . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $parts);
// Resolve "." and ".." segments without requiring the file to exist.
$segments = [];
foreach (explode(DIRECTORY_SEPARATOR, $joined) as $s) {
if ($s === '' || $s === '.') continue;
if ($s === '..') { array_pop($segments); continue; }
$segments[] = $s;
}
$resolved = DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $segments);
if (!str_starts_with($resolved, $base . DIRECTORY_SEPARATOR) && $resolved !== $base) {
throw new RuntimeException('Path traversal attempt blocked');
}
return $resolved;
}
echo safeJoin('/var/www/uploads', 'user123', 'avatar.jpg');
// /var/www/uploads/user123/avatar.jpg
// safeJoin('/var/www/uploads', '../../etc/passwd') → throws
Create a free account and build your private vault. Share publicly whenever you want.