PHP - Không thể mở luồng: Không có tệp hoặc thư mục như vậy


166

Trong kịch bản PHP, cho dù gọi include(), require(), fopen(), hoặc các dẫn xuất của họ chẳng hạn như include_once, require_oncehoặc thậm chí, move_uploaded_file(), người ta thường chạy vào một lỗi hoặc cảnh báo:

Không thể mở luồng: Không có tệp hoặc thư mục như vậy.

Một quá trình tốt để nhanh chóng tìm ra nguyên nhân gốc rễ của vấn đề là gì?


5
Tôi đã làm sạch các ý kiến ​​ngoài chủ đề về bài viết này. Hãy tiếp tục thảo luận về meta trong meta. Tuy nhiên, xin lưu ý rằng các cuộc thảo luận về các câu hỏi chính tắc có thể được thực hiện lặp đi lặp lại. Xem ví dụ ở đây .
Madara's Ghost

1
Tôi gặp vấn đề tương tự, giải pháp duy nhất luôn hoạt động là: -1 Chuyển đến tệp để bao gồm, bên phải, thuộc tính, sao chép đường dẫn đầy đủ Ví dụ: C: /......../ file.php 2- bao gồm nó. Trên thực tế tôi thấy rằng câu hỏi này đã được trả lời, và câu trả lời được xác thực, nhưng đối với tôi trong một số trường hợp không hoạt động, cho đến khi tôi tìm thấy cách mô tả ở trên.
Rshad

@Rash cảm ơn vì đã đóng góp. Thật không may, giải pháp của bạn là sai, bởi vì nó sẽ đề cập đến tên đường dẫn tuyệt đối, và đó là sai. Lý do tại sao điều này là sai, là vì sau đó khi bạn sao chép dự án của bạn ở một nơi khác hoặc di chuyển nó vào bên trong máy tính của bạn, mọi thứ sẽ bị phá vỡ.
Vic Seedoubleyew

Câu trả lời:


259

Có nhiều lý do tại sao một người có thể gặp phải lỗi này và do đó, một danh sách kiểm tra tốt về những gì cần kiểm tra trước giúp ích đáng kể.

Hãy xem xét rằng chúng tôi đang khắc phục sự cố dòng sau:

require "/path/to/file"


Danh mục


1. Kiểm tra đường dẫn tệp cho lỗi chính tả

  • hoặc kiểm tra thủ công (bằng cách kiểm tra trực quan đường dẫn)
  • hoặc di chuyển bất cứ thứ gì được gọi bởi require*hoặc include*đến biến của chính nó, lặp lại nó, sao chép nó và thử truy cập nó từ một thiết bị đầu cuối:

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

    Sau đó, trong một thiết bị đầu cuối:

    cat <file path pasted>


2. Kiểm tra xem đường dẫn tệp có chính xác về các cân nhắc đường dẫn tuyệt đối so với đường dẫn tuyệt đối không

  • nếu nó bắt đầu bằng dấu gạch chéo "/" thì nó không đề cập đến thư mục gốc của thư mục trang web của bạn (gốc tài liệu), mà là gốc của máy chủ của bạn.
    • ví dụ: thư mục trang web của bạn có thể là /users/tony/htdocs
  • nếu nó không bắt đầu bằng dấu gạch chéo về phía trước thì nó sẽ dựa vào đường dẫn bao gồm (xem bên dưới) hoặc đường dẫn là tương đối. Nếu nó là tương đối, thì PHP sẽ tính toán tương đối với đường dẫn của thư mục làm việc hiện tại .
    • do đó, không liên quan đến đường dẫn gốc của trang web của bạn hoặc tệp bạn đang nhập
    • Vì lý do đó, luôn luôn sử dụng đường dẫn tệp tuyệt đối

Thực hành tốt nhất :

Để làm cho tập lệnh của bạn mạnh mẽ trong trường hợp bạn di chuyển mọi thứ xung quanh, trong khi vẫn tạo một đường dẫn tuyệt đối khi chạy, bạn có 2 tùy chọn:

  1. sử dụng require __DIR__ . "/relative/path/from/current/file". Các __DIR__hằng số ma thuật trả về thư mục của tập tin hiện hành.
  2. tự xác định một SITE_ROOThằng số:

    • ở thư mục gốc của trang web của bạn, tạo một tệp, ví dụ: config.php
    • trong config.php, viết

      define('SITE_ROOT', __DIR__);
    • trong mọi tệp mà bạn muốn tham chiếu thư mục gốc của trang, bao gồm config.php, và sau đó sử dụng SITE_ROOThằng bất cứ nơi nào bạn muốn:

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

Hai thực tiễn này cũng làm cho ứng dụng của bạn dễ di chuyển hơn vì nó không phụ thuộc vào cài đặt ini như đường dẫn bao gồm.


3. Kiểm tra đường dẫn bao gồm của bạn

Một cách khác để bao gồm các tệp, không tương đối hay hoàn toàn tuyệt đối, là dựa vào đường dẫn bao gồm . Đây thường là trường hợp cho các thư viện hoặc khung như khung Zend.

Bao gồm như vậy sẽ trông như thế này:

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

Trong trường hợp đó, bạn sẽ muốn đảm bảo rằng thư mục chứa "Zend" là một phần của đường dẫn bao gồm.

Bạn có thể kiểm tra đường dẫn bao gồm:

echo get_include_path();

Bạn có thể thêm một thư mục vào nó với:

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


4. Kiểm tra xem máy chủ của bạn có quyền truy cập vào tập tin đó không

Có thể là tất cả cùng nhau, người dùng đang chạy quy trình máy chủ (Apache hoặc PHP) đơn giản là không có quyền đọc hoặc ghi vào tệp đó.

Để kiểm tra xem người dùng đang chạy máy chủ nào, bạn có thể sử dụng posix_getpwuid :

$user = posix_getpwuid(posix_geteuid());

var_dump($user);

Để tìm ra các quyền trên tệp, nhập lệnh sau trong thiết bị đầu cuối:

ls -l <path/to/file>

và nhìn vào ký hiệu tượng trưng cho phép


5. Kiểm tra cài đặt PHP

Nếu không có cách nào ở trên hoạt động, thì vấn đề có lẽ là một số cài đặt PHP cấm nó truy cập vào tệp đó.

Ba cài đặt có thể có liên quan:

  1. open_basingir
    • Nếu điều này được đặt, PHP sẽ không thể truy cập bất kỳ tệp nào bên ngoài thư mục được chỉ định (thậm chí không thông qua một liên kết tượng trưng).
    • Tuy nhiên, hành vi mặc định là không được đặt trong trường hợp không có hạn chế
    • Điều này có thể được kiểm tra bằng cách gọi phpinfo()hoặc bằng cách sử dụngini_get("open_basedir")
    • Bạn có thể thay đổi cài đặt bằng cách chỉnh sửa tệp php.ini hoặc tệp httpd.conf của bạn
  2. chế độ an toàn
    • nếu điều này được bật các hạn chế có thể áp dụng. Tuy nhiên, điều này đã bị xóa trong PHP 5.4. Nếu bạn vẫn đang ở phiên bản hỗ trợ nâng cấp chế độ an toàn lên phiên bản PHP vẫn đang được hỗ trợ .
  3. allow_url_fopen và allow_url_include
    • điều này chỉ áp dụng cho việc bao gồm hoặc mở tệp thông qua quy trình mạng, chẳng hạn như http: // chứ không phải khi cố gắng bao gồm các tệp trên hệ thống tệp cục bộ
    • điều này có thể được kiểm tra ini_get("allow_url_include")và thiết lập vớiini_set("allow_url_include", "1")


Trường hợp góc

Nếu không có cách nào ở trên được kích hoạt để chẩn đoán sự cố, đây là một số tình huống đặc biệt có thể xảy ra:


1. Bao gồm thư viện dựa trên đường dẫn bao gồm

Có thể xảy ra việc bạn bao gồm một thư viện, ví dụ, khung Zend, sử dụng đường dẫn tương đối hoặc tuyệt đối. Ví dụ :

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

Nhưng sau đó bạn vẫn nhận được cùng một loại lỗi.

Điều này có thể xảy ra bởi vì tệp mà bạn có (thành công) bao gồm, có một câu lệnh bao gồm cho một tệp khác và câu lệnh bao gồm thứ hai đó giả định rằng bạn đã thêm đường dẫn của thư viện đó vào đường dẫn bao gồm.

Ví dụ: tệp khung Zend được đề cập trước đây có thể bao gồm:

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

mà không phải là bao gồm bởi đường dẫn tương đối, cũng không phải là đường dẫn tuyệt đối. Giả định rằng thư mục khung Zend đã được thêm vào đường dẫn bao gồm.

Trong trường hợp như vậy, giải pháp thực tế duy nhất là thêm thư mục vào đường dẫn bao gồm của bạn.


2. TỰ TIN

Nếu bạn đang chạy Linux được tăng cường bảo mật, thì đó có thể là lý do của sự cố, bằng cách từ chối quyền truy cập vào tệp từ máy chủ.

Để kiểm tra xem Selinux có được bật trên hệ thống của bạn không, hãy chạy sestatuslệnh trong thiết bị đầu cuối. Nếu lệnh không tồn tại, thì SELinux không có trên hệ thống của bạn. Nếu nó tồn tại, thì nó sẽ cho bạn biết liệu nó có được thi hành hay không.

Để kiểm tra xem các chính sách của Selinux có phải là lý do của sự cố hay không, bạn có thể thử tắt nó tạm thời. Tuy nhiên, CẨN THẬN, vì điều này sẽ vô hiệu hóa bảo vệ hoàn toàn. Đừng làm điều này trên máy chủ sản xuất của bạn.

setenforce 0

Nếu bạn không còn gặp sự cố với SELinux nữa, thì đây là nguyên nhân gốc rễ.

Để giải quyết nó , bạn sẽ phải cấu hình SELinux tương ứng.

Các loại bối cảnh sau đây sẽ là cần thiết:

  • httpd_sys_content_t cho các tệp mà bạn muốn máy chủ của bạn có thể đọc được
  • httpd_sys_rw_content_t cho các tệp mà bạn muốn truy cập đọc và ghi
  • httpd_log_t cho các tệp nhật ký
  • httpd_cache_t cho thư mục bộ đệm

Ví dụ: để gán httpd_sys_content_tloại ngữ cảnh cho thư mục gốc trang web của bạn, hãy chạy:

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

Nếu tệp của bạn nằm trong thư mục chính, bạn cũng sẽ cần bật httpd_enable_homedirsboolean:

setsebool -P httpd_enable_homedirs 1

Trong mọi trường hợp, có thể có nhiều lý do tại sao SELinux sẽ từ chối quyền truy cập vào một tệp, tùy thuộc vào chính sách của bạn. Vì vậy, bạn sẽ cần phải hỏi về điều đó. Dưới đây là hướng dẫn cụ thể về cách định cấu hình SELinux cho máy chủ web.


3. Symfony

Nếu bạn đang sử dụng Symfony và gặp phải lỗi này khi tải lên máy chủ thì có thể bộ nhớ cache của ứng dụng chưa được đặt lại, vì app/cacheđã được tải lên hoặc bộ đệm đó chưa bị xóa.

Bạn có thể kiểm tra và sửa lỗi này bằng cách chạy lệnh console sau:

cache:clear


4. Các ký tự không ACSII trong tệp Zip

Rõ ràng, lỗi này cũng có thể xảy ra khi gọi zip->close()khi một số tệp trong zip có các ký tự không phải ASCII trong tên tệp của chúng, chẳng hạn như "é".

Một giải pháp tiềm năng là bọc tên tệp utf8_decode()trước khi tạo tệp đích.

Tín dụng cho Fran Cano để xác định và đề xuất giải pháp cho vấn đề này


4
Tôi nghĩ đề cập đến selinuxcó thể là một ý tưởng tốt ở đây. bạn sẽ cần ít nhấthttpd_sys_content_t (các thư mục và tệp chỉ đọc được sử dụng bởi Apache) trên các tệp được bao gồm.
bansi

Cảm ơn rất nhiều cho lời đề nghị. Vì tôi không quen thuộc với SELinux, tôi đã đọc và cố gắng trả lời trường hợp này. Xin vui lòng phản hồi hoặc đề xuất một số chỉnh sửa nếu nó không chính xác. Cảm ơn một lần nữa cho nhận xét !
Vic Seedoubleyew

chconlà tạm thời và sẽ không tồn tại một restoreconhoặc khởi động lại. bạn có thể cần sử dụng semanageđể thay đổi ngữ cảnh của tệp. Đây là một hướng dẫn đơn giản tốt cho trang web
bansi

Một khả năng khác để thêm: bộ nhớ đệm realpath: lyte.id.au/2014/05/01/what-the-hell-php
chrishiestand

@chrishiestand cảm ơn rất nhiều! Bài viết đó thực sự thú vị! Bạn có nhớ quá trình các sự kiện dẫn đến lỗi đó là gì không? Có phải người dùng ban đầu không đọc quyền truy cập vào một tệp, sau đó nó đã bị thay đổi, nhưng bộ đệm vẫn cho rằng nó không thể đọc được, vì vậy nó đã ném lỗi đó khi mở tệp?
Vic Seedoubleyew

16

Để thêm vào câu trả lời (thực sự tốt)

Phần mềm chia sẻ

open_basedirlà một thứ có thể làm bạn bối rối vì nó có thể được chỉ định trong cấu hình máy chủ web. Mặc dù điều này dễ dàng được khắc phục nếu bạn chạy máy chủ chuyên dụng của riêng mình, có một số gói phần mềm lưu trữ được chia sẻ ngoài đó (như Plesk, cPanel, v.v.) sẽ định cấu hình chỉ thị cấu hình trên cơ sở mỗi tên miền. Vì phần mềm xây dựng tệp cấu hình (tức là httpd.conf), bạn không thể thay đổi tệp đó trực tiếp vì phần mềm lưu trữ sẽ chỉ ghi đè lên khi khởi động lại.

Với Plesk, họ cung cấp một nơi để ghi đè lên được httpd.confgọi vhost.conf. Chỉ có quản trị viên máy chủ có thể viết tập tin này. Cấu hình cho Apache trông giống như thế này

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

Yêu cầu quản trị viên máy chủ của bạn tham khảo hướng dẫn sử dụng cho phần mềm lưu trữ và máy chủ web mà họ sử dụng.

Quyền tập tin

Điều quan trọng cần lưu ý là việc thực thi một tệp thông qua máy chủ web của bạn rất khác với thực thi công việc dòng lệnh hoặc cron. Sự khác biệt lớn là máy chủ web của bạn có người dùng và quyền riêng. Vì lý do bảo mật mà người dùng khá hạn chế. Apache, ví dụ, thường apache, www-datahoặc httpd(tùy thuộc vào máy chủ của bạn). Một công việc cron hoặc thực thi CLI có bất kỳ quyền nào mà người dùng đang chạy nó (tức là chạy tập lệnh PHP với quyền root sẽ thực thi với quyền của root).

Rất nhiều lần mọi người sẽ giải quyết vấn đề về quyền bằng cách thực hiện như sau (ví dụ về Linux)

chmod 777 /path/to/file

Đây không phải là một ý tưởng thông minh, vì tập tin hoặc thư mục hiện có thể ghi được trên thế giới. Nếu bạn sở hữu máy chủ và là người dùng duy nhất thì đây không phải là vấn đề lớn, nhưng nếu bạn đang ở trong một môi trường lưu trữ được chia sẻ, bạn vừa cấp cho mọi người quyền truy cập máy chủ của mình.

Những gì bạn cần làm là xác định (các) người dùng cần truy cập và chỉ cung cấp cho những người họ truy cập. Khi bạn biết người dùng nào cần truy cập, bạn sẽ muốn đảm bảo rằng

  1. Người dùng đó sở hữu tệp và có thể là thư mục mẹ (đặc biệt là thư mục mẹ nếu bạn muốn ghi tệp). Trong hầu hết các môi trường lưu trữ được chia sẻ, điều này sẽ không thành vấn đề, bởi vì người dùng của bạn nên sở hữu tất cả các tệp bên dưới thư mục gốc của bạn. Một ví dụ Linux được hiển thị dưới đây

     chown apache:apache /path/to/file
  2. Người dùng, và chỉ người dùng đó, có quyền truy cập. Trong Linux, một thông lệ tốt sẽ là chmod 600(chỉ chủ sở hữu có thể đọc và viết) hoặc chmod 644(chủ sở hữu có thể viết nhưng mọi người đều có thể đọc)

Bạn có thể đọc một cuộc thảo luận mở rộng hơn về quyền và người dùng Linux / Unix tại đây


7
  1. Nhìn vào lỗi chính xác

Mã của tôi hoạt động tốt trên tất cả các máy nhưng chỉ có cái này mới bắt đầu có vấn đề (thường dùng để tìm tôi đoán). Đã sử dụng đường dẫn echo "document_root" để gỡ lỗi và cũng xem xét kỹ lỗi, tìm thấy

Cảnh báo: bao gồm ( D: /MyProjects/testproject//fiances/connections.php ): không thể mở luồng:

Bạn có thể dễ dàng nhìn thấy vấn đề ở đâu. Các vấn đề là // trước các chức năng

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

Vì vậy, chỉ cần loại bỏ vận đơn / từ bao gồm và nó sẽ hoạt động tốt. Điều thú vị là hành vi này là khác nhau trên các phiên bản khác nhau. Tôi chạy cùng một mã trên Laptop, Macbook Pro và PC này, tất cả đều hoạt động tốt cho đến khi. Hy vọng điều này sẽ giúp được ai đó.

  1. Sao chép qua vị trí tệp trong trình duyệt để đảm bảo tệp tồn tại. Đôi khi các tập tin bị xóa bất ngờ (xảy ra với tôi) và đó cũng là vấn đề trong trường hợp của tôi.

Làm thế nào khác với bước 1 của danh sách kiểm tra dưới đây?
Vic Seedoubleyew

Bước 2 kiểm tra bổ sung, không liên quan đến bước 1. Chỉ cần điều hướng đến đường dẫn được đề xuất trong trình duyệt và xem bạn có thấy tệp ở đó không (không có trong windows explorer nhưng trong trình duyệt).
Hammad Khan

2

Thêm tập lệnh với các tham số truy vấn

Đó là trường hợp của tôi. Nó thực sự liên kết đến câu hỏi # 4485874 , nhưng tôi sẽ giải thích nó ngay sau đây.
Khi bạn cố gắng yêu cầu path/to/script.php?parameter=value, PHP tìm kiếm tệp có tên script.php?parameter=value, vì UNIX cho phép bạn có các đường dẫn như thế này.
Nếu bạn thực sự cần chuyển một số dữ liệu cho tập lệnh được bao gồm, chỉ cần khai báo nó theo $variable=...hoặc $GLOBALS[]=...theo cách khác mà bạn muốn.


2

Cổ phiếu Samba

Nếu bạn có máy chủ kiểm tra Linux và bạn làm việc từ Máy khách Windows, chia sẻ Samba sẽ can thiệp vào lệnh chmod . Vì vậy, ngay cả khi bạn sử dụng:

chmod -R 777 myfolder

Về phía Linux, hoàn toàn có khả năng Nhóm dữ liệu Unix vẫn không có quyền truy cập ghi. Một giải pháp hoạt động nếu chia sẻ của bạn được thiết lập rằng quản trị viên Windows được ánh xạ tới root: Từ Windows, mở Quyền, vô hiệu hóa Kế thừa cho thư mục của bạn bằng bản sao và sau đó cấp quyền truy cập đầy đủ cho dữ liệu www.


1

Một nguyên nhân có thể khác: Đổi tên và / hoặc di chuyển tệp trong khi đang trong trình soạn thảo văn bản. Tôi đã thực hiện tất cả các bước trên mà không thành công cho đến khi tôi xóa tệp tiếp tục ném lỗi này và tạo một lỗi mới, khắc phục sự cố.


1
nếu tôi hiểu ý của bạn là chính xác, thì điều này sẽ được xử lý ngay lập tức bởi lần kiểm tra đầu tiên trong danh sách
Vic Seedoubleyew

# 1 không rõ ràng về nguyên nhân tiềm năng
zMeadz

có, nhưng bạn không quan tâm đến nguyên nhân, bạn quan tâm đến việc tìm cách khắc phục vấn đề. Quan trọng hơn, bước số 1 sẽ giải quyết vấn đề của bạn
Vic Seedoubleyew
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.