PHP - Не удалось открыть поток: нет такого файла или каталога

В скриптах PHP, вызывающих include(), require(), fopen() или их производных, таких как include_once, require_once или даже, move_uploaded_file(), часто возникает ошибка или предупреждение:

Не удалось открыть поток: нет такого файла или каталога.

Что такое хороший процесс, чтобы быстро найти основную причину проблемы?

Ответ 1

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

Давайте рассмотрим, что мы устраняем неисправности в следующей строке:

require "/path/to/file"


Контрольный


1. Проверьте путь к файлу для опечаток

  • либо проверяйте вручную (визуально проверяя путь)
  • или переместите все, что вызвано require* или include*, в свою переменную, отобразите его, скопируйте и попробуйте получить доступ к нему из терминала:

    $path = "/path/to/file";
    
    echo "Path : $path";
    
    require "$path";
    

    Затем в терминале:

    cat <file path pasted>
    


2. Проверьте, правильно ли указан путь к файлу относительно относительного или абсолютного пути.

  • если он начинается с косой черты "/", то это относится не к корню папки вашего сайта (корню документа), а к корню вашего сервера.
    • например, каталог вашего сайта может быть /users/tony/htdocs
  • если он не начинается с косой черты, то он либо полагается на путь включения (см. ниже), либо путь является относительным. Если он относительный, то PHP рассчитает относительно пути к текущему рабочему каталогу.
    • таким образом, не относительно пути к корню вашего веб-сайта или файла, в который вы вводите
    • по этой причине всегда используйте абсолютные пути к файлам

Лучшие практики:

Чтобы сделать ваш сценарий надежным на случай, если вы все передвигаете, и при этом генерируете абсолютный путь во время выполнения, у вас есть 2 варианта:

  1. используйте require __DIR__ . "/relative/path/from/current/file". Волшебная константа __DIR__ возвращает каталог текущего файла.
  2. определите константу SITE_ROOT самостоятельно:

    • в корне каталога вашего веб-сайта создайте файл, например, config.php
    • в config.php напишите

      define('SITE_ROOT', __DIR__);
      
    • в каждом файле, где вы хотите сослаться на корневую папку сайта, включите config.php, а затем используйте константу SITE_ROOT, где хотите:

      require_once __DIR__."/../config.php";
      ...
      require_once SITE_ROOT."/other/file.php";
      

Эти 2 метода также делают ваше приложение более переносимым, поскольку оно не зависит от настроек ini, таких как путь включения.


3. Проверьте ваш путь включения

Другой способ включения файлов, ни относительный, ни сугубо абсолютный, заключается в использовании пути включения. Это часто относится к библиотекам или фреймворкам, таким как Zend Framework.

Такое включение будет выглядеть так:

include "Zend/Mail/Protocol/Imap.php"

В этом случае вам нужно убедиться, что папка, в которой находится "Zend", является частью пути включения.

Вы можете проверить путь включения с помощью:

echo get_include_path();

Вы можете добавить в него папку с помощью:

set_include_path(get_include_path().":"."/path/to/new/folder");


4. Убедитесь, что ваш сервер имеет доступ к этому файлу

Может случиться так, что все вместе, пользователь, выполняющий процесс сервера (Apache или PHP), просто не имеет разрешения на чтение или запись в этот файл.

Чтобы проверить, под каким пользователем работает сервер, вы можете использовать posix_getpwuid :

$user = posix_getpwuid(posix_geteuid());

var_dump($user);

Чтобы узнать разрешения для файла, введите в терминале следующую команду:

ls -l <path/to/file>

и посмотрите символьную запись разрешения


5. Проверьте настройки PHP

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

Три настройки могут иметь отношение:

  1. open_basedir
    • Если это установлено, PHP не сможет получить доступ ни к какому файлу за пределами указанного каталога (даже через символическую ссылку).
    • Тем не менее, поведение по умолчанию не должно быть установлено, в этом случае нет никаких ограничений
    • Это можно проверить, позвонив phpinfo() или воспользовавшись ini_get("open_basedir")
    • Вы можете изменить настройку, отредактировав файл php.ini или файл httpd.conf
  2. .безопасный режим
    • если это включено, ограничения могут применяться. Однако это было удалено в PHP 5.4. Если вы все еще используете версию, которая поддерживает обновление в безопасном режиме, до версии PHP, которая все еще поддерживается.
  3. allow_url_fopen и allow_url_include
    • это относится только к включению или открытию файлов через сетевой процесс, такой как http://не при попытке включить файлы в локальную файловую систему
    • это можно проверить с помощью ini_get("allow_url_include") и установить с помощью ini_set("allow_url_include", "1")


Angular чехлы

Если ничего из перечисленного не позволяет диагностировать проблему, вот несколько особых ситуаций, которые могут возникнуть:


1. Включение библиотеки с использованием пути включения

Может случиться так, что вы включите библиотеку, например, Zend Framework, используя относительный или абсолютный путь. Например:

require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"

Но тогда вы все равно получаете такую же ошибку.

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

Например, упомянутый ранее файл Zend Framework может содержать следующее:

include "Zend/Mail/Protocol/Exception.php" 

который не является ни включением ни по относительному пути, ни по абсолютному пути. Предполагается, что каталог Zend Framework был добавлен в путь включения.

В таком случае единственное практическое решение - добавить каталог в путь включения.


2. SELinux

Если вы работаете в Security-Enhanced Linux, это может быть причиной проблемы, поскольку доступ к файлу с сервера запрещен.

Чтобы проверить, включен ли SELinux в вашей системе, выполните команду sestatus в терминале. Если команда не существует, значит, SELinux отсутствует в вашей системе. Если он существует, он должен сообщить вам, применяется ли он принудительно или нет.

Чтобы проверить, являются ли политики SELinux причиной проблемы, вы можете временно отключить ее. Однако будьте осторожны, так как это полностью отключит защиту. Не делайте этого на своем производственном сервере.

setenforce 0

Если проблема с выключенным SELinux больше не возникает, это является основной причиной.

Чтобы решить эту проблему, вам необходимо соответствующим образом настроить SELinux.

Будут необходимы следующие типы контекста:

  • httpd_sys_content_t для файлов, которые вы хотите, чтобы ваш сервер мог читать
  • httpd_sys_rw_content_t для файлов, к которым вы хотите иметь права на чтение и запись
  • httpd_log_t для файлов журнала
  • httpd_cache_t для каталога кэша

Например, чтобы назначить тип контекста httpd_sys_content_t для корневого каталога вашего сайта, выполните:

semanage fcontext -a -t httpd_sys_content_t "/path/to/root(/.*)?"
restorecon -Rv /path/to/root

Если ваш файл находится в домашнем каталоге, вам также нужно включить логическое значение httpd_enable_homedirs:

setsebool -P httpd_enable_homedirs 1

В любом случае может быть множество причин, по которым SELinux может запретить доступ к файлу, в зависимости от ваших политик. Так что вам нужно будет выяснить это. Здесь - руководство по настройке SELinux для веб-сервера.


3. Symfony

Если вы используете Symfony и сталкиваетесь с этой ошибкой при загрузке на сервер, возможно, кэш приложения не был сброшен, либо потому, что app/cache был загружен, либо этот кэш не был очищен.

Вы можете проверить и исправить это, выполнив следующую консольную команду:

cache:clear


4. Не ACSII символы внутри Zip файла

По-видимому, эта ошибка также может возникать при вызове zip->close(), когда в некоторых файлах внутри zip есть не-ASCII-символы в имени файла, такие как "é".

Возможное решение - обернуть имя файла в utf8_decode() перед созданием целевого файла.

Кредиты Фрэн Кано для определения и предложения решения этой проблемы

Ответ 2

Чтобы добавить к (действительно хорошему) существующий ответ

Программное обеспечение общего хостинга

open_basedir - это тот, который может пнуть вас, потому что он может быть указан в конфигурации веб-сервера. Хотя это легко исправить, если вы запускаете собственный выделенный сервер, есть некоторые пакеты хостинга для совместного использования (например, Plesk, cPanel и т.д.), Которые будут настраивать директиву конфигурации для каждого домена. Поскольку программное обеспечение создает файл конфигурации (т.е. httpd.conf), вы не можете изменить этот файл напрямую, поскольку программное обеспечение хостинга просто перезапишет его при перезапуске.

С Plesk они предоставляют место для переопределения предоставленного httpd.conf, называемого vhost.conf. Этот файл может написать только администратор сервера. Конфигурация для Apache выглядит примерно так:

<Directory /var/www/vhosts/domain.com>
    <IfModule mod_php5.c>
        php_admin_flag engine on
        php_admin_flag safe_mode off
        php_admin_value open_basedir "/var/www/vhosts/domain.com:/tmp:/usr/share/pear:/local/PEAR"
    </IfModule>
</Directory>

Попросите администратора вашего сервера обратиться к руководству по использованию хостинга и веб-серверного программного обеспечения, которое они используют.

Разрешения файла

Важно отметить, что выполнение файла через ваш веб-сервер сильно отличается от выполнения командной строки или выполнения задания cron. Большая разница в том, что ваш веб-сервер имеет свой собственный пользователь и разрешения. По соображениям безопасности пользователь довольно ограничен. Apache, например, часто apache, www-data или httpd (в зависимости от вашего сервера). Задача cron или выполнение CLI имеет все права, которые пользователь запускает (например, запуск PHP скрипт как root будет выполняться с правами root).

Много раз люди решат проблему с разрешениями, выполняя следующие действия (пример Linux)

chmod 777 /path/to/file

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

Что вам нужно сделать, так это определить пользователя (пользователей), которым нужен доступ, и предоставить только те, к которым они имеют доступ. Как только вы узнаете, какие пользователи нуждаются в доступе, вы захотите убедиться, что

  • Этот пользователь имеет файл и, возможно, родительский каталог (особенно родительский каталог, если вы хотите писать файлы). В большинстве общедоступных хостинговых сред это не будет проблемой, потому что ваш пользователь должен владеть всеми файлами под вашим корнем. Пример Linux приведен ниже.

    chown apache:apache /path/to/file
    
  • Пользователь и только тот пользователь имеет доступ. В Linux хорошей практикой будет chmod 600 (только владелец может читать и писать) или chmod 644 (владелец может писать, но каждый может читать)

Вы можете прочитать более расширенное обсуждение разрешений и пользователей Linux/Unix здесь

Ответ 3

  1. Посмотрите на точную ошибку

Мой код работал нормально на всех машинах, но только на этом начали возникать проблемы (которые раньше работали, я думаю). Использовал echo "document_root" путь для отладки, а также внимательно посмотрел на ошибку, нашел это

Предупреждение: включают в себя (D: /MyProjects/testproject//functions/connections.php): не удалось открыть поток:

Вы можете легко увидеть, где проблемы. Проблемы//перед функциями

$document_root = $_SERVER['DOCUMENT_ROOT'];
echo "root: $document_root";
include($document_root.'/functions/connections.php');

Так что просто удалите накладку/из include, и она должна работать нормально. Что интересно, это поведение отличается в разных версиях. Я запускаю один и тот же код на ноутбуке, Macbook Pro и на этом компьютере, пока все работало нормально. Надеюсь, это кому-нибудь поможет.

  1. Скопируйте файл в браузере, чтобы убедиться, что файл существует. Иногда файлы неожиданно удаляются (случилось со мной), и это также было проблемой в моем случае.

Ответ 4

Добавить скрипт с параметрами запроса

Это был мой случай. На самом деле это ссылка на вопроС# 4485874, но я собираюсь объяснить это здесь в ближайшее время.
Когда вы пытаетесь потребовать path/to/script.php?parameter=value, PHP ищет файл с именем script.php?parameter=value, потому что UNIX позволяет вам иметь такие пути.
Если вам действительно нужно передать некоторые данные во включенный скрипт, просто объявите его как $variable=... или $GLOBALS[]=... или другим способом, который вам нравится.

Ответ 5

Акции Самбы

Если у вас есть тестовый сервер Linux, и вы работаете с клиентом Windows, общий ресурс Samba вмешивается в команду chmod. Так что, даже если вы используете:

chmod -R 777 myfolder

на стороне Linux вполне возможно, что Unix Group\www-data по-прежнему не имеет доступа для записи. Одно из рабочих решений, если ваш общий ресурс настроен так, что администраторы Windows отображаются в root: в Windows откройте "Разрешения", отключите наследование для вашей папки с копией, а затем предоставьте полный доступ к www-данным.

Ответ 6

Другая возможная причина: переименование и/или перемещение файлов в текстовом редакторе. Я прошел все этапы без успеха, пока не удалил файл, который продолжал бросать эту ошибку и создавал новую, которая исправила проблему.