Я столкнулся с этой проблемой сам, прежде всего в отношении нормализации путей.
Нормализация:
- Один разделитель (я выбрал поддержку, но никогда не возвращаю обратную косую черту
\\
)
- Разрешение косвенности:
/../
- Удаление дублирующих разделителей:
/home/www/uploads//file.ext
- Всегда снимать концевой разделитель.
Я написал функцию, которая достигает этого. У меня нет доступа к этому коду сейчас, но это не так сложно, чтобы написать его самостоятельно.
Независимо от того, является ли путь абсолютным или нет, для реализации этой функции нормализации значения не имеет, просто следите за ведущим разделителем, и все в порядке.
Меня не слишком беспокоит зависимость от ОС. Как Windows, так и Linux PHP понимают /
, поэтому для простоты я просто всегда использую это - но я думаю, что не имеет значения, какой разделитель вы используете.
Чтобы ответить на ваш вопрос: объединение путей может быть очень простым, если вы просто всегда используете /
и , предполагая , что в каталоге нет конечного разделителя. «Конечный разделитель отсутствует» кажется хорошим предположением, поскольку такие функции, как dirname
, удаляют конечный разделитель.
Тогда это всегда безопасно: $dir . "/" . $file
.
И даже если путь к результату /home/uploads/../uploads//my_uploads/myfile.ext
, он все равно будет работать нормально.
Нормализация становится полезной, когда вам нужно где-то сохранить путь. И поскольку у вас есть эта нормализующая функция, вы можете сделать эти предположения.
Еще одна полезная функция - это функция для создания относительных путей.
- / файлы / загрузки
- / файлы / загрузки / my_uploads / myfile.ext
Из этих двух путей может быть полезно определить относительный путь к файлу.
Realpath
Я обнаружил, что realpath
чрезвычайно тяжелая производительность. Это не так плохо, если вы звоните один раз, но если вы делаете это где-то в цикле, вы получите довольно большой успех. Помните, что каждый вызов realpath
также является вызовом файловой системы. Кроме того, он просто вернет false
, если вы передадите что-нибудь глупое, я бы предпочел, чтобы оно выдало исключение.
Для меня функция realpath
является хорошим примером ПЛОХОЙ функции , потому что она делает две вещи: 1. Нормализует путь и 2. Проверяет, существует ли путь. Обе эти функции, конечно, полезны, но они должны быть разделены. Он также не различает файлы и каталоги. Для окон это обычно не проблема, но для Linux это может быть.
И я думаю, что при использовании realpath("")
в Windows есть некоторые странности. Я думаю, он вернется \\
- что может быть совершенно неприемлемо.
/**
* This function is a proper replacement for realpath
* It will _only_ normalize the path and resolve indirections (.. and .)
* Normalization includes:
* - directiory separator is always /
* - there is never a trailing directory separator
* @param $path
* @return String
*/
function normalize_path($path) {
$parts = preg_split(":[\\\/]:", $path); // split on known directory separators
// resolve relative paths
for ($i = 0; $i < count($parts); $i +=1) {
if ($parts[$i] === "..") { // resolve ..
if ($i === 0) {
throw new Exception("Cannot resolve path, path seems invalid: `" . $path . "`");
}
unset($parts[$i - 1]);
unset($parts[$i]);
$parts = array_values($parts);
$i -= 2;
} else if ($parts[$i] === ".") { // resolve .
unset($parts[$i]);
$parts = array_values($parts);
$i -= 1;
}
if ($i > 0 && $parts[$i] === "") { // remove empty parts
unset($parts[$i]);
$parts = array_values($parts);
}
}
return implode("/", $parts);
}
/**
* Removes base path from longer path. The resulting path will never contain a leading directory separator
* Base path must occur in longer path
* Paths will be normalized
* @throws Exception
* @param $base_path
* @param $longer_path
* @return string normalized relative path
*/
function make_relative_path($base_path, $longer_path) {
$base_path = normalize_path($base_path);
$longer_path = normalize_path($longer_path);
if (0 !== strpos($longer_path, $base_path)) {
throw new Exception("Can not make relative path, base path does not occur at 0 in longer path: `" . $base_path . "`, `" . $longer_path . "`");
}
return substr($longer_path, strlen($base_path) + 1);
}