PHP 7.4 là nhánh cuối cùng của PHP 7 và đã hết hỗ trợ bảo mật từ cuối năm 2022. Trên nhiều cụm Cloud VPS, nó vẫn đang chạy không ít website, bên cạnh một nhóm site còn cũ hơn trên PHP 7.2, vốn đã hết hỗ trợ từ năm 2020. Tài liệu này giải thích EOL thực sự nghĩa là gì, vì sao không thể tắt PHP 7.2 và PHP 7.4 ngay, cách sống chung có kiểm soát và lộ trình nâng cấp từng site lên PHP 8 mà không ảnh hưởng các site khác.
1. EOL nghĩa là gì
Mỗi nhánh PHP có vòng đời cố định, được công bố trên trang các phiên bản PHP đang được hỗ trợ: hai năm hỗ trợ đầy đủ (sửa lỗi và bảo mật), tiếp theo một năm chỉ vá bảo mật. Hết giai đoạn đó là EOL (end of life): nhóm phát triển PHP không phát hành bản vá nào nữa, kể cả khi phát hiện lỗ hổng nghiêm trọng.
| Nhánh | Phát hành | Hết hỗ trợ đầy đủ | Hết vá bảo mật |
|---|---|---|---|
| PHP 7.2 | 2017 | 2019 | 2020 |
| PHP 7.3 | 2018 | 2020 | 2021 |
| PHP 7.4 | 2019 | 2021 | 2022 |
| PHP 8.0 | 2020 | 2022 | 2023 |
| PHP 8.1 | 2021 | 2023 | 2024 |
Các mốc chính xác luôn lấy từ trang php.net ở trên. Hai điểm hay bị hiểu nhầm:
- Gói vẫn cài được không có nghĩa là còn được hỗ trợ. Nhiều repo gói vẫn cung cấp
php7.4-fpmvàphp7.2-fpm, nhưng bản thân PHP không còn bản vá chính thức. Gói cài được chỉ cho biết có người còn đóng gói nó. - EOL không làm site ngừng chạy. Ngay sau mốc EOL, mọi site PHP 7.4 vẫn chạy y như trước. Rủi ro không đến ngay, nó tích luỹ theo từng lỗ hổng mới được công bố mà không ai vá.
2. Rủi ro khi tiếp tục chạy PHP 7.4 và 7.2
- Lỗ hổng không được vá. Lỗ hổng mới trong lõi PHP hay extension đi kèm sẽ tồn tại mãi trên máy đó.
- Lớp bảo vệ phía trước không thay được bản vá. Snippet chặn và rate limit trên balancer lọc được request rác ở tầng HTTP, nhưng không vá được lỗi trong runtime PHP khi một request trông hợp lệ khai thác nó.
- Hệ sinh thái bỏ lại phía sau. Thư viện Composer lần lượt nâng yêu cầu tối thiểu lên PHP 8. Site PHP 7.4 dần bị khoá ở các phiên bản thư viện cũ, và chính các thư viện đó cũng ngừng nhận bản vá.
- Nâng cấp càng muộn càng đắt. Từ PHP 7.2 lên 8.1 là vượt bốn nhánh một lúc, khối lượng sửa lớn hơn nhiều so với đi từng bước.
3. Vì sao chưa thể tắt PHP 7.2 và PHP 7.4 ngay
Lý do thường không nằm ở hạ tầng mà ở code. Nhiều site cũ được xây trên các nhánh Laravel đời đầu và package không còn được bảo trì, gắn với PHP 7.x. Nâng một site như vậy không phải đổi một dòng cấu hình, mà là một dự án nhỏ: nâng framework, thay package, sửa code, kiểm thử lại toàn bộ chức năng. Với site đang ở chế độ chỉ bảo trì, chi phí đó cần được chủ site đồng ý trước.
Vì vậy, cách hợp lý là sống chung có kiểm soát thay vì tắt bỏ:
- Đóng băng nhóm cũ. Không đưa site mới nào lên minion PHP 7.2 hay PHP 7.4. Site mới đi thẳng lên nhóm minion PHP 8.x.
- Giảm bề mặt tấn công. Minion cũ chỉ có IP private, chỉ nhận request đã qua balancer. Đặt
expose_php = Offđể header không tiết lộ phiên bản. Chặn ở balancer các đường dẫn không bao giờ hợp lệ. - Ưu tiên theo dữ liệu. Site có đăng nhập, thanh toán hay dữ liệu cá nhân được đưa lên đầu danh sách nâng cấp. Site giới thiệu tĩnh xếp sau.
4. Tách minion theo phiên bản: điều kiện để nâng cấp từng site
Như trong bài chạy nhiều phiên bản PHP song song, minion nên được chia theo phiên bản PHP. Cách chia này biến việc nâng cấp thành thao tác chuyển một site từ máy này sang máy khác:
- Nâng site A không đụng tới các site khác đang ở trên minion PHP 7.4.
- Database nằm ở tầng riêng, nên chuyển minion không phải di chuyển dữ liệu.
- Rollback chỉ là trả dòng upstream trên balancer về minion cũ.
Nếu nâng PHP tại chỗ trên một minion chung, mọi site trên máy đó phải sẵn sàng cùng lúc, và chỉ cần một site chưa tương thích là cả kế hoạch dừng lại.
5. Lộ trình nâng cấp một site từ PHP 7.4 lên 8.x
Bước 1: Kiểm kê
# ràng buộc PHP mà dự án khai báo
grep -A3 '"require"' composer.json # ví dụ "php": "^7.2"
# package nào đang chặn việc lên PHP 8.1
composer why-not php 8.1
# package trực tiếp đã có bản mới
composer outdated --directKết quả của composer why-not là danh sách việc cần làm: mỗi dòng là một package có ràng buộc PHP không cho phép 8.1. Framework thường là dòng đầu tiên. Laravel 9 yêu cầu PHP 8.0 trở lên, trong khi các nhánh cũ hơn chỉ hỗ trợ PHP 8 ở những bản vá cuối. Với site trên nhánh Laravel quá cũ, bước này đồng nghĩa với nâng framework.
Bước 2: Rà code tự động
Danh sách thay đổi không tương thích nằm trong hướng dẫn chuyển từ PHP 7.4 lên PHP 8.0 trên php.net. Những điểm hay gặp trong code cũ:
- So sánh chuỗi với số đổi hành vi:
0 == "abc"từtruethànhfalse. - Hàm đã bị gỡ:
each(),create_function(),money_format(). - Cú pháp truy cập offset bằng ngoặc nhọn
$str{0}không còn hợp lệ. matchtrở thành từ khoá, tên hàm hay tên class trùng sẽ lỗi cú pháp.- Từ PHP 8.1: truyền
nullcho tham số kiểu chuỗi của hàm có sẵn (nhưstrlen(null)) sinh cảnh báo deprecated, log có thể đầy rất nhanh.
Công cụ như PHPCompatibility (chạy trên PHP_CodeSniffer) phát hiện được nhiều điểm trên bằng phân tích tĩnh, còn Rector có thể tự sửa một phần. Không công cụ nào thay được việc chạy thử thật.
Bước 3: Chạy thử trên minion mới
# trên minion PHP 8.x: clone code vào đúng quy ước thư mục
cd /var/www/example.com
/usr/bin/php8.1 /usr/local/bin/composer install --no-dev
# kiểm thử qua domain demo trỏ vào minion mới
# staging: example.demo.example.com
tail -f storage/logs/laravel-*.log | grep -E 'ERROR|Deprecated'Composer phải chạy bằng đúng CLI của phiên bản đích, nếu không nó sẽ giải quyết dependency theo phiên bản PHP sai. Bản chạy thử dùng database staging, không dùng chung database production, vì code mới có thể chạy migration. Trong suốt thời gian này, bản PHP 7.4 trên minion cũ vẫn phục vụ người dùng như bình thường.
Bước 4: Chuyển upstream và theo dõi
# trên balancer: đổi đúng một dòng trong file cấu hình site
upstream example_com {
server 10.0.0.23:80; # trước đây 10.0.0.21 (minion PHP 7.4)
}
nginx -t && systemctl reload nginxTrước khi đổi, đồng bộ lần cuối thư mục upload (storage/app hoặc public/uploads) và chuyển file supervisord.ini của site, để queue worker chạy trên minion mới thay vì cả hai nơi. Sau khi đổi, theo dõi log lỗi của domain trên balancer và log Laravel trên minion mới ít nhất một chu kỳ cao điểm. Giữ nguyên bản cũ trên minion PHP 7.4 thêm một thời gian: nếu có lỗi, rollback chỉ là trả dòng server về IP cũ trên Nginx reverse proxy ở tầng balancer.
6. Lỗi thường gặp khi nâng từ PHP 7.4
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
Composer detected issues in your platform | Cài dependency bằng CLI khác phiên bản FPM | Gọi composer bằng đúng /usr/bin/php8.x |
| Log đầy cảnh báo deprecated | Truyền null cho hàm có sẵn trên PHP 8.1 | Sửa code; tạm thời hạ mức log deprecated ở production |
| Queue xử lý job hai lần | Worker còn chạy trên cả minion cũ và mới | Dừng supervisor của site trên minion cũ trước khi đổi upstream |
| Ảnh mới upload bị mất sau khi chuyển | Chưa đồng bộ lần cuối thư mục upload | Rsync lại ngay trước khi đổi upstream |
7. Checklist nâng cấp một site
- Ghi lại phiên bản PHP, Laravel và minion hiện tại của site.
- Chạy
composer why-not php 8.1, lập danh sách package cần nâng hoặc thay. - Quét code bằng công cụ tương thích, sửa các lỗi chặn.
- Cài lên minion PHP 8.x với composer chạy đúng CLI phiên bản đích.
- Kiểm thử qua domain demo với database staging: đăng nhập, form, thanh toán, upload, queue, scheduler.
- Thống nhất thời điểm chuyển với chủ site, ưu tiên giờ thấp điểm.
- Đồng bộ file upload lần cuối, chuyển supervisor và cron sang minion mới.
- Đổi upstream trên balancer,
nginx -t && systemctl reload nginx. - Theo dõi log lỗi, giữ bản cũ để rollback.
- Sau giai đoạn ổn định, gỡ site khỏi minion PHP 7.4 cũ và cập nhật tài liệu.
8. Khuyến nghị
EOL của PHP 7.4 không phải sự kiện bất ngờ: mốc hết hạn được công bố từ khi nhánh ra đời. Lộ trình nâng cấp nên bắt đầu khi nhánh vừa chuyển sang giai đoạn chỉ vá bảo mật, tức sớm khoảng một năm so với EOL, chứ không phải khi đã hết hạn. Kiến trúc cũng quyết định chi phí nâng cấp: khi minion tách theo phiên bản và database tách khỏi minion, nâng một site khỏi PHP 7.4 chỉ còn là chuỗi thao tác có thể hoàn tác, làm từng site một theo đúng thứ tự rủi ro.
Sống chung với PHP 7.2 và PHP 7.4 nên là trạng thái tạm thời được kiểm soát, không phải trạng thái đích. Mỗi site chuyển đi là một site bớt rủi ro, và khi nhóm minion cũ không còn site nào thì gỡ hẳn cả nhóm.
Bài tiếp theo
SSL cho hàng trăm domain với Certbot. HTTP-01 hoạt động thế nào, gia hạn tự động, reload nginx sau khi gia hạn, theo dõi chứng chỉ sắp hết hạn và giới hạn của Let's Encrypt.
