Как работает разрешение путей включения в require_once?

Я писал веб-приложение на PHP, когда столкнулся со странной ситуацией. Чтобы проиллюстрировать мою проблему, рассмотрим веб-приложение этой структуры:

/
    index.php
    f1/
        f1.php
    f2/
        f2.php

Содержимое этих файлов:

индекс.php:

<?php require_once("f1/f1.php"); ?>

f1.php:

<?php require_once("../f2/f2.php"); ?>

f2.php: пусто

теперь, когда я пытаюсь открыть index.php в своем браузере, я получаю эту ошибку:

Warning: require_once(../f2/f2.php) [function.require-once]: 
failed to open stream: No such file or directory in /var/www/reqtest/f1/f1.php on line 2
Fatal error: require_once() [function.require]: 
Failed opening required '../f2/f2.php' (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/reqtest/f1/f1.php on line 2

Есть ли что-то очевидное, что я упускаю? как включить пути в PHP?


Прежде чем я задал этот вопрос, я попытался поэкспериментировать и выяснить. Я настроил еще один тест, например:

/
    index.php
    f1/
        f1.php
        f2.php

индекс.php:

<?php require_once("f1/f1.php"); ?>

f1.php:

<?php require_once("f2.php"); ?>

f2.php: пусто

К моему удивлению (и полному замешательству), это сработало отлично!

Итак, в чем секрет разрешения пути?

PS Я видел этот вопрос, но он по-прежнему не отвечает на второй случай, который я изложил здесь.


person jrharshath    schedule 29.12.2009    source источник
comment
Я уже обошел эту проблему (используя имя каталога). Я хочу знать, почему второй случай не терпит неудачу. Это баг или фича?   -  person jrharshath    schedule 29.12.2009
comment
Отредактировал мой ответ, чтобы охватить второй пример.   -  person Karsten    schedule 29.12.2009
comment
Я не могу найти справочную страницу, в которой задокументирован успешный вызов require_once('f2.php') из f1.php. Документы говорят, что include_path игнорируется, когда информация о пути не указана (в любом случае удаление '.' из include_path не имеет никакого эффекта), а getcwd() показывает, что рабочий каталог одинаков во всей цепочке включения. Серьезно, это похоже на недокументированную функцию.   -  person Álvaro González    schedule 29.12.2009
comment
Полезная статья: cjhaas.com/2019/05/21/php- include-path-сюрпризы   -  person Dan Stevens    schedule 06.12.2019


Ответы (5)


Если вы включаете другой файл, рабочий каталог остается там, где находится включаемый файл.

Ваши примеры работают по назначению.

Изменить: второй пример работает, потому что . (фактический каталог) находится в вашем пути включения (см. ваше сообщение об ошибке).

Edit2: Во втором примере ключевым моментом, который вас интересует, является эта строка:

<?php require_once("f2.php"); ?>

Сначала он будет искать в текущем рабочем каталоге (/var/www/req_path_test), но не находит f2.php.

В качестве запасного варианта он попытается найти f2.php в вашем include_path ('.:/usr/share/php:/usr/share/pear'), начиная с '.' (относительно фактического файла, а не включающего).

Итак, «./f2.php» работает, и требование не завершается ошибкой.

person Karsten    schedule 29.12.2009
comment
Я пробовал echo:ing getcwd() в index.php и f1.php. Вывод из обоих мест /var/www/req_path_test так что. нахождение в пути включения, вероятно, означает, что /var/www/req_path_test находится в пути включения. - person jrharshath; 29.12.2009
comment
и в подтверждение этого факта я попробовал require_once("f2/f2.php"). Это сработало, когда я открываю index.php, но не когда открываю f1/f1.php. - person jrharshath; 29.12.2009
comment
хм, это кажется наиболее подходящим объяснением... не могли бы вы указать какие-либо источники? - person jrharshath; 29.12.2009
comment
Я думаю, что это работает против других языков. По этой причине я бы назвал это конструктивным недостатком. Автор этого комментария демонстрирует, что будут делать другие языки/системы. - person Josh Robinson; 23.10.2015

Когда вы открываете index.php, рабочий каталог устанавливается в папку, в которой находится этот файл. И внутри включенного f1.php этот рабочий каталог не изменяется.

Вы можете включать файлы, используя их абсолютные пути относительно текущего включаемого файла, например:

require_once(dirname(__FILE__).'/../../test/file.php')

Но лучше рассмотреть возможность использования автозагрузчика, если эти файлы содержат классы.

person Vladislav Rastrusny    schedule 29.12.2009
comment
Попробуйте добавить 'echo getcwd();' к вашим сценариям, чтобы увидеть, как и если текущий рабочий каталог изменяется. php.net/manual/en/function.getcwd.php — комментарии здесь говорит, что его поведение изменилось, например, с PHP4 на PHP5. - person Vladislav Rastrusny; 29.12.2009

Обычно в вашей старой структуре

<?php require_once("f2/f2.php"); ?>

вместо

<?php require_once("../f2/f2.php"); ?>

должно сработать. Насколько я знаю, php берет пути из исходного скрипта

person user207675    schedule 29.12.2009
comment
Я знаю. Что я действительно хочу знать, так это то, почему второй случай не терпит неудачу. Это не проблема, это зуд :) - person jrharshath; 29.12.2009

Похоже, что на вашем сервере включен параметр open_basedir. в конфигурации PHP. Это делает невозможным включение (и открытие) файлов в папках выше вашей в структуре каталогов (т. е. вы не можете использовать ../ для перехода вверх по структуре папок).

person Emil Vikström    schedule 29.12.2009
comment
на моем сервере параметр open_basedir не имеет значения. Что бы это значило? - person jrharshath; 29.12.2009

Из документации по PHP включение PHP

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

Если путь к файлу не указан, то, например, require_once("f2.php");

1-й. include_path проверен

2-й. Проверяется собственная директория вызывающих скриптов

3-й. Наконец, текущий рабочий каталог проверяется

Если файл не найден, PHP выдает предупреждение о включении файла и фатальную ошибку при запросе

Если путь определен — будь то абсолютный (начинающийся с буквы диска или \ в Windows или / в системах Unix/Linux) или относительный к текущему каталогу (начинающийся с . или ..) — include_path будет полностью проигнорирован. Например, если имя файла начинается с ../, синтаксический анализатор будет искать запрошенный файл в родительском каталоге.

Если вы включаете/требуете, чтобы ваш файл начинался с . или .. или ./ тогда синтаксический анализатор PHP будет искать в родительском каталоге, который является текущим рабочим каталогом, т.е. require_once("../f2/f2.php"), php проверит корневой каталог как вызывающий скрипт index.php находится в этом каталоге.

Теперь вы не определили какой-либо путь включения в свой скрипт PHP, поэтому он всегда возвращается к вызывающему скрипту, а затем в текущий рабочий каталог.

// Check your default include path, most likely to be C:\xampp\php\PEAR
echo get_include_path();

// To set include path
set_include_path ( string $new_include_path ) : string

Текущий рабочий каталог получается из вашего основного вызывающего скрипта index.php.

// The Current Working Directory can be checked
echo getcwd();

В первом примере, где требуемый файл "../f2/f2.php" взят из f1.php

Ваш код не работает, потому что -

  1. Указанный путь игнорируется PHP, так как имя вашего файла начинается с ../
  2. f1/ собственный каталог вызывающего скрипта также игнорируется.
  3. Каталог синтаксического анализатора просматривает родительский каталог, чтобы найти запрошенный файл. Текущий рабочий каталог — это корневой каталог, из которого вы инициировали рабочий скрипт index.php. Файл находится не в этом каталоге, указан неправильный путь.

Таким образом, вы получаете фатальную ошибку

Во втором примере вы изменили каталог и из f1.php вам требуется_once("f2.php").

Ваш код работает, потому что -

  1. На этот раз вам требуется("f2.php") без ведущих ../ или ./ На этот раз PHP проверяет include_path, но находит его там, так как вы его не определили, и файл не находится в предустановленном include_path по умолчанию.
  2. На этот раз директория вызывающего скрипта f1.php — f1/. и вам нужен файл ("f2.php") находится в этом каталоге. PHP На этот раз проверяет файл в этом каталоге и находит его.
  3. PHP не должен проверять рабочий каталог, так как файл был найден.

Таким образом, ваш код работает нормально!

person Mohammad Daud Ibrahim    schedule 27.02.2019