Thiếu PHP extension là một trong những lỗi dễ gặp nhất khi cài Laravel hoặc chạy một ứng dụng PHP mới trên Ubuntu. Lỗi thường xuất hiện dưới dạng “Class not found”, “Call to undefined function”, “Your Composer dependencies require…” hoặc ứng dụng không kết nối được database. Vấn đề không phải lúc nào cũng là PHP chưa cài; nhiều trường hợp extension đã có ở một phiên bản PHP nhưng lại thiếu ở phiên bản CLI, PHP-FPM hoặc Apache đang thực sự chạy.
Bài này trình bày quy trình chẩn đoán theo thứ tự: xác định PHP binary và SAPI đang dùng, đọc chính xác thông báo thiếu module, tìm tên package tương ứng, cài đúng extension cho phiên bản PHP, restart đúng dịch vụ và kiểm tra lại từ cả CLI lẫn web. Ubuntu cung cấp các package PHP qua APT, còn PHP Manual mô tả cách dùng `php -m`, `phpinfo()` và `extension_loaded()` để xác nhận module đã được load [1] [2] [3].
Nguyên tắc quan trọng: đừng cài một package PHP theo tên đoán rồi kết luận đã xong. Hãy xác định ứng dụng đang chạy PHP nào, đọc danh sách module của đúng SAPI đó và kiểm tra lại sau khi restart đúng service.
1. Phân biệt PHP CLI, PHP-FPM và PHP của web server
Trên Ubuntu, một máy có thể cài nhiều phiên bản PHP cùng lúc. Lệnh `php` trong terminal có thể trỏ tới PHP 8.3, trong khi Nginx chuyển request đến socket PHP-FPM 8.2. Nếu bạn chạy `php -m` và thấy `mbstring`, nhưng ứng dụng web vẫn báo thiếu module, nguyên nhân thường là hai tiến trình đang đọc hai bộ `php.ini` khác nhau. Apache module, CGI, CLI và FPM có thể có đường dẫn cấu hình riêng.
Bước đầu tiên là ghi nhận phiên bản, đường dẫn binary, SAPI và file ini đang được load. Không nên bắt đầu bằng việc sửa hàng loạt file trong `/etc/php`; hãy lưu output để so sánh trước và sau khi cài.
php -v
php --ini
php -m | sort
command -v php
update-alternatives --display php 2>/dev/null || true
ps aux | grep '[p]hp-fpm'
`php --ini` cho biết file cấu hình chính và các thư mục scan thêm của CLI. Với PHP-FPM, có thể dùng binary tương ứng hoặc kiểm tra process đang chạy, service name và socket mà Nginx trỏ tới. Trong môi trường nhiều phiên bản, đừng chỉ nhìn kết quả của `command -v php`; cần xác nhận cả upstream trong Nginx và service PHP-FPM.
2. Đọc lỗi để biết extension nào đang thiếu
Mỗi loại lỗi gợi ý một cách kiểm tra khác nhau. Composer thường nêu trực tiếp tên extension, ví dụ `ext-curl`, `ext-mbstring`, `ext-xml`, `ext-fileinfo`, `ext-bcmath` hoặc `ext-pdo`. Lỗi runtime lại có thể chỉ hiển thị tên hàm hoặc class không tồn tại. Hãy lưu toàn bộ log thay vì chỉ sao chép dòng cuối; tên package cần cài có thể khác tên extension mà PHP hiển thị.
| Triệu chứng | Khả năng thường gặp | Kiểm tra đầu tiên |
|---|---|---|
| Composer báo `ext-xxx` thiếu | Package chưa cài hoặc Composer đang dùng PHP khác | `php -m`, `php --ini`, `composer check-platform-reqs` |
| `Call to undefined function` | Extension chưa load trong SAPI hiện tại | `php -r` với `extension_loaded()` và kiểm tra web SAPI |
| `Class not found` của PDO/DOM/XML | Thiếu module hoặc autoload/cache chưa cập nhật | Tên class, `php -m`, `composer dump-autoload` |
| CLI chạy, web lỗi | CLI và FPM/Apache dùng ini hoặc version khác | PHP-FPM process, socket, phpinfo tạm thời |
| Cài xong vẫn lỗi | Chưa restart service hoặc cài nhầm phiên bản | `systemctl status`, `php --ini`, log error |
3. Tìm package PHP extension trên Ubuntu
PHP Manual khuyến nghị cập nhật package list trước khi cài và dùng `apt search` hoặc `apt-cache search` để tìm package. Ubuntu cũng minh họa các package phổ biến như `php-cli`, `php-mysql`, `php-pgsql`; tên package cụ thể có thể phụ thuộc phiên bản mặc định của Ubuntu [1] [2]. Với server cài nhiều version PHP, package có thể mang hậu tố version, chẳng hạn `php8.3-curl` hoặc `php8.2-mbstring`, thay vì package meta `php-curl`.
sudo apt update
apt search '^php.*(curl|mbstring|xml|mysql|gd|zip|bcmath|intl)$'
apt-cache search php | grep -E 'curl|mbstring|xml|mysql|gd|zip|bcmath|intl'
apt policy php php-cli php-fpm php-curl php-mbstring
Không nên cài “tất cả extension” chỉ để làm Composer im lặng. Mỗi module làm tăng bề mặt bảo trì và có thể kéo theo thư viện hệ thống. Hãy lập danh sách phụ thuộc từ `composer.json`, tài liệu framework và lỗi thực tế, sau đó cài đúng nhóm cần thiết. Với Laravel, các module thường gặp gồm PDO driver cho database, cURL, Mbstring, XML, Fileinfo, OpenSSL, tokenizer và JSON; nhưng danh sách cuối cùng phải đối chiếu với phiên bản Laravel/PHP của dự án.
4. Cài extension bằng APT theo đúng phiên bản PHP
Nếu máy chỉ có một PHP mặc định, package meta như `php-curl` thường là lựa chọn đơn giản. Nếu có nhiều phiên bản, hãy chỉ rõ package version để tránh cài extension cho PHP không chạy ứng dụng. Ví dụ dưới đây minh họa hai cách; thay `8.3` bằng version thực tế đã xác định ở bước chẩn đoán.
# Cách 1: PHP mặc định của Ubuntu
sudo apt install php-curl php-mbstring php-xml php-mysql php-zip php-bcmath
# Cách 2: chỉ rõ PHP-FPM/PHP version
sudo apt install php8.3-curl php8.3-mbstring php8.3-xml \
php8.3-mysql php8.3-zip php8.3-bcmath
APT thường tự thêm file ini vào thư mục cấu hình PHP tương ứng. Tuy vậy, việc cài package chưa đảm bảo worker FPM đang dùng cấu hình mới. Sau khi cài, hãy xác định service chính xác bằng `systemctl list-units` hoặc tên package đã cài, rồi restart đúng service. Nếu chạy Nginx với PHP-FPM, không cần restart Nginx cho mọi extension; restart PHP-FPM thường là bước cần thiết, nhưng `nginx -t` vẫn nên chạy sau khi thay đổi cấu hình Nginx.
systemctl list-units --type=service --all | grep -E 'php.*fpm|apache2'
sudo systemctl restart php8.3-fpm
sudo systemctl is-active php8.3-fpm
sudo nginx -t
5. Kiểm tra extension sau khi cài
PHP Manual ghi rõ `php -m` dùng để liệt kê module khả dụng trong CLI hoặc CGI. Với một extension cụ thể, `extension_loaded()` trả về `true` hoặc `false`; đây là cách kiểm tra phù hợp trong một script nhỏ. Kiểm tra theo tên module thay vì chỉ kiểm tra package đã cài, vì package có thể tồn tại nhưng file ini chưa được load hoặc process cũ chưa restart [3].
php -m | grep -Ei 'curl|mbstring|xml|pdo|mysql|zip|bcmath|intl'
php -r "var_export(extension_loaded('curl')); echo PHP_EOL;"
php -r "var_export(extension_loaded('mbstring')); echo PHP_EOL;"
php -r "var_export(extension_loaded('pdo_mysql')); echo PHP_EOL;"
<?php
$required = ['curl', 'mbstring', 'xml', 'fileinfo', 'pdo_mysql'];
foreach ($required as $name) {
printf("%-12s %s\n", $name, extension_loaded($name) ? 'loaded' : 'MISSING');
}
Nếu `php -m` cho kết quả đúng nhưng web vẫn lỗi, tạo một file `phpinfo.php` tạm thời trong vùng được bảo vệ, mở đúng URL qua trình duyệt và đối chiếu “Loaded Configuration File”, “Scan this dir for additional .ini files” cùng danh sách module. Xóa file ngay sau khi kiểm tra vì phpinfo có thể tiết lộ đường dẫn, biến môi trường và cấu hình nhạy cảm.
6. Những lỗi cài extension hay gặp
Cài đúng tên extension nhưng sai tên package
Tên Composer `ext-pdo_mysql` không nhất thiết là tên package đầy đủ. Trên Ubuntu, package thường là `php-mysql` hoặc `php8.3-mysql`, còn module hiển thị trong PHP là `pdo_mysql`. Hãy tra bằng `apt search` và kiểm tra file package, không tự tạo symlink `.so` nếu chưa hiểu ABI và cấu hình PHP.
Đang dùng sai phiên bản PHP
Một server chạy nhiều PHP version có thể có `/etc/php/8.2`, `/etc/php/8.3` và nhiều socket FPM. Cài `php8.3-curl` nhưng Nginx gửi request tới `php8.2-fpm.sock` sẽ không sửa được lỗi của website. Kiểm tra `fastcgi_pass`, service FPM, `php-fpm -i` hoặc phpinfo của đúng virtual host.
Quên restart worker
PHP-FPM là nhóm process dài hạn. Worker đã khởi động trước khi package được cài có thể chưa đọc cấu hình mới. Đây là lý do lỗi vẫn còn dù `apt` báo cài thành công. Hãy restart service, kiểm tra trạng thái và xem error log ngay sau khi gửi một request mới.
Nhầm CLI với web
Cron chạy bằng CLI có thể dùng một PHP binary khác với Nginx. Hãy ghi rõ đường dẫn `/usr/bin/php8.3`, đặt PATH trong cron nếu cần và chạy thử đúng user. Đối với Laravel queue hoặc scheduler, extension thiếu trong CLI vẫn có thể làm job fail dù frontend đang hoạt động.
7. Checklist xử lý nhanh trong production
- Ghi lại lỗi đầy đủ và xác định extension hoặc class/function bị thiếu.
- Chạy `php -v`, `php --ini`, `php -m` và kiểm tra process PHP-FPM đang phục vụ website.
- Đối chiếu PHP version với package extension; không cài mù theo tên module.
- Chạy `apt update`, cài package tối thiểu và kiểm tra dependency của Composer.
- Restart đúng PHP-FPM/Apache, kiểm tra `systemctl is-active` và log lỗi.
- Xác nhận module bằng `php -m`, `extension_loaded()` và một request thật qua web.
- Xóa phpinfo/debug file, ghi lại package/version và cập nhật runbook vận hành.
8. Kết luận
Cài PHP extension trên Ubuntu là quy trình xác định đúng runtime trước, package sau và restart đúng service cuối cùng. Khi website dùng Nginx và PHP-FPM, hãy coi CLI, FPM và web request là ba lớp cần kiểm tra riêng. Nếu server chạy nhiều version PHP, việc chỉ rõ package, socket và binary sẽ tránh phần lớn lỗi “đã cài nhưng vẫn thiếu module”. Bạn có thể xem thêm bài [chạy nhiều phiên bản PHP với Nginx và PHP-FPM] để đối chiếu cách tách runtime.
Nguồn tham khảo
[1] Ubuntu Server — How to install and configure PHP; [2] PHP Manual — Installing from packages on Debian GNU/Linux; [3] PHP Manual — extension_loaded.