PHP - nie można otworzyć strumienia: brak takiego pliku lub katalogu

166

W skryptach PHP, czy powołanie include(), require(), fopen()lub ich pochodne, takie jak include_once, require_oncelub nawet move_uploaded_file()jeden często prowadzi do wystąpienia błędu lub ostrzeżenia:

Nie udało się otworzyć strumienia: brak takiego pliku lub katalogu.

Jaki jest dobry proces szybkiego znajdowania pierwotnej przyczyny problemu?

Vic Seedoubleyew
źródło
5
Wyczyściłem komentarze nie na temat w tym poście. Proszę utrzymuj dyskusje meta w meta. Należy jednak pamiętać, że dyskusja na temat wykonalności pytań kanonicznych była prowadzona w kółko. Zobacz przykład tutaj .
Madara's Ghost
1
Mam ten sam problem, jedynym rozwiązaniem, które zawsze działało, jest: -1 Przejdź do pliku, aby dołączyć, prawy przycisk, właściwości, skopiuj pełną ścieżkę Na przykład: C: /......../ file.php 2 - dołącz to. Właściwie widziałem, że odpowiedź na to pytanie jest potwierdzona, ale dla mnie w niektórych przypadkach nie zadziałało, dopóki nie znajdę sposobu opisanego powyżej.
Rshad
@Rash dzięki za wkład. Niestety twoje rozwiązanie jest błędne, ponieważ będzie zawierało bezwzględną nazwę ścieżki, a to jest złe. Powodem, dla którego jest to nie tak, jest to, że w momencie, gdy skopiujesz projekt w inne miejsce lub przeniesiesz go do komputera, wszystko się zepsuje.
Vic Seedoubleyew

Odpowiedzi:

259

Istnieje wiele powodów, dla których ktoś może napotkać ten błąd, a zatem dobra lista kontrolna tego, co należy sprawdzić najpierw, znacznie pomaga.

Rozważmy, że rozwiązujemy problem z następującym wierszem:

require "/path/to/file"


Lista kontrolna


1. Sprawdź ścieżkę do pliku pod kątem literówek

  • albo sprawdź ręcznie (wizualnie sprawdzając ścieżkę)
  • lub przenieś cokolwiek jest wywoływane przez require*lub include*do jego własnej zmiennej, powtórz to, skopiuj i spróbuj uzyskać do niego dostęp z terminala:

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

    Następnie w terminalu:

    cat <file path pasted>


2. Sprawdź, czy ścieżka do pliku jest poprawna pod względem względów względnych i bezwzględnych

  • jeśli zaczyna się od ukośnika „/”, oznacza to, że nie odnosi się do katalogu głównego folderu serwisu WWW (katalogu głównego dokumentu), ale do katalogu głównego serwera.
    • na przykład katalogiem Twojej witryny może być /users/tony/htdocs
  • jeśli nie zaczyna się od ukośnika, to albo opiera się na ścieżce dołączania (patrz poniżej), albo ścieżka jest względna. Jeśli jest względna, PHP obliczy względną ścieżkę do bieżącego katalogu roboczego .
    • w związku z tym nie względem ścieżki do katalogu głównego Twojej witryny internetowej ani do pliku, w którym piszesz
    • z tego powodu zawsze używaj bezwzględnych ścieżek plików

Najlepsze praktyki :

Aby Twój skrypt był niezawodny na wypadek przenoszenia elementów, jednocześnie generując bezwzględną ścieżkę w czasie wykonywania, masz 2 opcje:

  1. używać require __DIR__ . "/relative/path/from/current/file". __DIR__Stała magia zwraca katalogu bieżącego pliku.
  2. sam zdefiniuj SITE_ROOTstałą:

    • w katalogu głównym swojej witryny internetowej utwórz plik, np config.php
    • w config.php, napisz

      define('SITE_ROOT', __DIR__);
    • w każdym pliku, do którego chcesz odwołać się do folderu głównego serwisu, dołącz config.php, a następnie użyj SITE_ROOTstałej, gdziekolwiek chcesz:

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

Te dwie praktyki sprawiają, że aplikacja jest bardziej przenośna, ponieważ nie opiera się na ustawieniach ini, takich jak ścieżka dołączania.


3. Sprawdź ścieżkę dołączania

Innym sposobem dołączania plików, ani względnym, ani czysto absolutnym, jest poleganie na ścieżce dołączania . Dzieje się tak często w przypadku bibliotek lub frameworków, takich jak framework Zend.

Takie włączenie będzie wyglądać następująco:

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

W takim przypadku będziesz chciał się upewnić, że folder, w którym znajduje się „Zend”, jest częścią ścieżki dołączania.

Możesz sprawdzić ścieżkę dołączania za pomocą:

echo get_include_path();

Możesz dodać do niego folder za pomocą:

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


4. Sprawdź, czy serwer ma dostęp do tego pliku

Może się tak zdarzyć, że użytkownik uruchamiający proces serwera (Apache lub PHP) po prostu nie ma uprawnień do odczytu lub zapisu w tym pliku.

Aby sprawdzić, pod jakim użytkownikiem działa serwer, możesz użyć posix_getpwuid :

$user = posix_getpwuid(posix_geteuid());

var_dump($user);

Aby sprawdzić uprawnienia do pliku, wpisz następujące polecenie w terminalu:

ls -l <path/to/file>

i spójrz na symboliczną notację pozwolenia


5. Sprawdź ustawienia PHP

Jeśli żadne z powyższych nie zadziałało, problem prawdopodobnie polega na tym, że niektóre ustawienia PHP zabraniają mu dostępu do tego pliku.

Istotne mogą być trzy ustawienia:

  1. open_basedir
    • Jeśli ta opcja jest ustawiona, PHP nie będzie mieć dostępu do żadnego pliku poza określonym katalogiem (nawet przez dowiązanie symboliczne).
    • Jednak domyślne zachowanie nie jest ustawiane, w którym to przypadku nie ma ograniczeń
    • Można to sprawdzić, dzwoniąc phpinfo()lub używającini_get("open_basedir")
    • Możesz zmienić to ustawienie, edytując plik php.ini lub plik httpd.conf
  2. tryb bezpieczeństwa
    • jeśli jest włączona, mogą obowiązywać ograniczenia. Jednak zostało to usunięte w PHP 5.4. Jeśli nadal korzystasz z wersji obsługującej tryb bezpieczny, uaktualnij do wersji PHP, która nadal jest obsługiwana .
  3. allow_url_fopen i allow_url_include
    • dotyczy to tylko dołączania lub otwierania plików za pośrednictwem procesu sieciowego, takiego jak http: //, nie dotyczy próby dołączenia plików do lokalnego systemu plików
    • można to sprawdzić ini_get("allow_url_include")i ustawić za pomocąini_set("allow_url_include", "1")


Skrzynie narożne

Jeśli żadne z powyższych nie umożliwiło zdiagnozowania problemu, oto kilka specjalnych sytuacji, które mogą się zdarzyć:


1. Włączenie biblioteki korzystającej ze ścieżki dołączania

Może się zdarzyć, że włączysz bibliotekę, na przykład framework Zend, używając ścieżki względnej lub bezwzględnej. Na przykład :

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

Ale nadal pojawia się ten sam rodzaj błędu.

Może się tak zdarzyć, ponieważ plik, który został (pomyślnie) dołączony, sam zawiera instrukcję include dla innego pliku, a ta druga instrukcja include zakłada, że ​​dodano ścieżkę tej biblioteki do ścieżki dołączania.

Na przykład wspomniany wcześniej plik frameworka Zend może zawierać następujące elementy:

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

co nie jest ani włączeniem przez ścieżkę względną, ani przez ścieżkę absolutną. Zakłada się, że katalog frameworka Zend został dodany do ścieżki włączania.

W takim przypadku jedynym praktycznym rozwiązaniem jest dodanie katalogu do ścieżki dołączania.


2. SELinux

Jeśli używasz systemu Linux z rozszerzonymi zabezpieczeniami, przyczyną problemu może być odmowa dostępu do pliku z serwera.

Aby sprawdzić, czy SELinux jest włączony w twoim systemie, uruchom sestatuspolecenie w terminalu. Jeśli polecenie nie istnieje, oznacza to, że SELinuksa nie ma w twoim systemie. Jeśli istnieje, powinien poinformować Cię, czy jest wymuszony, czy nie.

Aby sprawdzić, czy przyczyną problemu są polityki SELinux , możesz spróbować tymczasowo je wyłączyć. Należy jednak uważać, ponieważ spowoduje to całkowite wyłączenie ochrony. Nie rób tego na serwerze produkcyjnym.

setenforce 0

Jeśli nie masz już problemu z wyłączonym SELinuksem, to jest to główna przyczyna.

Aby go rozwiązać , będziesz musiał odpowiednio skonfigurować SELinux.

Niezbędne będą następujące typy kontekstów:

  • httpd_sys_content_t dla plików, które serwer ma mieć możliwość odczytu
  • httpd_sys_rw_content_t dla plików, dla których chcesz mieć dostęp do odczytu i zapisu
  • httpd_log_t dla plików dziennika
  • httpd_cache_t dla katalogu pamięci podręcznej

Na przykład, aby przypisać httpd_sys_content_ttyp kontekstu do katalogu głównego serwisu WWW, uruchom:

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

Jeśli twój plik znajduje się w katalogu domowym, musisz również włączyć wartość httpd_enable_homedirslogiczną:

setsebool -P httpd_enable_homedirs 1

W każdym razie może istnieć wiele powodów, dla których SELinux mógłby odmówić dostępu do pliku, w zależności od polityki użytkownika. Więc będziesz musiał się o to dowiedzieć. Oto samouczek dotyczący konfigurowania SELinux dla serwera WWW.


3. Symfony

Jeśli używasz Symfony i napotykasz ten błąd podczas przesyłania na serwer, może to być spowodowane tym, że pamięć podręczna aplikacji nie została zresetowana, ponieważ app/cachezostała załadowana lub pamięć podręczna nie została wyczyszczona.

Możesz to przetestować i naprawić, uruchamiając następujące polecenie konsoli:

cache:clear


4. Znaki inne niż ACSII w pliku ZIP

Najwyraźniej ten błąd może wystąpić również podczas wywoływania, zip->close()gdy niektóre pliki w pliku zip mają w nazwie znaki inne niż ASCII, takie jak „é”.

Potencjalnym rozwiązaniem jest zawinięcie nazwy pliku utf8_decode()przed utworzeniem pliku docelowego.

Podziękowania dla Fran Cano za zidentyfikowanie i zasugerowanie rozwiązania tego problemu

Vic Seedoubleyew
źródło
4
Myślę, że wspomnienie o tym selinuxmoże być dobrym pomysłem. będziesz potrzebować przynajmniej httpd_sys_content_tuprawnień (tylko do odczytu katalogów i plików używanych przez Apache) dla dołączonych plików.
bansi
Wielkie dzięki za sugestię. Ponieważ nie znam SELinux, poczytałem trochę i próbowałem odpowiedzieć na ten przypadek. Jeśli nie są poprawne, prosimy o przesłanie opinii lub zasugerowanie pewnych zmian. Jeszcze raz dziękuję za komentarz!
Vic Seedoubleyew
chconjest tymczasowy i nie przetrwa restoreconani ponownego uruchomienia. może być konieczne użycie semanagedo zmiany kontekstu pliku. Oto dobry, prosty poradnik dla strony internetowej
bansi
Inną możliwością, aby dodać: buforowanie realpath: lyte.id.au/2014/05/01/what-the-hell-php
chrishiestand
@chrishiestand wielkie dzięki! Ten artykuł jest naprawdę interesujący! Czy pamiętasz, jaki był bieg wydarzeń, które doprowadziły do ​​tego błędu? Czy to było tak, że początkowo użytkownik nie miał dostępu do odczytu do pliku, potem został zmieniony, ale pamięć podręczna nadal uważała, że ​​nie można go odczytać, więc wyświetlał ten błąd po otwarciu pliku?
Vic Seedoubleyew,
16

Aby dodać do (naprawdę dobrej) istniejącej odpowiedzi

Współdzielone oprogramowanie hostingowe

open_basedirto taki, który może cię zranić, ponieważ można go określić w konfiguracji serwera WWW. Chociaż można to łatwo naprawić, jeśli uruchomisz własny serwer dedykowany, istnieją pewne pakiety oprogramowania do hostingu współdzielonego (takie jak Plesk, cPanel itp.), Które skonfigurują dyrektywę konfiguracyjną dla każdej domeny. Ponieważ oprogramowanie tworzy plik konfiguracyjny (tj. httpd.conf), Nie można bezpośrednio zmienić tego pliku, ponieważ oprogramowanie hostingowe po prostu nadpisze go po ponownym uruchomieniu.

Plesk zapewnia miejsce do zastąpienia podanego httpd.confwywołania vhost.conf. Tylko administrator serwera może zapisać ten plik. Konfiguracja Apache wygląda mniej więcej tak

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

Poproś administratora serwera o zapoznanie się z instrukcją obsługi hostingu i oprogramowania serwera WWW, z którego korzysta.

Uprawnienia do plików

Ważne jest, aby pamiętać, że wykonanie pliku za pośrednictwem serwera WWW bardzo różni się od wykonania zadania z wiersza poleceń lub cron. Największą różnicą jest to, że serwer WWW ma własnego użytkownika i uprawnienia. Ze względów bezpieczeństwa ten użytkownik jest dość ograniczony. Apache, na przykład, jest często apache, www-datalub httpd(w zależności od serwera). Zadanie cron lub wykonanie CLI ma wszelkie uprawnienia, jakie ma uruchamiający je użytkownik (tj. Uruchomienie skryptu PHP jako root będzie wykonywane z uprawnieniami roota).

Często ludzie rozwiązują problem z uprawnieniami, wykonując następujące czynności (przykład z Linuksem)

chmod 777 /path/to/file

Nie jest to mądry pomysł, ponieważ plik lub katalog jest teraz dostępny dla wszystkich. Jeśli jesteś właścicielem serwera i jesteś jedynym użytkownikiem, nie jest to taka wielka sprawa, ale jeśli korzystasz ze współdzielonego środowiska hostingowego, właśnie dałeś wszystkim dostęp do serwera.

Musisz tylko określić użytkowników, którzy potrzebują dostępu, i dać dostęp tylko tym, którym są one potrzebne. Gdy już wiesz, którzy użytkownicy potrzebują dostępu, będziesz chciał się upewnić

  1. Ten użytkownik jest właścicielem pliku i prawdopodobnie katalogu nadrzędnego (zwłaszcza katalogu nadrzędnego, jeśli chcesz zapisywać pliki). W większości współdzielonych środowisk hostingowych nie będzie to problemem, ponieważ użytkownik powinien być właścicielem wszystkich plików znajdujących się w katalogu głównym. Przykład Linuksa pokazano poniżej

     chown apache:apache /path/to/file
  2. Użytkownik i tylko ten użytkownik ma dostęp. W Linuksie dobrą praktyką byłoby chmod 600(tylko właściciel może czytać i pisać) lub chmod 644(właściciel może pisać, ale każdy może czytać)

Możesz przeczytać bardziej rozszerzone omówienie uprawnień i użytkowników Linuksa / Uniksa tutaj

Machavity
źródło
7
  1. Spójrz na dokładny błąd

Mój kod działał dobrze na wszystkich maszynach, ale tylko na tym zaczął powodować problem (który, jak sądzę, działał). Użyłem ścieżki echo „document_root” do debugowania, a także przyjrzałem się uważnie błędowi, znalazłem to

Ostrzeżenie: include ( D: /MyProjects/testproject//functions/connections.php ): nie udało się otworzyć strumienia:

Możesz łatwo zobaczyć, gdzie są problemy. Problemy występują // przed funkcjami

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

Po prostu usuń ładunek / z dołączenia i powinno działać dobrze. Co ciekawe, zachowanie to jest różne w różnych wersjach. Uruchomiłem ten sam kod na laptopie, Macbooku Pro i tym komputerze PC, wszystko działało dobrze do tej pory. Mam nadzieję, że to komuś pomoże.

  1. Skopiuj lokalizację pliku w przeglądarce, aby upewnić się, że plik istnieje. Czasami pliki są usuwane nieoczekiwanie (zdarzyło się to ze mną) i był to również problem w moim przypadku.
Hammad Khan
źródło
Czym się to różni od kroku 1 z poniższej listy kontrolnej?
Vic Seedoubleyew,
Krok 2 dodatkowa kontrola, niezwiązana z krokiem 1. Po prostu przejdź do proponowanej ścieżki w przeglądarce i zobacz, czy widzisz tam plik (nie w Eksploratorze Windows, ale w przeglądarce).
Hammad Khan,
2

Dodaj skrypt z parametrami zapytania

To był mój przypadek. W rzeczywistości prowadzi do pytania nr 4485874 , ale wkrótce to wyjaśnię.
Kiedy próbujesz wymagać path/to/script.php?parameter=value, PHP szuka pliku o nazwie script.php?parameter=value, ponieważ UNIX pozwala na takie ścieżki.
Jeśli naprawdę potrzebujesz, aby przekazać niektóre dane zawarte skryptu, po prostu zadeklarować ją jako $variable=...lub $GLOBALS[]=...czy inny sposób chcesz.

Paul Lynn
źródło
2

Akcje Samby

Jeśli masz serwer testowy Linux i pracujesz z klienta Windows, udział Samba koliduje z poleceniem chmod . Tak więc, nawet jeśli używasz:

chmod -R 777 myfolder

po stronie Linuksa jest w pełni możliwe, że Unix Group \ www-data nadal nie ma prawa do zapisu. Jedno działające rozwiązanie, jeśli Twój udział jest skonfigurowany tak, że administratorzy systemu Windows są zmapowani do katalogu głównego: W systemie Windows otwórz uprawnienia, wyłącz dziedziczenie dla swojego folderu z kopiowaniem, a następnie udziel pełnego dostępu do danych www.

Stephan Brunker
źródło
1

Inna możliwa przyczyna: zmiana nazwy i / lub przenoszenie plików w edytorze tekstu. Wykonałem wszystkie powyższe kroki bez powodzenia, dopóki nie usunąłem pliku, który ciągle zgłaszał ten błąd i utworzyłem nowy, który naprawił problem.

zMeadz
źródło
1
jeśli dobrze rozumiem, co masz na myśli, byłby to problem natychmiastowy po pierwszym sprawdzeniu na liście
Vic Seedoubleyew
# 1 nie jest jednoznaczny na temat potencjalnych przyczyn
zMeadz
tak, ale nie dbasz o przyczyny, zależy ci na znalezieniu sposobu na rozwiązanie problemu. Co ważniejsze, krok numer 1 rozwiązałby twój problem
Vic Seedoubleyew