Vấn đề: Tại sao PHP ngừng thực thi
PHP rất khắt khe về việc đặt tên. Nếu engine thấy hai hàm có tên hoàn toàn giống nhau trong cùng một phạm vi (scope), nó sẽ báo lỗi. Nó đưa ra một Fatal Error và dừng script ngay lập tức. Điều này thường xảy ra khi bạn vô tình include cùng một file hai lần hoặc khi hai thư viện khác nhau sử dụng những tên chung chung như init() hoặc send_email().
Tôi đã gặp phải vấn đề này gần đây khi chuyển đổi một mã nguồn cũ (legacy codebase) năm năm tuổi sang một framework hiện đại. Đây là lỗi chính xác xuất hiện trong error.log của tôi:
PHP Fatal error: Cannot redeclare send_email() (previously declared in /var/www/html/functions.php:10) in /var/www/html/helper.php on line 5
Quy trình Debug
Đừng bỏ qua thông báo lỗi—nó cho bạn biết chính xác nơi xảy ra xung đột. Bạn sẽ nhận được hai manh mối quan trọng:
- Nguồn gốc ban đầu:
/var/www/html/functions.php:10(Nơi hàm được khai báo lần đầu tiên). - Nguồn gây xung đột:
/var/www/html/helper.php on line 5(Nơi PHP cố gắng tạo lại hàm đó).
Trong 90% trường hợp, điều này xảy ra vì một trong ba lý do. Bạn có thể đang sử dụng include bên trong một vòng lặp. Bạn có thể có hai file khác nhau sử dụng tên giống hệt nhau. Hoặc, autoloader trong composer.json của bạn có thể bị cấu hình sai, dẫn đến việc load cùng một file hai lần.
Giải pháp 1: Chuyển sang require_once
Đây là tuyến phòng thủ đầu tiên của bạn. Nếu lỗi của bạn bắt nguồn từ việc include cùng một file nhiều lần—chẳng hạn như cả header và footer đều gọi config.php—hãy chuyển sang require_once hoặc include_once.
Cách làm sai:
// Trong index.php
require 'functions.php';
require 'helper.php'; // Nếu helper.php cũng gọi require 'functions.php', script sẽ bị lỗi.
Cách làm đúng:
// Trong index.php
require_once 'functions.php';
require_once 'helper.php';
Hậu tố _once yêu cầu PHP kiểm tra danh sách các file đã được load. Nếu một file đã nằm trong danh sách, PHP sẽ bỏ qua yêu cầu thứ hai. Thay đổi đơn giản này sẽ khắc phục phần lớn các vấn đề khai báo lại (redeclaration).
Giải pháp 2: Bao bọc hàm trong function_exists
Nếu bạn đang xây dựng một plugin hoặc một thư viện dùng chung, bạn không thể kiểm soát cách các lập trình viên khác include file của mình. Để ngăn chặn lỗi, hãy bao bọc các khai báo của bạn trong một kiểm tra điều kiện. Đây là tiêu chuẩn bắt buộc trong phát triển WordPress.
if (!function_exists('send_email')) {
function send_email($to, $subject, $message) {
return mail($to, $subject, $message);
}
}
Bằng cách sử dụng kiểm tra này, PHP chỉ định nghĩa hàm nếu tên đó vẫn còn trống. Nếu một script khác đã sử dụng tên đó, PHP sẽ đơn giản là bỏ qua khối mã và tiếp tục thực thi.
Giải pháp 3: Namespaces (Tiêu chuẩn Chuyên nghiệp)
Nếu bạn có hai hàm trùng tên nhưng thực hiện các tác vụ khác nhau thì sao? Bạn không nên phải đổi tên chúng thành thứ gì đó lộn xộn như send_email_v2_final(). Thay vào đó, hãy sử dụng namespaces để giữ chúng trong các "thư mục" riêng biệt.
File: functions.php
namespace App\Core;
function send_email() {
echo "Đang gửi qua Core Mailer...";
}
File: helper.php
namespace App\Utilities;
function send_email() {
echo "Đang gửi qua Utility Helper...";
}
Cách sử dụng:
use App\Core as CoreMail;
use App\Utilities as UtilMail;
CoreMail\send_email();
UtilMail\send_email();
Namespacing là cách sạch nhất để tổ chức mã nguồn PHP hiện đại. Nó cho phép App\Core\send_email và App\Utilities\send_email tồn tại song song mà không gây ra bất kỳ xung đột nào.
Cách xác minh việc khắc phục
Sau khi áp dụng giải pháp, hãy thực hiện các bước sau để đảm bảo lỗi đã được xử lý triệt để:
- Xóa log: Xóa sạch
error_logcủa bạn, tải lại trang và kiểm tra xem file log có còn trống không. - Kiểm tra CLI: Chạy
php index.phptrong terminal của bạn. Nếu nó thực thi mà không có stack trace, bạn đã thành công. - Chạy Unit Tests: Nếu bạn sử dụng PHPUnit, hãy chạy bộ test của mình để đảm bảo logic vẫn hoạt động sau khi đổi tên hoặc bao bọc các hàm.
Mẹo chuyên nghiệp để viết code sạch
- Luôn sử dụng
require_oncecho các file cấu trúc cốt lõi như cài đặt hoặc kết nối cơ sở dữ liệu. - Tránh sử dụng các tên chung chung. Thay vì
init(), hãy sử dụng tên duy nhất nhưstripe_payment_init(). - Áp dụng cấu trúc dựa trên Class. Các phương thức (methods) bên trong class không gặp phải xung đột đặt tên toàn cục.
- Hãy để Composer xử lý các công việc nặng nhọc. Autoloader PSR-4 giúp loại bỏ hoàn toàn nhu cầu sử dụng các câu lệnh
requirethủ công.

