我正在尝试在文件系统抽象中强制执行根目录。我遇到的问题如下:
API允许您读取和写入文件,不仅是本地文件,还包括远程存储文件。因此,各种各样的规范化正在进行中。目前它不支持相对路径,所以不可能这样:
$filesystem->write('path/to/some/../relative/file.txt', 'file contents');
我希望能够安全地解析路径,因此输出结果为:path/to/relative/file.txt
。
正如在为此错误/增强(https://github.com/FrenkyNet/Flysystem/issues/36#issuecomment-30319406)创建的github问题中所述,它需要执行更多操作,只需拆分段并相应地删除它们。
此外,由于程序包处理远程文件系统和不存在的文件,因此不能使用realpath。
那么,在处理这些路径时应该怎么做呢?
答案 0 :(得分:11)
有些人在面对问题时会想“我知道,我会使用正则表达式”。 现在他们有两个问题。
protected function getAbsoluteFilename($filename) {
$path = [];
foreach(explode('/', $filename) as $part) {
// ignore parts that have no value
if (empty($part) || $part === '.') continue;
if ($part !== '..') {
// cool, we found a new part
array_push($path, $part);
}
else if (count($path) > 0) {
// going back up? sure
array_pop($path);
} else {
// now, here we don't like
throw new \Exception('Climbing above the root is not permitted.');
}
}
// prepend my root directory
array_unshift($path, $this->getPath());
return join('/', $path);
}
答案 1 :(得分:6)
我已经解决了如何做到这一点,这是我的解决方案:
/**
* Normalize path
*
* @param string $path
* @param string $separator
* @return string normalized path
*/
public function normalizePath($path, $separator = '\\/')
{
// Remove any kind of funky unicode whitespace
$normalized = preg_replace('#\p{C}+|^\./#u', '', $path);
// Path remove self referring paths ("/./").
$normalized = preg_replace('#/\.(?=/)|^\./|\./$#', '', $normalized);
// Regex for resolving relative paths
$regex = '#\/*[^/\.]+/\.\.#Uu';
while (preg_match($regex, $normalized)) {
$normalized = preg_replace($regex, '', $normalized);
}
if (preg_match('#/\.{2}|\.{2}/#', $normalized)) {
throw new LogicException('Path is outside of the defined root, path: [' . $path . '], resolved: [' . $normalized . ']');
}
return trim($normalized, $separator);
}
答案 2 :(得分:0)
./当前位置
../上一级
function normalize_path($str){
$N = 0;
$A =explode("/",preg_replace("/\/\.\//",'/',$str)); // remove current_location
$B=[];
for($i = sizeof($A)-1;$i>=0;--$i){
if(trim($A[$i]) ===".."){
$N++;
}else{
if($N>0){
$N--;
}
else{
$B[] = $A[$i];
}
}
}
return implode("/",array_reverse($B));
}
如此:
"a/b/c/../../d" -> "a/d"
"a/./b" -> "a/b"
答案 3 :(得分:-1)
/**
* Remove '.' and '..' path parts and make path absolute without
* resolving symlinks.
*
* Examples:
*
* resolvePath("test/./me/../now/", false);
* => test/now
*
* resolvePath("test///.///me///../now/", true);
* => /home/example/test/now
*
* resolvePath("test/./me/../now/", "/www/example.com");
* => /www/example.com/test/now
*
* resolvePath("/test/./me/../now/", "/www/example.com");
* => /test/now
*
* @access public
* @param string $path
* @param mixed $basePath resolve paths realtively to this path. Params:
* STRING: prefix with this path;
* TRUE: use current dir;
* FALSE: keep relative (default)
* @return string resolved path
*/
function resolvePath($path, $basePath=false) {
// Make absolute path
if (substr($path, 0, 1) !== DIRECTORY_SEPARATOR) {
if ($basePath === true) {
// Get PWD first to avoid getcwd() resolving symlinks if in symlinked folder
$path=(getenv('PWD') ?: getcwd()).DIRECTORY_SEPARATOR.$path;
} elseif (strlen($basePath)) {
$path=$basePath.DIRECTORY_SEPARATOR.$path;
}
}
// Resolve '.' and '..'
$components=array();
foreach(explode(DIRECTORY_SEPARATOR, rtrim($path, DIRECTORY_SEPARATOR)) as $name) {
if ($name === '..') {
array_pop($components);
} elseif ($name !== '.' && !(count($components) && $name === '')) {
// … && !(count($components) && $name === '') - we want to keep initial '/' for abs paths
$components[]=$name;
}
}
return implode(DIRECTORY_SEPARATOR, $components);
}