PHP scenarijuose, skambinant include()
, require()
, fopen()
arba jų išvestinėmis funkcijomis, tokiomis kaip include_once
, require_once
ar net move_uploaded_file()
, dažnai susiduriama su klaida arba įspėjimu:
Nepavyko atidaryti srauto: Nėra tokio failo ar katalogo.
Koks yra geras procesas, padedantis greitai rasti pagrindinę problemos priežastį?
Priežasčių, kodėl galima susidurti su šia klaida, yra daug, todėl geras kontrolinis sąrašas, ką pirmiausia reikia patikrinti, labai padeda. Panagrinėkime, kad šaliname šią eilutę:
require "/path/to/file"
require*
arba include*
, į atskirą kintamąjį, echo, nukopijuokite jį ir pabandykite pasiekti iš terminalo:
$path = "/path/to/file";
echo "Kelias : $path";
require "$path";
Tada terminale:
cat <įterptas failo kelias>
/users/tony/htdocs
require __DIR__ . "/relative/path/from/current/file"
. Magiškoji konstanta __DIR__
grąžina dabartinio failo katalogą.SITE_ROOT
:config.php
.config.php
įrašykite
define('SITE_ROOT', DIR);config.php
, o tada naudokite konstantą SITE_ROOT
visur, kur tik norite:
require_once DIR."/../config.php";
...
require_once SITE_ROOT."/other/file.php";
Dėl šių 2 praktikų jūsų programa taip pat tampa lengviau perkeliama, nes ji nepriklauso nuo ini nustatymų, pavyzdžiui, įtraukimo kelio.
Dar vienas būdas įtraukti failus, nei santykinai, nei grynai absoliučiai, yra pasikliauti include path. Dažnai taip būna su bibliotekomis arba karkasais, pavyzdžiui, "Zend" karkasu. Toks įtraukimas atrodys taip :
include "Zend/Mail/Protocol/Imap.php"
Tokiu atveju norėsite įsitikinti, kad aplankas, kuriame yra "Zend", yra įtraukties kelio dalis. Galite patikrinti įtraukimo kelią naudodami :
echo get_include_path();
Galite pridėti aplanką prie jo naudodami :
set_include_path(get_include_path().":"."/path/to/new/folder");
Gali būti, kad visi kartu serverio procesą (Apache arba PHP) valdantis naudotojas tiesiog neturi teisės skaityti iš šio failo arba rašyti į jį. Norėdami patikrinti, su kokiu vartotoju veikia serveris, galite naudoti posix_getpwuid :
$user = posix_getpwuid(posix_geteuid());
var_dump($user);
Norėdami sužinoti failo teises, terminale įveskite šią komandą:
ls -l <path/to/file>
ir pažiūrėkite leidimų simbolinis užrašas.
Jei nė vienas iš pirmiau pateiktų būdų nepadėjo, problema greičiausiai yra ta, kad kai kurie PHP nustatymai draudžia pasiekti tą failą. Gali būti svarbūs trys nustatymai :
phpinfo()
arba naudojant ini_get("open_basedir")
ini_get("allow_url_include")
ir nustatyti su ini_set("allow_url_include", "1")
Jei nė vienas iš pirmiau pateiktų būdų nepadėjo diagnozuoti problemos, štai keletas ypatingų situacijų, kurios gali nutikti :
Gali atsitikti taip, kad įtrauksite biblioteką, pavyzdžiui, "Zend framework", naudodami santykinį arba absoliutų kelią. Pavyzdžiui :
require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"
Tačiau tada vis tiek gaunama tokia pati klaida. Taip gali atsitikti todėl, kad failas, kurį (sėkmingai) įtraukėte, pats turi kito failo įtraukimo teiginį, o tas antrasis įtraukimo teiginys daro prielaidą, kad į įtraukimo kelią įtraukėte tos bibliotekos kelią. Pavyzdžiui, anksčiau minėtame Zend framework faile gali būti toks include :
include "Zend/Mail/Protocol/Exception.php"
Tai nėra nei įtraukimas pagal santykinį, nei pagal absoliutų kelią. Daroma prielaida, kad Zend framework katalogas buvo įtrauktas į include kelią.
Tokiu atveju vienintelis praktinis sprendimas yra įtraukti katalogą į include kelią.
Jei naudojate "Linux" su padidinto saugumo sistema, tai gali būti problemos priežastis, nes neleidžiama prieiti prie failo iš serverio.
Jei norite patikrinti, ar jūsų sistemoje įjungta SELinux, terminale paleiskite komandą sestatus
. Jei komanda neegzistuoja, vadinasi, jūsų sistemoje SELinux nėra įjungta. Jei ji egzistuoja, ji turėtų pasakyti, ar ji įvesta, ar ne.
Tam, kad patikrintumėte, ar SELinux politika yra problemos priežastis, galite pabandyti ją laikinai išjungti. Tačiau būkite ATSARGŪS, nes taip apsauga bus visiškai išjungta. Nedarykite to savo gamybiniame serveryje.
setenforce 0
Jei išjungus "SELinux" problemos nebeturite, vadinasi, tai yra pagrindinė priežastis. Norėdami ją išspręsti, turėsite atitinkamai sukonfigūruoti "SELinux". Reikės nustatyti šiuos konteksto tipus :
httpd_sys_content_t
failams, kuriuos norite, kad jūsų serveris galėtų skaitytihttpd_sys_rw_content_t
- failams, kuriuos norite gauti prieigą skaityti ir rašytihttpd_log_t
žurnalo failamshttpd_cache_t
- talpyklos katalogui
Pavyzdžiui, norėdami priskirti httpd_sys_content_t
konteksto tipą savo svetainės šakniniam katalogui, paleiskite :semanage fcontext -a -t httpd_sys_content_t "/path/to/root(/.*)?"
restorecon -Rv /path/to/root
Jei jūsų failas yra namų kataloge, taip pat reikės įjungti httpd_enable_homedirs
loginį ženklą :
setsebool -P httpd_enable_homedirs 1
Bet kokiu atveju, gali būti įvairių priežasčių, dėl kurių SELinux neleis pasiekti failo, priklausomai nuo jūsų politikos. Taigi jums reikės apie tai pasidomėti. Čia yra specialiai SELinux konfigūravimui žiniatinklio serveryje skirta pamoka.
Jei naudojate "Symfony" ir patiriate šią klaidą įkeldami į serverį, gali būti, kad programos talpykla nebuvo iš naujo nustatyta, nes buvo įkelta app/cache
arba talpykla nebuvo išvalyta.
Tai galite patikrinti ir ištaisyti paleidę šią konsolės komandą:
cache:clear
Akivaizdu, kad ši klaida gali įvykti ir iškvietus zip->close()
, kai kai kurių failų, esančių ZIP faile, pavadinime yra ne ASCII simbolių, pavyzdžiui, "é".
Galimas sprendimas - prieš sukuriant tikslinį failą, failo pavadinimą užrašyti utf8_decode()
.
Kreditai Fran Cano už tai, kad nustatė ir pasiūlė šios problemos sprendimą.
Pridėti prie (tikrai gero) esamo atsakymo
open_basedir
gali užkliūti, nes jį galima nurodyti žiniatinklio serverio konfigūracijoje. Nors tai lengva ištaisyti, jei naudojate savo dedikuotą serverį, yra kai kurių bendrųjų prieglobos programų paketų (pvz., Plesk, cPanel ir kt.), kurie konfigūruoja konfigūracijos direktyvą kiekvienam domenui. Kadangi programinė įranga sukuria konfigūracijos failą (t. y. httpd.conf
), negalite tiesiogiai keisti šio failo, nes prieglobos programinė įranga jį tiesiog perrašys, kai bus paleista iš naujo.
"Plesk" teikia vietą, kurioje galima pakeisti pateiktą httpd.conf
, vadinamą vhost.conf
. Šį failą gali įrašyti tik serverio administratorius. Apache konfigūracija atrodo maždaug taip
<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>
Tegul serverio administratorius susipažįsta su savo naudojamos prieglobos ir žiniatinklio serverio programinės įrangos vadovu.
Svarbu pažymėti, kad failo vykdymas per žiniatinklio serverį labai skiriasi nuo komandinės eilutės ar cron užduoties vykdymo. Didelis skirtumas yra tas, kad jūsų žiniatinklio serveris turi savo vartotoją ir leidimus. Saugumo sumetimais šis naudotojas yra gana ribotas. Pavyzdžiui, "Apache" dažnai yra apache
, www-data
arba httpd
(priklausomai nuo jūsų serverio). Cron darbas arba CLI vykdymas turi tokias teises, kokias turi jį paleidžiantis naudotojas (t. y. PHP scenarijus, paleistas kaip root, bus vykdomas su root teisėmis).
Dažnai žmonės išsprendžia leidimų problemą atlikdami šiuos veiksmus (Linux pavyzdys)
chmod 777 /path/to/file
Tai nėra protinga idėja, nes dabar į failą ar katalogą galima rašyti visame pasaulyje. Jei serveris priklauso jums ir esate vienintelis naudotojas, tai nėra tokia didelė problema, bet jei naudojatės bendra prieglobos aplinka, ką tik suteikėte prieigą visiems, esantiems jūsų serveryje.
Jums reikia nustatyti naudotoją (-us), kuriam (-iems) reikalinga prieiga, ir tik jam (jiems) suteikti prieigą. Kai žinosite, kuriems naudotojams reikia prieigos, norėsite įsitikinti, kad
Tas naudotojas yra failo ir galbūt pagrindinio katalogo savininkas (ypač pagrindinio katalogo, jei norite rašyti failus). Daugumoje bendrųjų prieglobos paslaugų aplinkų tai nebus problema, nes jūsų naudotojui turėtų priklausyti visi failai, esantys po jūsų šakniniu katalogu. Linux pavyzdys pateiktas toliau
chown apache:apache /path/to/file
Naudotojas ir tik tas naudotojas turi prieigą. Linux sistemoje gera praktika būtų chmod 600
(tik savininkas gali skaityti ir rašyti) arba chmod 644
(savininkas gali rašyti, bet visi gali skaityti).
Plačiau apie "Linux" / "Unix" leidimus ir naudotojus galite paskaityti čia
Kita galima priežastis: Pervadinimas ir (arba) failų perkėlimas dirbant su teksto redaktoriumi. Atlikau visus pirmiau nurodytus veiksmus, bet nesėkmingai, kol ištryniau failą, dėl kurio nuolat kildavo ši klaida, ir sukūriau naują, kuris išsprendė problemą.