Lỗi xuất hiện bất ngờ
Ứng dụng của bạn đang chạy bình thường. Bạn pull code mới nhất, deploy nhanh, và giờ mọi trang đều báo lỗi:
Fatal error: Uncaught Error: Class 'App\Services\PaymentService' not found in /var/www/html/src/Controllers/CheckoutController.php:42
Stack trace:
#0 {main}
thrown in /var/www/html/src/Controllers/CheckoutController.php on line 42
Hoặc có thể là class của bên thứ ba như Stripe\Charge hay Carbon\Carbon. Dù sao đi nữa, PHP không tìm được class và quá trình thực thi dừng hẳn.
Sáu nguyên nhân gốc rễ chiếm đến 99% các lỗi này. Hãy xử lý theo thứ tự sau — mỗi bước loại trừ nguyên nhân có khả năng cao nhất tiếp theo.
Bước 1: Kiểm tra file có thực sự tồn tại không
Trước khi đổ lỗi cho autoload, hãy xác nhận file class có thực sự trên ổ đĩa không:
# Thay bằng đường dẫn class thực tế
find /var/www/html -name "PaymentService.php" -type f
Không có kết quả nghĩa là file đã mất. Có thể nó chưa bao giờ được commit, bị xóa, hoặc nằm trên một branch chưa được merge. Git có thể cho bạn biết:
git log --all --full-history -- src/Services/PaymentService.php
File tồn tại nhưng PHP vẫn không tìm thấy? Chuyển sang Bước 2.
Bước 2: Kiểm tra namespace có khớp với cấu trúc thư mục không
Namespace không khớp là nguyên nhân gây ra lỗi này nhiều hơn bất kỳ thứ gì khác. Autoloader PSR-4 của PHP ánh xạ namespace sang thư mục — nếu chúng không khớp chính xác, class sẽ vô hình với PHP dù file đang nằm ngay đó.
Mở file class và kiểm tra namespace được khai báo:
<?php
// src/Services/PaymentService.php
namespace App\Services; // ← phải khớp với đường dẫn thư mục
class PaymentService
{
// ...
}
Sau đó kiểm tra composer.json để xem PSR-4 được cấu hình như thế nào:
{
"autoload": {
"psr-4": {
"App\\": "src/" // ← App\ ánh xạ tới src/
}
}
}
Với cấu hình này, App\Services\PaymentService phải nằm tại src/Services/PaymentService.php. Ba kiểu không khớp thường gặp nhất:
- File nằm tại
src/services/PaymentService.php(chữsthường) — Linux phân biệt chữ hoa/thường, Windows thì không, nên trên máy tính của bạn chạy được nhưng lên server thì hỏng - Namespace ghi
App\Service(số ít) nhưng thư mục làServices(số nhiều) - Có thêm cấp thư mục trong đường dẫn nhưng không được phản ánh trong namespace
Sửa lại namespace hoặc di chuyển file cho khớp, rồi tái tạo autoloader.
Bước 3: Tái tạo Composer autoloader
Composer xây dựng classmap lúc cài đặt và cache lại. Thêm file mới hoặc đổi tên namespace mà không tái tạo, PHP sẽ không biết những class đó tồn tại.
# Tái tạo thông thường
composer dump-autoload
# Tối ưu cho production (classmap của mọi class)
composer dump-autoload --optimize
# Không chắc những gì đã được đăng ký? Kiểm tra file được tạo ra
cat vendor/composer/autoload_psr4.php
Chạy dump-autoload, tải lại trang. Hết lỗi? Cache autoload cũ là thủ phạm — chỉ mất 3 giây để sửa nhưng làm bạn mất hàng giờ nếu không nghĩ đến.
Bước 4: Kiểm tra câu lệnh use trong class gọi đến
File đúng và namespace hợp lệ vẫn không cứu được bạn nếu câu lệnh use bị gõ sai:
<?php
namespace App\Controllers;
// Sai — số ít thay vì số nhiều
use App\Service\PaymentService;
// Đúng
use App\Services\PaymentService;
class CheckoutController
{
public function checkout()
{
$payment = new PaymentService(); // Class 'App\Service\PaymentService' not found
}
}
Cũng cần chú ý khi thiếu hoàn toàn câu lệnh use. Không có import, PHP sẽ tìm class trong namespace hiện tại và thất bại:
<?php
namespace App\Controllers;
// Thiếu: use Carbon\Carbon;
class ReportController
{
public function generate()
{
$date = new Carbon(); // Fatal error: Class 'App\Controllers\Carbon' not found
}
}
Bước 5: Với project không dùng Composer, kiểm tra đường dẫn require thủ công
Codebase legacy, framework tự xây, không có Composer? File class phải được require tường minh trước khi bất kỳ đoạn code nào sử dụng nó:
<?php
// Trước khi dùng MyClass ở bất kỳ đâu
require_once __DIR__ . '/../lib/MyClass.php';
$obj = new MyClass();
Hoặc cấu hình autoloader thủ công để không phải viết require_once khắp nơi:
<?php
spl_autoload_register(function ($className) {
$file = __DIR__ . '/lib/' . str_replace('\\', '/', $className) . '.php';
if (file_exists($file)) {
require_once $file;
}
});
Bước 6: Class của package vendor bị thiếu sau khi cài đặt
Khi class bị thiếu thuộc về một package — Stripe, Guzzle, Monolog — vấn đề thường là package chưa được cài đặt, hoặc thư mục vendor/ không được đưa lên server:
# Kiểm tra package có được liệt kê không
composer show | grep stripe
# Không có? Cài đặt nó
composer require stripe/stripe-php
# vendor/ trông không đầy đủ (upload FTP thiếu, deploy thiếu)
composer install --no-dev
Deploy bằng FTP nổi tiếng hay gây ra vấn đề này. Ai đó upload file ứng dụng, quên mất vendor/, rồi thắc mắc tại sao production bị hỏng. Luôn deploy toàn bộ thư mục vendor/, hoặc chạy composer install trực tiếp trên server.
Xác nhận đã sửa xong
Đừng vào thẳng trình duyệt. Hãy kiểm tra từ CLI trước — nó cho bạn thông báo lỗi rõ ràng không bị nhiễu bởi web framework:
# Thử khởi tạo class trực tiếp
php -r "require 'vendor/autoload.php'; new App\\Services\\PaymentService();"
# Hoặc dùng reflection để kiểm tra PHP có load được không
php -r "
require 'vendor/autoload.php';
if (class_exists('App\\Services\\PaymentService')) {
echo 'Class found OK' . PHP_EOL;
} else {
echo 'Still not found' . PHP_EOL;
}
"
Kết quả mong muốn là Class found OK. Bất kỳ kết quả nào khác nghĩa là namespace hoặc đường dẫn file vẫn còn sai.
Danh sách kiểm tra nhanh
- File thực sự tồn tại tại đường dẫn mong đợi: dùng
findhoặcls - Namespace trong file khớp với ánh xạ PSR-4 trong
composer.json - Chữ hoa/thường của tên thư mục và file khớp chính xác với namespace (cẩn thận trên Linux)
- Câu lệnh
usetrong file gọi đến đúng và có mặt đầy đủ - Đã chạy
composer dump-autoloadsau bất kỳ thay đổi file hoặc namespace nào - Thư mục
vendor/đầy đủ và cập nhật trên server
Khuôn mẫu đằng sau những lần bị gọi lúc 2 giờ sáng
Hầu hết các sự cố nửa đêm đều bắt nguồn từ một trong ba điều. Deploy không bao gồm vendor/. Không khớp chữ hoa/thường chỉ bộc phát trên Linux. Hoặc đổi tên namespace mà ai đó quên chạy dump-autoload.
Cách khắc phục cho cả ba: thêm composer dump-autoload --optimize vào script deploy của bạn. Một dòng lệnh, và bạn loại bỏ vĩnh viễn cả một danh mục sự cố này.

