TL;DR: Cách sửa nhanh trong 10 giây
Phần lớn thời gian, lỗi này xảy ra do bạn quên khai báo cho hàm của mình tìm kiếm biến cơ sở dữ liệu toàn cục. Thêm một dòng mã sau thường sẽ giải quyết được vấn đề ngay lập tức:
function get_my_data() {
global $wpdb; // Đây là kết nối còn thiếu
return $wpdb->get_results("SELECT * FROM {$wpdb->prefix}options WHERE option_name = 'siteurl'");
}
Tại sao mã của bạn bị lỗi
PHP khá trực diện. Khi nó báo không thể gọi một hàm trên 'null', điều đó có nghĩa là biến bạn đang sử dụng—trong trường hợp này là $wpdb—hiện đang trống. Nó giống như việc cố gắng khởi động xe khi bạn thậm chí không có chìa khóa trong tay. Trong WordPress, $wpdb là cầu nối chính đến cơ sở dữ liệu MySQL của bạn. Nếu cầu nối đó chưa được xây dựng trước khi bạn cố gắng đi qua, tập lệnh sẽ thất bại.
Tôi thường thấy lỗi này xảy ra vì ba lý do chính:
- **Phạm vi biến (Variable Scope):** Hàm của bạn đang nằm trong "bong bóng" riêng và không biết sự tồn tại của `$wpdb` ở cấp độ toàn cục.
- **Sai thời điểm (Bad Timing):** Bạn thực hiện truy vấn trước khi WordPress hoàn tất việc tải các tệp cốt lõi của cơ sở dữ liệu.
- **Tập lệnh bị cô lập (Isolated Scripts):** Bạn tạo một tệp `.php` tùy chỉnh nhưng không tích hợp môi trường WordPress vào đó.
Cách khắc phục
1. Đưa $wpdb vào phạm vi sử dụng
Các hàm trong PHP được thiết kế để hoạt động biệt lập. Ngay cả khi WordPress định nghĩa $wpdb ngay từ đầu, hàm cụ thể của bạn sẽ không thấy nó trừ khi bạn yêu cầu rõ ràng. Đây là nguyên nhân gây ra khoảng 90% lỗi trong plugin và theme.
Cách làm sai:
function fetch_user_count() {
$count = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->users"); // Lỗi: $wpdb bị null tại đây
}
Cách làm đúng:
function fetch_user_count() {
global $wpdb;
$count = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->users");
return $count;
}
2. Tuân thủ thứ tự tải
WordPress tuân theo một trình tự khởi động nghiêm ngặt. Nếu bạn đặt truy vấn cơ sở dữ liệu ở ngay đầu tệp functions.php mà không đặt nó trong một hook, truy vấn đó có khả năng sẽ thất bại vì đối tượng $wpdb vẫn chưa được khởi tạo.
Luôn đợi cho đến khi WordPress sẵn sàng. Hãy sử dụng các hook như init hoặc plugins_loaded để đảm bảo môi trường đã ổn định trước khi chạm vào cơ sở dữ liệu.
add_action('plugins_loaded', function() {
global $wpdb;
// Bây giờ bạn có thể thực hiện các truy vấn một cách an toàn
$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}posts LIMIT 5");
});
3. Khởi tạo môi trường cho tập lệnh bên ngoài
Nếu bạn đang chạy một tập lệnh di chuyển dữ liệu độc lập hoặc một endpoint API tùy chỉnh bên ngoài cấu trúc thư mục của WordPress, $wpdb sẽ không tồn tại theo mặc định. Bạn cần tải lõi WordPress theo cách thủ công. Hãy trỏ tập lệnh của bạn đến tệp wp-load.php. Tệp này đóng vai trò là cổng vào cho tất cả các hàm và biến của WordPress.
<?php
// Điều chỉnh đường dẫn dựa trên vị trí tệp tin của bạn
require_once( __DIR__ . '/wp-load.php' );
global $wpdb;
$user_count = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->users");
echo "Tổng số người dùng: " . $user_count;
?>
4. Làm việc với Class
Việc sử dụng global bên trong các class là phổ biến nhưng có thể gây lộn xộn. Một cách tiếp cận sạch sẽ hơn là truyền $wpdb vào hàm khởi tạo (constructor) của class. Điều này giúp mã của bạn dễ kiểm thử và dễ đọc hơn nhiều.
class MyPluginHandler {
private $db;
public function __construct($database_object) {
$this->db = $database_object;
}
public function get_data() {
return $this->db->get_results("SELECT * FROM my_table");
}
}
// Cách sử dụng
global $wpdb;
$my_handler = new MyPluginHandler($wpdb);
Cách kiểm tra mọi thứ đã hoạt động bình thường chưa
Đừng chỉ tải lại trang và hy vọng mọi thứ sẽ ổn. Hãy mở tệp wp-content/debug.log của bạn. Nếu bạn đã khắc phục được vấn đề về phạm vi (scope), các mục lỗi Fatal error sẽ ngừng xuất hiện. Bạn cũng có thể thêm một bước kiểm tra đơn giản để xem đối tượng có tồn tại hay không trước khi sử dụng. Điều này ngăn chặn việc toàn bộ trang web bị sập nếu có lỗi xảy ra sau này.
global $wpdb;
if (!isset($wpdb)) {
error_log('Thiếu đối tượng cơ sở dữ liệu!');
return;
}
$data = $wpdb->get_results("...");

