Hàm khởi tạo (constructor) trong PHP là phương thức tên __construct(). PHP tự động gọi nó khi bạn viết new ClassName(...), và các đối số trong cặp ngoặc trở thành tham số của nó, nhờ vậy đối tượng có sẵn giá trị ngay từ lúc được tạo ra.
Không có hàm khởi tạo, bạn sẽ tạo một đối tượng rỗng rồi gán từng thuộc tính sau đó, và chẳng có gì ngăn bạn quên mất một thuộc tính. Có hàm khởi tạo, new User() không kèm đối số sẽ gây ra ArgumentCountError.
Constructor property promotion
Khai báo một thuộc tính, liệt kê nó làm tham số rồi gán $this->name = $name khiến mỗi cái tên bị lặp lại ba lần. Từ PHP 8.0, một từ khóa phạm vi truy cập đặt trước tham số của hàm khởi tạo làm cả ba việc cùng lúc:
Tham số được promote cũng có thể là readonly (public readonly string $id), đây là cách ngắn nhất để viết một value object bất biến. Bạn có thể dùng lẫn tham số được promote và tham số thường, và thân hàm khởi tạo vẫn có thể chứa code chạy sau khi các thuộc tính được promote đã được gán.
Giá trị mặc định và đối số có tên
Tham số của hàm khởi tạo nhận giá trị mặc định như mọi hàm khác. Tham số có giá trị mặc định đứng sau các tham số bắt buộc. Với đối số có tên (named arguments, PHP 8.0), nơi gọi có thể nhảy thẳng tới tùy chọn cần đặt thay vì lặp lại mọi giá trị mặc định đứng trước nó:
Hãy chạy thử: đầu ra có HTML, nên tab Page hiển thị ba nút đã được vẽ, còn tab Đầu ra hiển thị các thẻ mà PHP in ra. Để ý cặp ngoặc trong (new Button('Save'))->render(): từ PHP 8.4 chúng không bắt buộc, nhưng PHP 8.3 vẫn cần.
Kiểm tra đối số trong hàm khởi tạo
Hàm khởi tạo không thể trả về false hay một mã lỗi: mọi giá trị nó trả về đều bị bỏ qua. Để từ chối dữ liệu đầu vào không hợp lệ, hãy ném một ngoại lệ. Khi đó đối tượng không bao giờ được tạo ra, nên phần còn lại của chương trình có thể tin tưởng mọi Temperature mà nó nhận được.
Trang try catch trình bày chi tiết hơn cách bắt các ngoại lệ này.
Nhiều hàm khởi tạo với phương thức factory static
PHP không có nạp chồng hàm khởi tạo: một lớp, một __construct. Khi một đối tượng có thể được tạo từ nhiều loại dữ liệu khác nhau (một chuỗi ngày, một mảng, một timestamp), hãy thêm các phương thức static có tên rõ ràng để chuẩn bị đối số rồi gọi new static(...). Đặt hàm khởi tạo là private sẽ buộc mọi người phải đi qua các factory.
Giờ new Color(1, 2, 3) từ bên ngoài lớp sẽ ném ra Error ("Call to private Color::__construct() from global scope"). Trang static giải thích vì sao ở đây dùng new static chứ không phải new self.
Gọi hàm khởi tạo của lớp cha bằng parent::__construct
Khi lớp con định nghĩa hàm khởi tạo riêng, hàm khởi tạo của lớp cha không tự chạy. Hãy gọi nó bằng parent::__construct(...), thường ở dòng đầu, để thuộc tính của lớp cha được gán trước khi lớp con thêm thuộc tính của mình:
Nếu lớp con hoàn toàn không có hàm khởi tạo, PHP dùng hàm khởi tạo của lớp cha, và new Truck('Volvo') sẽ chuyển thẳng đối số của nó cho Vehicle::__construct. Xem thêm ở trang kế thừa.
Quên gọi parent::__construct
Lỗi này không gây ra thông báo nào ở đúng dòng xảy ra lỗi. Hàm khởi tạo của lớp con vẫn chạy, các thuộc tính có kiểu của lớp cha vẫn ở trạng thái chưa khởi tạo, và chương trình chỉ hỏng về sau, ở chỗ thuộc tính được đọc lần đầu:
Thêm parent::__construct(); vào dòng đầu tiên của Post::__construct rồi chạy lại: chương trình in ra ngày tháng. Một phương thức liên quan là __destruct() (hàm hủy), chạy khi tham chiếu cuối cùng tới đối tượng biến mất hoặc khi script kết thúc. Trong code thường ngày hiếm khi cần đến nó, vì PHP tự đóng tệp và giải phóng bộ nhớ khi mỗi request kết thúc.
Câu hỏi thường gặp
__construct trong PHP là gì?
__construct() là hàm khởi tạo: một phương thức PHP tự động gọi ngay sau khi new tạo ra đối tượng. Các đối số bạn truyền cho new User('Ada', 36) được chuyển vào __construct(string $name, int $age), nơi chúng thường được lưu vào thuộc tính.
Constructor property promotion trong PHP là gì?
Từ PHP 8.0, đặt một từ khóa phạm vi truy cập trước tham số của hàm khởi tạo sẽ vừa khai báo thuộc tính vừa gán giá trị cho nó trong một bước: public function __construct(private string $name) {} tương đương với khai báo private string $name; rồi viết $this->name = $name; trong thân hàm.
Một lớp PHP có thể có nhiều hàm khởi tạo không?
Không. PHP không có nạp chồng phương thức (overloading), nên một lớp chỉ có một __construct. Hãy dùng tham số tùy chọn có giá trị mặc định, hoặc thêm các phương thức factory static như public static function fromArray(array $data): static gọi new static(...) với đúng đối số.
Hàm khởi tạo của lớp cha có được gọi tự động trong PHP không?
Chỉ khi lớp con không có hàm khởi tạo riêng. Nếu lớp con định nghĩa __construct, hàm khởi tạo của lớp cha sẽ không chạy trừ khi bạn tự gọi parent::__construct(...), thường ở dòng đầu tiên.
Hàm khởi tạo trong PHP có trả về giá trị được không?
Không. new luôn trả cho bạn đối tượng, và mọi giá trị __construct trả về đều bị bỏ qua. Để báo lỗi, hãy ném một ngoại lệ từ hàm khởi tạo, ví dụ throw new InvalidArgumentException('Age must be positive').