Web

Ошибка при открытии потока в PHP: отсутствует файл или каталог

В PHP скриптах, вызывая include()require()fopen() или их производные, такие как include_oncerequire_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", который  возвращает каталог текущего файла;

  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";

 

Эти действия делают ваше приложение более переносимым, поскольку они не зависят от настроек 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").

 

Другие случаи

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

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

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

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

 

Но по-прежнему получаете такую же ошибку.

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

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

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

 

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

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

 

2. SELinux

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

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

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

setenforce0

 

Если проблема с выключенным 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. Внутри Zip-файла символы, отличные от ASCII

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

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

 

Ответ 2

В дополнение к предыдущему ответу:

Программное обеспечение для виртуального хостинга

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

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, например, часто бывает apachewww-data или httpd (в зависимости от сервера). Задание cron или выполнение CLI имеет все разрешения, которые есть у запускающего его пользователя (т. е. запуск PHP-скрипта от имени root будет выполняться с разрешениями root).

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

chmod 777 /path/to/file

 

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

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

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

  2. В Linux хорошей практикой будет использованиеchmod 600 (только владелец может читать и писать) или chmod 644 (владелец может писать, но все могут читать).



Ответ 3

Samba Shares

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

chmod -R 777 myfolder

 

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

Схожие статьи

Web

Как использовать символы Юникода в PHP строках

Web

Как проверить, что все скобки правильно открыты/закрыты

Web

Есть ли функция для извлечения «столбца» из массива в PHP?

Web

Как я могу измерить скорость кода, написанного на PHP

×