มีสาเหตุหลายประการที่อาจเกิดข้อผิดพลาดนี้และทำให้รายการตรวจสอบที่ดีของสิ่งที่จะตรวจสอบก่อนช่วยอย่างมาก
ลองพิจารณาว่าเรากำลังแก้ไขปัญหาบรรทัดต่อไปนี้:
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 ตัวเลือก:
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";
แนวทางปฏิบัติ 2 ข้อเหล่านี้ยังทำให้แอปพลิเคชันของคุณพกพาได้มากขึ้นเนื่องจากไม่ได้พึ่งพาการตั้งค่า ini เช่นพา ธ รวม
3. ตรวจสอบเส้นทางของคุณ
วิธีการรวมไฟล์อีกค่าค่อนข้างมิได้หมดจดอย่างคือการพึ่งพารวมถึงเส้นทาง นี่เป็นกรณีของไลบรารีหรือกรอบงานเช่นกรอบ Zend
การรวมเช่นนี้จะมีลักษณะเช่นนี้:
include "Zend/Mail/Protocol/Imap.php"
ในกรณีนั้นคุณจะต้องแน่ใจว่าโฟลเดอร์ที่เป็น "Zend" เป็นส่วนหนึ่งของพา ธ include
คุณสามารถตรวจสอบเส้นทางรวมกับ:
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 บางอย่างห้ามไม่ให้เข้าถึงไฟล์นั้น
การตั้งค่าสามรายการอาจเกี่ยวข้อง:
- open_basedir
- หากตั้งค่าไว้ PHP จะไม่สามารถเข้าถึงไฟล์ใด ๆ นอกไดเรกทอรีที่ระบุได้ (แม้จะไม่ผ่านลิงก์สัญลักษณ์)
- อย่างไรก็ตามพฤติกรรมเริ่มต้นนั้นไม่สามารถตั้งค่าได้ในกรณีที่ไม่มีข้อ จำกัด
- สามารถตรวจสอบได้โดยโทร
phpinfo()
หรือใช้ini_get("open_basedir")
- คุณสามารถเปลี่ยนการตั้งค่าได้โดยแก้ไขไฟล์ php.ini หรือไฟล์ httpd.conf ของคุณ
- โหมดปลอดภัย
- หากมีการเปิดใช้ข้อ จำกัด อาจนำไปใช้ อย่างไรก็ตามสิ่งนี้ถูกลบใน PHP 5.4 หากคุณยังอยู่ในรุ่นที่รองรับเซฟโหมดอัปเกรดเป็นเวอร์ชัน PHP ที่ยังคงรองรับอยู่
- allow_url_fopen และ allow_url_include
- สิ่งนี้ใช้ได้เฉพาะกับการรวมหรือเปิดไฟล์ผ่านกระบวนการเครือข่ายเช่น http: // ไม่เมื่อพยายามรวมไฟล์ไว้ในระบบไฟล์โลคัล
- สามารถตรวจสอบด้วย
ini_get("allow_url_include")
และตั้งค่าด้วยini_set("allow_url_include", "1")
กรณีมุม
หากไม่มีการเปิดใช้งานด้านบนเพื่อวินิจฉัยปัญหานี่คือสถานการณ์พิเศษที่อาจเกิดขึ้นได้:
1. การรวมไลบรารีที่ใช้พา ธ include
มันอาจเกิดขึ้นได้ที่คุณรวมไลบรารีตัวอย่างเช่นกรอบงาน Zend โดยใช้เส้นทางแบบสัมพันธ์หรือแบบสัมบูรณ์ ตัวอย่างเช่น :
require "/usr/share/php/libzend-framework-php/Zend/Mail/Protocol/Imap.php"
แต่คุณยังคงได้รับข้อผิดพลาดประเภทเดียวกัน
สิ่งนี้อาจเกิดขึ้นเนื่องจากไฟล์ที่คุณมี (รวมสำเร็จ) มีคำสั่ง include สำหรับไฟล์อื่นและตัวที่สองนั้นรวมคำสั่งจะถือว่าคุณได้เพิ่มพา ธ ของไลบรารีนั้นไปยังพา ธ include
ตัวอย่างเช่นไฟล์เฟรมเวิร์ก Zend ที่กล่าวถึงก่อนหน้านี้อาจมีดังต่อไปนี้:
include "Zend/Mail/Protocol/Exception.php"
ซึ่งไม่ใช่การรวมโดยเส้นทางสัมพัทธ์หรือโดยเส้นทางสัมบูรณ์ สันนิษฐานว่าไดเร็กทอรีเฟรมเวิร์ก Zend ถูกเพิ่มเข้ากับพา ธ include
ในกรณีเช่นนี้ทางออกเดียวที่ทำได้คือเพิ่มไดเรกทอรีให้กับเส้นทางของคุณ
2. SELinux
หากคุณใช้งาน 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 สำหรับเว็บเซิร์ฟเวอร์
3. Symfony
หากคุณกำลังใช้ Symfony และพบข้อผิดพลาดนี้เมื่ออัปโหลดไปยังเซิร์ฟเวอร์อาจเป็นไปได้ว่าแคชของแอปไม่ได้ถูกรีเซ็ตเนื่องจากอาจapp/cache
ถูกอัพโหลดหรือไม่ได้ล้างแคชนั้น
คุณสามารถทดสอบและแก้ไขปัญหานี้ได้โดยเรียกใช้คำสั่งคอนโซลต่อไปนี้:
cache:clear
4. ไม่ใช่อักขระ ACSII ภายในไฟล์ซิป
เห็นได้ชัดว่าข้อผิดพลาดนี้สามารถเกิดขึ้นได้เมื่อมีการโทรzip->close()
เมื่อไฟล์บางไฟล์ใน zip มีอักขระที่ไม่ใช่ ASCII ในชื่อไฟล์เช่น "é"
ทางออกที่เป็นไปได้คือการตัดชื่อไฟล์utf8_decode()
ก่อนสร้างไฟล์เป้าหมาย
ให้เครดิตแก่Fran Canoสำหรับการระบุและแนะนำวิธีแก้ไขปัญหานี้