Đó là một câu chuyện dài và buồn.
Khi PHP 5.2 lần đầu tiên đưa ra cảnh báo này, các liên kết tĩnh muộn vẫn chưa có trong ngôn ngữ này. Trong trường hợp bạn không quen với các ràng buộc tĩnh muộn, hãy lưu ý rằng mã như thế này không hoạt động theo cách bạn có thể mong đợi:
<?php
abstract class ParentClass {
static function foo() {
echo "I'm gonna do bar()";
self::bar();
}
abstract static function bar();
}
class ChildClass extends ParentClass {
static function bar() {
echo "Hello, World!";
}
}
ChildClass::foo();
Bỏ qua cảnh báo chế độ nghiêm ngặt, mã trên không hoạt động. Lời self::bar()
gọi trong foo()
đề cập đến bar()
phương thức của ParentClass
, ngay cả khi foo()
được gọi như một phương thức của ChildClass
. Nếu bạn cố gắng chạy mã này với chế độ nghiêm ngặt đang tắt, bạn sẽ thấy " Lỗi nghiêm trọng PHP: Không thể gọi phương thức trừu tượng ParentClass :: bar () ".
Do đó, các phương thức tĩnh trừu tượng trong PHP 5.2 là vô dụng. Các toàn bộ điểm của việc sử dụng một phương pháp trừu tượng là bạn có thể viết mã mà các cuộc gọi phương pháp này mà không biết những gì thực hiện nó sẽ được gọi điện thoại - và sau đó cung cấp triển khai khác nhau trên lớp con khác nhau. Nhưng vì PHP 5.2 không cung cấp cách nào rõ ràng để viết một phương thức của lớp cha gọi một phương thức tĩnh của lớp con mà nó được gọi, nên việc sử dụng các phương thức tĩnh trừu tượng này là không thể. Do đó, bất kỳ cách sử dụng nào abstract static
trong PHP 5.2 đều là mã xấu, có thể được lấy cảm hứng từ sự hiểu nhầm về cách self
hoạt động của từ khóa. Hoàn toàn hợp lý khi đưa ra một cảnh báo về điều này.
Nhưng sau đó PHP 5.3 được bổ sung thêm khả năng tham chiếu đến lớp mà một phương thức được gọi thông qua static
từ khóa (không giống như self
từ khóa, luôn đề cập đến lớp mà phương thức được định nghĩa ). Nếu bạn thay đổi self::bar()
thành static::bar()
trong ví dụ của tôi ở trên, nó hoạt động tốt trong PHP 5.3 trở lên. Bạn có thể đọc thêm về self
vs static
tại New self vs. new static .
Với từ khóa static được thêm vào, lập luận rõ ràng về việc abstract static
đưa ra cảnh báo đã không còn nữa. Mục đích chính của late static bindings là cho phép các phương thức được định nghĩa trong lớp cha gọi các phương thức tĩnh sẽ được định nghĩa trong các lớp con; cho phép các phương thức tĩnh trừu tượng có vẻ hợp lý và nhất quán với sự tồn tại của các ràng buộc tĩnh muộn.
Tôi đoán bạn vẫn có thể tạo ra một trường hợp để giữ cảnh báo. Ví dụ, bạn có thể tranh luận rằng kể từ PHP cho phép bạn gọi các phương thức tĩnh của lớp trừu tượng, trong ví dụ của tôi ở trên (ngay cả sau khi sửa chữa nó bằng cách thay thế self
với static
) bạn đang phơi bày một phương pháp nào ParentClass::foo()
đó được phá vỡ và rằng bạn không thực sự muốn lộ ra. Sử dụng một lớp non-static - nghĩa là, tạo tất cả các phương thức instance của phương thức và làm cho phần con của ParentClass
tất cả chúng trở thành singleleton hoặc một cái gì đó - sẽ giải quyết vấn đề này, vì ParentClass
nó là trừu tượng, không thể được khởi tạo và vì vậy các phương thức instance của nó không thể được gọi là. Tôi nghĩ lập luận này là yếu (bởi vì tôi nghĩ rằngParentClass::foo()
không phải là một vấn đề lớn và việc sử dụng các lớp đơn thay vì các lớp tĩnh thường không cần thiết phải dài dòng và xấu xí), nhưng bạn có thể không đồng ý một cách hợp lý - đó là một cách gọi hơi chủ quan.
Vì vậy, dựa trên lập luận này, các nhà phát triển PHP đã giữ cảnh báo trong ngôn ngữ, phải không?
Uh, không chính xác .
Báo cáo lỗi PHP 53081, được liên kết ở trên, đã yêu cầu loại bỏ cảnh báo vì việc bổ sung static::foo()
cấu trúc đã làm cho các phương thức tĩnh trừu tượng trở nên hợp lý và hữu ích. Rasmus Lerdorf (người tạo ra PHP) bắt đầu bằng cách gắn nhãn yêu cầu là không có thật và trải qua một chuỗi dài các lý do tồi tệ để cố gắng biện minh cho cảnh báo. Sau đó, cuối cùng, cuộc trao đổi này diễn ra:
Giorgio
tôi biết nhưng:
abstract class cA
{
//static function A(){self::B();} error, undefined method
static function A(){static::B();} // good
abstract static function B();
}
class cB extends cA
{
static function B(){echo "ok";}
}
cB::A();
Rasmus
Đúng, đó chính xác là cách nó sẽ hoạt động.
Giorgio
nhưng nó không được phép :(
Rasmus
Những gì không được phép?
abstract class cA {
static function A(){static::B();}
abstract static function B();
}
class cB extends cA {
static function B(){echo "ok";}
}
cB::A();
Điều này hoạt động tốt. Rõ ràng là bạn không thể gọi self :: B (), nhưng static :: B () thì ổn.
Tuyên bố của Rasmus rằng mã trong ví dụ của anh ta "hoạt động tốt" là sai; như bạn biết, nó đưa ra một cảnh báo chế độ nghiêm ngặt. Tôi đoán anh ấy đang kiểm tra mà không bật chế độ nghiêm ngặt. Bất chấp điều đó, một Rasmus bối rối đã khiến yêu cầu bị đóng lại một cách sai lầm là "không có thật".
Và đó là lý do tại sao cảnh báo vẫn còn bằng ngôn ngữ. Đây có thể không phải là một lời giải thích hoàn toàn thỏa mãn - bạn có thể đến đây với hy vọng có một lời biện minh hợp lý cho lời cảnh báo. Thật không may, trong thế giới thực, đôi khi những lựa chọn được sinh ra từ những sai lầm trần tục và những suy luận tồi tệ hơn là từ những quyết định hợp lý. Đây chỉ đơn giản là một trong những thời điểm đó.
May mắn thay, Nikita Popov có thể ước tính đã xóa cảnh báo khỏi ngôn ngữ trong PHP 7 như một phần của PHP RFC: Phân loại lại các thông báo E_STRICT . Cuối cùng, sự tỉnh táo đã chiếm ưu thế và một khi PHP 7 được phát hành, tất cả chúng ta có thể vui vẻ sử dụng abstract static
mà không nhận được cảnh báo ngớ ngẩn này.