Menu

Từ khóa static trong PHP: phương thức tĩnh, self và static

Một phương thức hay thuộc tính static thuộc về lớp chứ không thuộc về đối tượng: bạn gọi nó bằng ClassName::method() và đọc bằng ClassName::$count, không cần new. Tìm hiểu self:: và static:: (late static binding), new static, biến static bên trong hàm, và khi nào static là lựa chọn sai.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Một phương thức hay thuộc tính static thuộc về chính lớp thay vì về bất kỳ đối tượng nào. Bạn gọi phương thức tĩnh bằng ClassName::method() và đọc thuộc tính tĩnh bằng ClassName::$property, không cần tạo đối tượng trước. Bên trong lớp, self:: trỏ đến chính các thành viên đó.

Không hề có new Temperature() ở đâu cả. Thành viên tĩnh hữu ích cho các hàm tiện ích không cần trạng thái của đối tượng (chuyển đổi, factory, kiểm tra dữ liệu) và cho dữ liệu thuộc về cả lớp, như một bộ đếm.

Thuộc tính tĩnh dùng chung cho mọi đối tượng

Một thuộc tính bình thường có một giá trị cho mỗi đối tượng. Thuộc tính tĩnh có một giá trị cho cả lớp, nên mọi đối tượng đều thấy cùng một giá trị. Ở đây mỗi User mới tăng một bộ đếm dùng chung và nhận ID tiếp theo:

Chú ý dấu $ trong self::$nextId: thuộc tính tĩnh giữ dấu này sau ::, còn hằng số (self::MAX) thì không. Một thuộc tính tĩnh chỉ tồn tại trong một request PHP. Trên một website điển hình (PHP-FPM hoặc Apache), nó bắt đầu lại sau mỗi lần tải trang, nên đây không phải chỗ để lưu dữ liệu giữa các người xem.

Phương thức tĩnh không có $this

Phương thức tĩnh chạy mà không có đối tượng, nên bên trong không tồn tại $this. Phương thức tĩnh có thể gọi các phương thức tĩnh khác bằng self::, nhưng không đọc được thuộc tính bình thường. Gọi một phương thức bình thường theo kiểu tĩnh cũng thất bại:

Chiều ngược lại thì không sao: một phương thức bình thường có thể gọi phương thức tĩnh, như withSeparator() làm với self::make().

self và static: late static binding

self:: trỏ đến lớp nơi đoạn code được viết. static:: trỏ đến lớp mà phương thức được gọi trên đó lúc chạy. Khác biệt chỉ lộ ra khi có kế thừa, và quan trọng nhất với các phương thức factory:

Post::createWithSelf() trả về một Model, gần như không bao giờ là thứ người gọi mong muốn. static được hiểu là Post vì đó là lớp được nêu tên trong lời gọi; PHP gọi cơ chế này là late static binding. Hãy dùng static:: và new static() trong những phương thức sẽ được kế thừa, và self:: khi bạn thực sự muốn nói đúng lớp này. Quy tắc tương tự áp dụng cho hằng số: static::TABLE lấy TABLE riêng của lớp con, còn self::TABLE luôn đọc hằng số trong lớp nơi đoạn code được viết.

Thuộc tính tĩnh kế thừa được dùng chung

Lớp con dùng chung thuộc tính tĩnh của lớp cha, trừ khi nó tự khai báo thuộc tính của riêng mình. Đây là bất ngờ kinh điển với static: hai lớp con cùng đếm vào một con số.

Apples không có $count riêng, nên Apples::$count chính là Counter::$count, và cả hai đều in ra 2. Pears khai báo lại thuộc tính và tự đếm riêng. Một thuộc tính tĩnh khai báo trong trait thì hoạt động khác: mỗi lớp dùng trait đó có một bản sao riêng.

Biến static bên trong hàm

static còn có nghĩa thứ hai bên trong thân hàm: một biến static giữ nguyên giá trị giữa các lần gọi. Giá trị khởi tạo chỉ được gán ở lần đầu tiên hàm chạy:

Lần gọi slowSquare(12) thứ hai bỏ qua bước tính toán. Giống như thuộc tính tĩnh, các giá trị này được đặt lại khi mỗi request kết thúc. Trang biến trình bày phạm vi biến nói chung.

Khi nào static là lựa chọn sai

Phương thức tĩnh rất tiện, và chính vì vậy mà dễ bị lạm dụng. Một lớp mà mọi phương thức đều đọc và ghi thuộc tính tĩnh thực chất là trạng thái toàn cục khoác thêm tên lớp: mọi nơi gọi đều dùng chung nó, test không lấy được một bản mới, và bạn không thể thay bằng một cách cài đặt khác.

Những trường hợp nên dùng static: hàm tiện ích thuần không có trạng thái (Slug::make()), hàm khởi tạo có tên (Color::fromHex()), và hằng số dùng cho cả lớp. Nếu một phương thức cần dữ liệu có thể khác nhau giữa hai lần dùng, thứ nó cần là một đối tượng.

Câu hỏi thường gặp

Làm sao gọi một phương thức tĩnh trong PHP?

Dùng tên lớp và ::: MathHelper::square(4). Bên trong cùng lớp, dùng self::square(4) hoặc static::square(4). Không cần đối tượng nào, và bên trong phương thức tĩnh không có $this.

self và static trong PHP khác nhau thế nào?

self:: luôn chỉ lớp nơi đoạn code được viết. static:: chỉ lớp mà phương thức thực sự được gọi trên đó, có thể là một lớp con; cơ chế này gọi là late static binding. Vì vậy trong phương thức create() của lớp cha, new self() luôn tạo lớp cha, còn new static() tạo ra đúng lớp con đã gọi nó.

Thuộc tính tĩnh trong PHP là gì?

Một thuộc tính khai báo public static int $count = 0; thuộc về lớp, nên chỉ có một giá trị dùng chung cho mọi đối tượng của lớp đó (và các lớp con, trừ khi lớp con khai báo lại). Đọc và ghi nó bằng ClassName::$count hoặc self::$count; chú ý dấu $.

Có thể gọi phương thức tĩnh trên một đối tượng trong PHP không?

Có. $obj->square(4) và $obj::square(4) đều gọi phương thức tĩnh của lớp của đối tượng, nhưng bên trong vẫn không có $this. Chiều ngược lại mới thất bại: gọi một phương thức không tĩnh bằng ClassName::method() sẽ ném Error từ PHP 8.

Có dùng được $this trong phương thức tĩnh không?

Không. Phương thức tĩnh chạy mà không có đối tượng, nên $this không được định nghĩa ở đó và dùng nó sẽ ném Error ("Using $this when not in object context"). Dùng self:: hoặc static:: để truy cập các thành viên tĩnh khác.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu