PHP - не вдалося відкрити потік: такого файлу чи каталогу немає


166

У PHP скрипти, називаючи чи include(), require(), fopen()або їх похідні , такі як include_once, require_onceабо навіть move_uploaded_file(), часто впадає в помилки або попередження:

Не вдалося відкрити потік: Немає такого файлу чи каталогу.

Який хороший процес швидкого пошуку першопричини проблеми?


5
Я прибрав поза тематичні коментарі до цієї публікації. Прошу тримати мета-дискусії в мета. Однак зауважте, що обговорення канонічних питань життєздатності проводилось знову і знову. Дивіться приклад тут .
Привид

1
У мене така ж проблема, єдине рішення, яке завжди працювало, це: -1 Перейти до файлу, щоб включити, правильний боттон, властивості, скопіювати повний шлях. Наприклад: C: /......../ file.php 2- включіть його. Насправді я бачив, що на це питання відповідають, і відповідь підтверджена, але для мене в деяких випадках не вийшло, поки я не знайду описаний вище спосіб.
Ршад

Дякую @Rash за внесок. На жаль, ваше рішення неправильне, оскільки воно буде згадувати абсолютну назву шляху, і це неправильно. Причина, чому це неправильно, полягає в тому, що тоді, коли ви скопіюєте проект десь в іншому місці або пересунете його всередині комп'ютера, все зламається.
Вік Седублєв

Відповіді:


259

Є багато причин, чому можна зіткнутися з цією помилкою, і тому хороший перелік того, що перевірити спочатку, значно допомагає.

Розглянемо, що ми усуваємо проблеми з наступного рядка:

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 практики також роблять вашу програму більш портативною, оскільки вона не покладається на такі настройки, як шлях включення.


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. enable_url_fopen і enable_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 Framework.

У такому випадку єдиним практичним рішенням є додати каталог до шляху включення.


2. SELinux

Якщо ви працюєте з Linux-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 і відчуваєте цю помилку під час завантаження на сервер, можливо, кеш програми не було скинуто, тому app/cacheщо він завантажений, або кеш не очищений.

Ви можете перевірити та виправити це, виконавши таку команду консолі:

cache:clear


4. Не символи ACSII всередині Zip-файлу

Мабуть, ця помилка може статися і під час виклику, zip->close()коли деякі файли всередині zip мають у своєму імені файли не ASCII символи, наприклад, "é".

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

Подяка Фран Кано за виявлення та пропозицію вирішення цього питання


4
Я думаю, згадка про це selinuxможе бути хорошою ідеєю тут. Вам буде потрібно як мінімум необхідний httpd_sys_content_tдозвіл (лише каталоги та файли, що використовуються Apache) на включені файли.
bansi

Дякую за пропозицію. Оскільки я не знайомий із SELinux, я трохи прочитав і спробував відповісти на цю справу. Будь ласка, не соромтеся надіслати відгук або запропонуйте деякі зміни, якщо вони невірні. Ще раз дякую за коментар!
Вік Седублеєв

chconє тимчасовим і не переживе restoreconані перезавантаження. можливо, вам доведеться використовувати semanageдля зміни контексту файлу. Ось хороший простий підручник для веб-сайту
bansi

Ще одна можливість додати: кешування realpath
chrishiestand

@chrishiestand велике спасибі! Ця стаття справді цікава! Ви пам’ятаєте, який хід подій призвів до цієї помилки? Це було те, що спочатку користувач не читав доступ до файлу, потім його змінили, але кеш все-таки вважав, що він не читабельний, тому він викинув цю помилку при відкритті файлу?
Вік Седублеєв

16

Щоб додати (дійсно гарну) існуючу відповідь

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

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

Це не розумна ідея, оскільки файл або каталог зараз доступні для запису у всьому світі. Якщо ви є власником сервера і ви є єдиним користувачем, це не така вже й велика справа, але якщо ви перебуваєте у спільному хостинговому середовищі, ви просто надали всім доступ до свого сервера.

Що вам потрібно зробити, це визначити користувачів (-ів), які потребують доступу, і надати доступ лише тим, хто їм. Коли ви дізнаєтесь, яким користувачам потрібен доступ, вам захочеться переконатися в цьому

  1. Цей користувач володіє файлом і, можливо, батьківським каталогом (особливо батьківський каталог, якщо ви хочете писати файли). У більшості спільних середовищ хостингу це не буде проблемою, оскільки ваш користувач повинен володіти всіма файлами під вашим коренем. Приклад Linux показаний нижче

     chown apache:apache /path/to/file
  2. Користувач, і лише той користувач, має доступ. У Linux гарною практикою буде chmod 600(лише власник може читати і писати) або chmod 644(власник може писати, але кожен може читати)

Ви можете прочитати більш розширене обговорення дозволів Linux та Unix тут


7
  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');

Тому просто вийміть навантаження / з включення, і воно повинно працювати нормально. Цікаво, що така поведінка відрізняється в різних версіях. Я запускаю той самий код на ноутбуках, Macbook Pro і на цьому ПК, і все працювало чудово досі. Сподіваюся, що це комусь допоможе.

  1. Скопіюйте повне розташування файлу у браузері, щоб переконатися, що файл існує. Іноді файли видаляються несподівано (траплялося зі мною), і це також було проблемою в моєму випадку.

Чим це відрізняється від кроку 1 контрольного списку нижче?
Вік Седублеєв,

Крок 2 - додаткова перевірка, не пов’язана з кроком 1. Просто перейдіть до запропонованого шляху в браузері і перевірте, чи бачите ви там файл (не в Windows Explorer, а в браузері).
Хаммад Хан

2

Додати сценарій з параметрами запиту

Це був мій випадок. Він насправді посилається на питання # 4485874 , але я скоро його поясню.
Коли ви намагаєтеся вимагати path/to/script.php?parameter=value, PHP шукає файл з іменем script.php?parameter=value, тому що UNIX дозволяє вам мати такі шляхи.
Якщо ви дійсно повинні передати деякі дані включені сценарії, просто оголосити його як $variable=...або $GLOBALS[]=...чи іншим способом , вам подобається.


2

Самба Акції

Якщо у вас є тестовий сервер Linux і ви працюєте з клієнтом Windows, спільний доступ Samba втручається в команду chmod . Отже, навіть якщо ви використовуєте:

chmod -R 777 myfolder

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


1

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


1
якщо я розумію, що ви маєте на увазі правильно, це було б негайно
усунене проблемою

№1 не є явним щодо потенційних причин
zMeadz

так, але вас не цікавлять причини, ви дбаєте про те, щоб знайти спосіб усунення проблеми. Що ще важливіше, крок №1 вирішив би вашу проблему
Вік Седоблеєв,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.