Включает ли PHP пути относительно файла или вызывающего кода?

111

У меня проблемы с пониманием набора правил, касающихся относительных путей включения PHP. Если я запускаю файл A.PHP- и файл A.PHP включает файл B.PHP, который включает файл C.PHP, должен ли относительный путь к C.PHP быть относительно местоположения B.PHP или местоположения A .PHP? То есть имеет значение, из какого файла вызывается include, или только из того, что является текущим рабочим каталогом, и что определяет текущий рабочий каталог?

Ярин
источник
На мой взгляд, более точный, чем принятый ответ: stackoverflow.com/a/23902890/1636522 .
лист

Ответы:

132

Это относительно основного скрипта, в данном случае A.php. Помните, что это include()просто вставляет код в текущий скрипт.

То есть имеет значение, из какого файла вызывается включение?

Нет.

Если вы хотите сделать это важным и выполнить включение относительно B.php, используйте __FILE__константу (или __DIR__начиная с PHP 5.2 IIRC), которая всегда будет указывать на буквальный текущий файл, в котором находится строка кода.

include(dirname(__FILE__)."/C.PHP");
Пекка
источник
@ Pekka - круто - именно то, что я искал. Для получения дополнительной информации см. Stackoverflow.com/questions/2184810/…
Ярин
7
Вы также можете использовать __DIR__для этой цели.
Ник Бедфорд
1
Этот ответ несколько излишний: НЕТ НЕОБХОДИМО include(dirname(__FILE__)."/C.PHP");, потому что этого include("C.PHP");достаточно (да! C.PHP может находиться в том же каталоге, что и B.PHP). Он может завершиться ошибкой только тогда, когда в вашем проекте есть два файла C.PHP.
Джонни Вонг,
Для большей ясности: он относится как к B.php, так и к A.php, если в пути включения нет префикса «./» или «../». Смотрите мой ответ ниже.
Джонни Вонг,
21

@Pekka привел меня туда, но я просто хочу поделиться тем, что я узнал:

getcwd() возвращает каталог, в котором находится файл, который вы начали выполнять.

dirname(__FILE__) возвращает каталог файла, содержащего исполняемый в данный момент код.

Используя эти две функции, вы всегда можете создать путь включения относительно того, что вам нужно.

например, если b.php и c.php совместно используют каталог, b.php может включать c.php, например:

include(dirname(__FILE__).'/c.php');

независимо от того, откуда был вызван b.php.

Фактически, это предпочтительный способ установки относительных путей, поскольку дополнительный код освобождает PHP от необходимости повторять include_path в попытке найти целевой файл.

Источники:

Разница между getcwd () и dirname (__ FILE__)? Что мне использовать?

Почему вам следует использовать dirname (__ FILE__)

Ярин
источник
Я хотел бы что-то добавить, у меня был файл csb.php, включал файл функций из папки, а файл функций включал файл с именем t.php из той же папки, что и файл функций. Когда я пытался включить файл с именем csb.php из папки t.php, он начал включать тот же csb.php, который вызывал файл функций, но когда я изменил второй csb.php на csbe.php, он сразу начал работать. Итак, похоже, что приоритет отдается первой папке, а затем второй включаемой папке!
Мигель Виейра
18
  1. Если путь включения не начинается с ./или ../, например:

    include 'C.php'; // precedence: include_path (which include '.' at first),
                     // then path of current `.php` file (i.e. `B.php`), then `.`.
  2. Если путь включения начинается с ./или ../, например:

    include './C.php';  // relative to '.'
    
    include '../C.php'; // also relative to '.'

Значение .или ..выше относится к getcwd(), что по умолчанию соответствует пути к входному .phpфайлу (т.е. A.php).

Протестировано на PHP 5.4.3 (Дата сборки: 8 мая 2012 г., 00:47:34).

(Также обратите внимание, что chdir()можно изменить вывод getcwd().)

Джонни Вонг
источник
В заключение, сначала ищется путь A.php для C.php, затем ищется путь B.php, если в include нет префикса './' или '../'. (Предположим, что настройки PHP по умолчанию)
Джонни Вонг
Спасибо за подсказку, как использовать chdir(__DIR__)для решения проблемы.
HartleySan,
[...] getcwd(), который по умолчанию соответствует пути к файлу входа .php [...] - не обязательно верно. Если я запускаю PHP в командной строке, он getcwd()обращается к текущему рабочему каталогу оболочки, независимо от того, какой .phpфайл я вызываю. Однако я могу представить, что если PHP запускается в среде веб-сервера, среда инициализирует текущий рабочий каталог в .phpфайл записи . Протестировано на macOS с PHP 7.2.2, установленным через Homebrew.
herzbube
17

Принятый ответ Пекки неполон и в общем контексте вводит в заблуждение. Если файл указан как относительный путь, вызываемая языковая конструкция includeбудет искать его следующим образом.

Во-первых, он пройдется по путям переменной окружения include_path, которую можно установить с помощью ini_set. Если это не удается, он будет искать в собственном каталоге вызывающего скрипта dirname(__FILE__)( __DIR__с php> = 5.3.). Если и это не удается, только тогда он будет искать в рабочем каталоге! Оказывается, по умолчанию переменная окружения include_pathначинается с .текущего рабочего каталога. Это единственная причина, по которой он сначала выполняет поиск в текущем рабочем каталоге. См. Http://php.net/manual/en/function.include.php .

Файлы включаются на основе указанного пути к файлу или, если он не указан, указанного include_path. Если файл не найден в include_path, include, наконец, проверит собственный каталог вызывающего скрипта и текущий рабочий каталог перед ошибкой.

Итак, правильный ответ на первую часть вопроса состоит в том, что не имеет значения, где находится включенный вызывающий скрипт. Ответ на последнюю часть вопроса заключается в том, что исходный рабочий каталог в контексте веб-сервера - это каталог вызываемого скрипта, скрипта, который включает в себя все остальные, пока обрабатывается PHP. В контексте командной строки исходный рабочий каталог - это то, что он есть, когда php вызывается в командной строке, не обязательно каталог, в котором находится вызываемый скрипт. Однако текущий рабочий каталог можно изменить во время выполнения с помощью функции PHP chdir. См. Http://php.net/manual/en/function.chdir.php .

Этот абзац добавлен для комментариев к другим ответам. Некоторые упомянули, что использование в системе include_pathменее надежно и поэтому предпочтительнее использовать полные пути, такие как ./pathили __DIR__ . /path. Некоторые дошли до того, что полагаться на сам рабочий каталог .небезопасно, потому что его можно изменить. Однако иногда вам нужно полагаться на ценности среды. Например, вы можете указать include_pathпустое значение, чтобы каталог вызывающего скрипта был первым местом, в котором он будет искать, даже до текущего рабочего каталога. Код может быть уже написан и регулярно обновляться из внешних источников, и вы не хотите повторно вставлять префикс __DIR__каждый раз при обновлении кода.


источник
"если это не удается, он будет искать в собственном каталоге вызывающего скрипта dirname(__FILE__) (__DIR__)с php> = 5.3.)" Вы уверены? Где это задокументировано? Я надеюсь, что вы ошибаетесь, и PHP не использует __FILE__и __DIR__для этой цели, так как это сразу нарушит включение «родственных» скриптов из символьных ! : -o (Что, к счастью, здесь работает нормально, на моей настройке 7.1.)
Sz.
6

Краткий ответ: это относительно включающего скрипта.

TFM правильно объясняет это:

Если файл не найден в include_path, include проверит каталог вызывающего скрипта и текущий рабочий каталог.

Итак, если /app/main.php говорит, include("./inc.php")что найдет /app/inc.php .

./ не является строго необходимым , но удаляет какую - либо зависимость от include_path.

Я бы не стал полагаться на поиск включаемых файлов в текущем рабочем каталоге на случай, если кто-то изменит его с помощью chdir().

Денис Хау
источник
Итак, если вы начинаете строку с ./, проверяет ли она сначала каталог вызывающего сценария или текущий рабочий каталог?
Pacerier
2
@Denis Howe Нет, вы уже зависите от текущего рабочего каталога, если ваш путь включения начинается с ./. т.е. chdir ("/ app / other") приведет к include("./inc.php")ошибке. Таким образом, include("inc.php")будьте осторожны в этом случае.
Джонни Вонг
@Pacerier, если строка начинается с ./ или ../, проверяется только текущий рабочий каталог. (include_path и каталог вызывающего скрипта (B.php) игнорируются). См. Мой ответ для более подробной информации.
Джонни Вонг
-2
dir
-> a.php
-> c.php

- dir2 
-> b.php

Чтобы включить aв bвам нужноinclude("../a.php");

Чтобы включить bв cвам нужноinclude("dir2/b.php");

Олли
источник
4
@ Oll - вы упускаете суть вопроса - я спрашивал о том, как определяются относительные пути, когда включаются в цепочку.
Ярин