PHP конкатенация путей - PullRequest
       46

PHP конкатенация путей

4 голосов
/ 24 июля 2011

Есть ли внутренняя функция PHP для конкатенации путей? Какие возможности у меня есть, чтобы объединить несколько путей (абсолютных и относительных).

//Example: 
$path1="/usr/home/username/www";
$path2="domainname";
$path3="images/thumbnails";
$domain="exampledomain.com";

//As example: Now I want to create a new path (domain + path3) on the fly. 
$result = $domain.DIRECTORY_SEPARATOR.$path3

Хорошо, в этом примере есть простое решение, но что, если есть разные словарные разделители или некоторые пути немного сложнее?

Существует ли существующее решение для обрезки, например: /home/uploads/../uploads/tmp => / home / uploads / tmp ....

А как будет выглядеть независимая от платформы версия path-concat-function?

должен ли относительный путь начинаться с "./" в качестве префикса или это "home / path / img /" обычный путь?

Ответы [ 3 ]

5 голосов
/ 24 июля 2011

Я столкнулся с этой проблемой сам, прежде всего в отношении нормализации путей.

Нормализация:

  • Один разделитель (я выбрал поддержку, но никогда не возвращаю обратную косую черту \\)
  • Разрешение косвенности: /../
  • Удаление дублирующих разделителей: /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);
}
4 голосов
/ 24 июля 2011

Если путь действительно существует, вы можете использовать realpath для его расширения.

echo realpath("/home/../home/dogbert")
/home/dogbert
0 голосов
/ 06 августа 2014

Другая проблема с realpath заключается в том, что он не работает с URL-адресами, и, тем не менее, логика конкатенации по существу одинакова (между двумя соединенными компонентами должен быть ровно один слеш). Очевидно, что часть протокола в начале с двумя слешами является исключением из этого правила. Но все же, функция, которая объединяет части URL вместе, была бы действительно хороша.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...