Какие каталоги проверяет PHP при включении относительного пути с помощью include()?

Очень странно, кто-нибудь когда-нибудь подвел итог?

Иногда он также проверяет каталог включенного файла.

Но иногда нет.

Д:\тест\1.php

<?php

include('sub\2.php');

Д:\тест\2.php

<?php

include('3.php');

Где 3.php находится в том же каталоге, что и 2.php.

Вышеупомянутое работает, но почему? Текущий каталог должен быть D:\test, но он все еще может найти 3.php, который находится в D:\test\sub

Дополнительная история(последняя)

Около года назад я столкнулся с этой проблемой, а затем исправил ее с помощью жесткого кодирования, как показано ниже:

Общий.php:

if (file_exists("../../../Common/PHP/Config.inc"))
    include('../../../Common/PHP/Config.inc');

if (file_exists("../../Common/PHP/Config.inc"))
    include('../../Common/PHP/Config.inc');

if (file_exists("../Common/PHP/Config.inc"))
    include('../Common/PHP/Config.inc');

if (file_exists("Common/PHP/Config.inc"))
    include('Common/PHP/Config.inc');

Где Config.inc находится в том же каталоге, что и Common.php


person user198729    schedule 13.03.2010    source источник
comment
Хороший вопрос! Я могу подтвердить это на Windows и Linux. Я понятия не имею, почему это так.   -  person Pekka    schedule 13.03.2010
comment
Что касается вашего последнего примера, если Config.inc находится в том же каталоге, что и Common.php, то его можно упростить до include(dirname(__FILE__).'/Config.inc'); — это всегда будет работать, независимо от include_path и того, в какой файл Common.php включен. Если нет возможности найти Config.inc в include_path (куда часто включается текущий каталог), то вы можете просто вызвать include 'Config.inc';, хотя это, возможно, менее эффективно, поскольку сначала выполняется поиск в include_path (что не удается).   -  person MrWhite    schedule 10.02.2015
comment
В вашем первом примере я предполагаю, что D:\test\2.php должен быть D:\test\sub\2.php? (Иначе include('sub\2.php'); никогда бы не сработало.)   -  person MrWhite    schedule 10.02.2015
comment
Я только что испытал то, что вы описываете. Я всегда включал файлы без пути в свой включенный файл (где оба файла существуют в одном и том же подкаталоге), и у меня это работает на нескольких серверах. В пятницу на одном сервере это внезапно перестало работать. Теперь мне нужно указать путь во включении, иначе он его не найдет. Понятия не имею почему. Одинаковые версии PHP на всех серверах. Если вы когда-нибудь догадались об этом, я хотел бы получить обновленную информацию о вашей истории.   -  person Vincent    schedule 09.02.2020


Ответы (3)


Если вы посмотрите на исходный код php в main/fopen_wrappers.c, вы найдете

/* check in calling scripts' current working directory as a fall back case
     */
    if (zend_is_executing(TSRMLS_C)) {
        char *exec_fname = zend_get_executed_filename(TSRMLS_C);
        int exec_fname_length = strlen(exec_fname);

        while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
        if (exec_fname && exec_fname[0] != '[' &&
            exec_fname_length > 0 &&
            exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
            memcpy(trypath, exec_fname, exec_fname_length + 1);
            memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
            actual_path = trypath;

Похоже, что это выполняется безоговорочно и, следовательно, всегда будет делать файл по тому же пути, что и сценарий включения/открытия файла, доступным... в качестве последнего выбора после всех возможностей, указанных в include_path. И только если вы не определяете относительный или абсолютный путь в include().

person VolkerK    schedule 13.03.2010
comment
Кажется, я не понимаю этот код, есть ли какие-то подсказки, почему иногда он не проверяет каталог включенного файла? - person user198729; 13.03.2010
comment
Для 3.php он сначала проверяет все местоположения, указанные в include_path. Относительный путь, указанный там, например. include_path=.;..;lalala будет относиться к текущему рабочему каталогу (который не изменяется с помощью include()). Только последний (дополнительный) вариант — искать в том же каталоге, в котором находится включаемый файл. Он берет первое 3.php, которое может найти. Это ваша проблема: php захватывает другой 3.php, как вы и ожидали? Другие ваши вопросы, похоже, указывают на то, что... - person VolkerK; 13.03.2010
comment
Моя проблема в том, что иногда он не находит 3.php, но я не могу легко его воспроизвести. Но он находится в том же каталоге, что и включаемый файл. Возможно, когда include находится в функции/методе, я думаю. . - person user198729; 13.03.2010
comment
Итак, для уточнения: в этом случае он не находит никакого 3.php (не просто неправильный, а вообще ни одного). Вы не можете воспроизвести это легко... хм. Это происходит на разных серверах/версиях/конфигурациях? Или это похоже на гейзенбаг, который возникает на той же машине с тем же кодом, не затрагивая ничего, что кажется связанным? - person VolkerK; 13.03.2010
comment
Да. Это происходит на разных серверах, я не думаю, что это гейзенбаг. - person user198729; 13.03.2010
comment
И вы уверены, что проблема действительно сводится к примеру, который вы привели в вопросе? (Извините за ворчание, но у меня такое чувство, что в примере чего-то не хватает, и вопрос Вышеупомянутое работает, но почему? как сейчас ответил ;-)) - person VolkerK; 13.03.2010
comment
@VolkerK, это отвечает на половину вопроса (почему это работает), вы пропустили часть Но иногда нет в моем посте? :-) - person user198729; 13.03.2010
comment
Да, я это сделал ;-) Но так как я ничего не понимаю, возможно, вы можете добавить какую-то информацию. Например. Это происходит на разных серверах, и это должно заставить вас нажать кнопку редактирования исходного вопроса. Возможно, вы заметили какую-то закономерность. Версия php, что-то в настройке include_path на серверах, что-то примечательное в команде configure в выводе phpoinfo() на разных серверах... что угодно ;-) Поскольку, как вы можете видеть из всех ответов, это поведение php не является чем-то слишком очевидным. - person VolkerK; 13.03.2010
comment
Я предоставил всю историю (imo) здесь: stackoverflow.com/questions/2438356/ - person user198729; 13.03.2010
comment
Хорошо, это история о хаке, который вы хотите добавить в свой код. Я имел в виду историю о том, как вы обнаружили и исследовали основную проблему. Это может быть только я, но я никогда раньше не видел кода, который должен был прибегнуть к debug_backtrace(), чтобы получить правильное включение ;-) Прямо сейчас вы работаете над решением проблемы. И когда хаки уходят, они возвращаются к вам в наименее благоприятное время и наименее благоприятным образом. - person VolkerK; 13.03.2010
comment
Да, рад, что вы полностью поняли мою проблему (которая не может быть легко воспроизведена :():-) Наконец, я вручную изменил cwd на chdir() в качестве обходного пути, который намного безопаснее/эффективнее (imo).. - person user198729; 13.03.2010
comment
Только один последний вопрос... какие версии php задействованы? В ветке 4.3/5.0 было несколько исправлений (и возвратов), которые могли повлиять на то, выполняет ли php код, который я цитировал выше, или нет. - person VolkerK; 13.03.2010
comment
@VolkerK, PHP 5.3.0. Но я не думаю, что это связано с ООП, потому что я заметил это до использования классов, но я не стал думать о причине в то время :-) - person user198729; 13.03.2010
comment
Каждый задействованный сервер - это php 5.3? Хорошо, тогда я понятия не имею (и нет, я имел в виду не oop, а zend_stream_open_function ;-)) - person VolkerK; 13.03.2010
comment
+1 никогда не знал, что php будет использовать каталог файла, который был включен, в крайнем случае. Я всегда думал, что cwd и include_path были единственными вариантами. - person goat; 13.03.2010

Он проверяет текущий путь и каталоги, перечисленные в include_path.

Вы можете запустить phpinfo(), чтобы увидеть путь включения.

person Pekka    schedule 13.03.2010
comment
Это не объясняет проблему, которую я описал :( - person user198729; 13.03.2010
comment
@user, насколько я знаю, это все, что нужно. Пожалуйста, покажите несколько примеров, когда файлы загружаются, а когда нет. - person Pekka; 13.03.2010
comment
Изменится ли getcwd() во время запроса, если мы не изменим его явно (например, chdir()). Скоро я приведу пример. - person user198729; 13.03.2010
comment
Если под текущим путем вы подразумеваете текущий рабочий каталог, тогда CWD проверяется только в том случае, если он включен как часть include_path (что по умолчанию). Однако, если под текущим путем вы подразумеваете каталог, содержащий скрипт (т.е. dirname(__FILE__), который имеет оператор include и не обязательно совпадает с CWD), то он проверяется только после того, как ему не удается найти его в include_path. - person MrWhite; 10.02.2015

Иногда каталог включаемого файла указан как current working directory, а иногда нет
Текущий каталог можно проверить с помощью getcwd()

person Your Common Sense    schedule 13.03.2010