В PHP скриптовете, независимо дали се извикват include()
, require()
, fopen()
или техните производни, като include_once
, require_once
или дори move_uploaded_file()
, често се среща грешка или предупреждение:
Неуспешно отваряне на поток: Няма такъв файл или директория.
Какъв е добрият процес за бързо намиране на основната причина за проблема?
Има много причини, поради които човек може да се сблъска с тази грешка, и затова един добър контролен списък за това какво да се провери първо помага значително. Нека разгледаме, че отстраняваме следния ред:
require "/path/to/file"
require*
или include*
, в собствена променлива, повторете го, копирайте го и опитайте да го достъпите от терминал:
$path = "/path/to/file";
echo "Path : $path";
require "$path";
След това в терминал:
cat <вмъкнат път до файла>
/users/tony/htdocs
require __DIR__ . "/relative/path/from/current/file"
. Магическата константа __DIR__
връща директорията на текущия файл.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 настройки като пътя за включване.
Друг начин за включване на файлове, нито относително, нито чисто абсолютно, е да се разчита на include path. Това често се случва при библиотеки или рамки, като например рамката Zend. Такова включване ще изглежда по следния начин :
include "Zend/Mail/Protocol/Imap.php"
В този случай ще трябва да се уверите, че папката, в която се намира "Zend", е част от пътя за включване. Можете да проверите пътя на включване с :
echo get_include_path();
Можете да добавите папка към него с :
set_include_path(get_include_path().":"."/path/to/new/folder");
Възможно е всички заедно да се окаже, че потребителят, който изпълнява процеса на сървъра (Apache или PHP), просто няма'разрешение да чете от или да записва в този файл. За да проверите под какъв потребител работи сървърът, можете да използвате posix_getpwuid :
$user = posix_getpwuid(posix_geteuid());
var_dump($user);
За да разберете правата на файла, въведете следната команда в терминала:
ls -l <path/to/file>
и погледнете в permission symbolic notation.
Ако нито едно от горните действия не е дало резултат, тогава проблемът вероятно е, че някои настройки на PHP забраняват достъпа до този файл. Три настройки могат да бъдат от значение :
phpinfo()
или чрез използване на ini_get("open_basedir")
ini_get("allow_url_include")
и да се зададе с ini_set("allow_url_include", "1")
Ако нито един от горепосочените начини не е позволил да се диагностицира проблемът, ето някои специални ситуации, които могат да се случат :
Може да се случи да включите библиотека, например рамката Zend, като използвате относителен или абсолютен път. Например :
require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"
Но тогава все още се получава същата грешка. Това може да се случи, защото файлът, който сте включили (успешно), има в себе си инструкция за включване за друг файл и тази втора инструкция за включване предполага, че сте добавили пътя на тази библиотека към пътя за включване. Например, споменатият преди това файл на Zend framework може да съдържа следното include :
include "Zend/Mail/Protocol/Exception.php"
което не е включване нито по относителен път, нито по абсолютен път. Предполага се, че директорията Zend framework е добавена към пътя за включване.
В такъв случай единственото практическо решение е да добавите директорията към пътя за включване.
Ако използвате 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 за уеб сървър.
Ако използвате Symfony и изпитвате тази грешка при качване на сървър, тогава може да се окаже, че кешът на приложението не е бил нулиран, или защото app/cache
е бил качен, или че кешът не е бил изчистен.
Можете да проверите и поправите това, като изпълните следната конзолна команда:
cache:clear
Очевидно тази грешка може да се случи и при извикване на zip->close()
, когато някои файлове вътре в ZIP файла имат неASCII символи в името си, като например "é".
Възможно решение е да се обвие името на файла в utf8_decode()
преди създаването на целевия файл.
Кредити на Fran Cano за идентифицирането и предлагането на решение на този проблем
Да добавим към (наистина добрия) съществуващ отговор
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 тук
Друга възможна причина: Преименуване и/или преместване на файлове, докато сте в текстов редактор. Преминах през всички стъпки по-горе без успех, докато не изтрих файла, който продължаваше да причинява тази грешка, и не създадох нов, което реши проблема.