You just should validate the input before you use it.
For example you might want to only allow the characters a-z and / to allow subdirectories. Probably you want to allow the . as well. If you make this subset small, it's easy to validate if the input is allowed or not by the allowed characters already.
At the moment you allow ., as you have noticed, you have the problem that relative paths could be created like /../../ which could be used for directory traversal attacks.
To validate if a string contains only characters of a specific range, you can validate this with a regular expression or the filter functions. If your website does not need to allow any relative path parts you can look if they exist in the path to validate the input:
$valid = !array_intersect(array('', '.', '..'), explode('/', $path));
Valid will be FALSE if there is any // or /./ or /../ part inside the path.
If you need to allow relative paths, realpath has already been suggested, so to query the input against your directory structure first. I would only use it as last resort as it is relatively expensive, but it's good to know about.
However you can resolve the string your own as well with some simple function like the following one:
/**
* resolve path to itself
*
* @param string $path
* @return string resolved path
*/
function resolvePath($path)
{
$path = trim($path, '/');
$segmentsIn = explode('/', $path);
$segmentsOut = array();
foreach ($segmentsIn as $in)
{
switch ($in)
{
case '':
$segmentsOut = array();
break;
case '.':
break;
case '..';
array_pop($segmentsOut);
break;
default:
$segmentsOut[] = $in;
}
}
return implode('/', $segmentsOut);
}
Usage:
$tests = array(
'hello',
'world/.',
'../minka',
'../../42',
'../.bar',
'../hello/path/./to/../../world',
);
foreach($tests as $path)
{
printf("%s -> %s\n", $path, resolvePath($path));
}
Output:
hello -> hello
world/. -> world
../minka -> minka
../../42 -> 42
../.bar -> .bar
../hello/path/./to/../../world -> hello/world
I can only suggest you first validate the input based on it's own data before letting touch it the filesystem, even through realpath.