Một "loại giao lộ" yêu cầu một giá trị để đáp ứng nhiều ràng buộc loại thay vì một ràng buộc duy nhất
Các loại giao lộ hiện không được hỗ trợ bởi ngôn ngữ. Thay vào đó, người ta phải sử dụng chú thích phpdoc và/hoặc lạm dụng các thuộc tính đã nhập [1] như có thể thấy trong ví dụ sau
___class Test { private ?Traversable $traversable = null; private ?Countable $countable = null; /** @var Traversable&Countable */ private $both = null; public function __construct($countableIterator) { $this->traversable =& $this->both; $this->countable =& $this->both; $this->both = $countableIterator; } }Việc hỗ trợ các kiểu giao nhau trong ngôn ngữ cho phép chúng tôi di chuyển nhiều thông tin kiểu hơn từ phpdoc sang chữ ký hàm, với những lợi thế thông thường mà điều này mang lại
Các loại thực sự được thi hành, vì vậy có thể phát hiện sớm các lỗi
Bởi vì chúng được thực thi, loại thông tin ít có khả năng trở nên lỗi thời hoặc bỏ lỡ các trường hợp cạnh
Các loại được kiểm tra trong quá trình kế thừa, thực thi Nguyên tắc thay thế Liskov
Các loại có sẵn thông qua Reflection
Cú pháp ít soạn sẵn hơn nhiều so với phpdoc
Động lực
Có thể mô phỏng các loại giao lộ bằng cách tạo một giao diện mới kế thừa từ nhiều giao diện, một trường hợp như vậy là interface CountableIterator extends Iterator, Countable {}7 tích hợp mở rộng giao diện interface CountableIterator extends Iterator, Countable {}8 bằng cách thêm phương thức interface CountableIterator extends Iterator, Countable {}9 trên đó. Tuy nhiên, một trình vòng lặp cũng có thể đếm được, và nếu một hàm cần nhập theo yêu cầu như vậy thì cách duy nhất có thể hiện tại là tạo một giao diện mới
______ 1Điều này hoạt động, nhưng nếu chúng ta muốn một trình vòng lặp có thể đếm được và tìm kiếm được thì sao?
___interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}Như vậy, mỗi yêu cầu mới đòi hỏi phải tạo ra nhiều giao diện mới có tính đến tất cả các kết hợp có thể
Hơn nữa, lớp cần triển khai giao diện cụ thể và không thể chỉ dựa vào việc triển khai các giao diện cơ sở, nghĩa là việc giới thiệu các giao diện như vậy cần được phổ biến cho tất cả các lớp có liên quan, điều này có thể dễ xảy ra lỗi. Xem ví dụ này
___interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());Các loại giao lộ giải quyết những vấn đề này
đề nghị
Hỗ trợ thêm cho các loại giao lộ thuần túy được chỉ định bằng cú pháp interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}0 và có thể được sử dụng ở tất cả các vị trí hiện đang chấp nhận các loại
___________số 8Điều này có nghĩa là không thể kết hợp các loại giao lộ và liên kết với nhau, chẳng hạn như interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}1, đây là phạm vi trong tương lai
Các loại được hỗ trợ
Chỉ các loại lớp (giao diện và tên lớp) được hỗ trợ bởi các loại giao nhau
Cơ sở lý luận là đối với gần như tất cả các loại tiêu chuẩn sử dụng chúng trong một loại giao lộ sẽ dẫn đến một loại không bao giờ được thỏa mãn (e. g. ____________ 52)
Việc sử dụng interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}3 trong một loại giao lộ là không cần thiết vì interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}4 tương ứng với interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}5, vì vậy điều này không được phép
Tương tự, sử dụng interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}6 trong giao lộ dẫn đến loại không hợp lệ dư thừa, điều này có thể được nhìn thấy bằng cách mở rộng biểu thức loại interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}7
Mặc dù giao lộ với interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}8 có thể hợp lý (e. g. chuỗi&có thể gọi được), chúng tôi cho rằng điều đó là không khôn ngoan và chỉ ra một lỗi
Tương tự, interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}9, interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());0 và interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());1 khả thi về mặt kỹ thuật và có thể được sử dụng như một phần của giao lộ, nhưng áp đặt các hạn chế kỳ lạ đối với lớp con mà lớp cơ sở vi phạm hoặc lớp cơ sở đã đáp ứng các yêu cầu về loại trong trường hợp đó là dư thừa. những loại đó Do đó, ba loại cũng bị cấm vì chúng có khả năng chỉ ra một vấn đề về thiết kế
Các loại trùng lặp và dư thừa
Để bắt một số lỗi đơn giản trong khai báo kiểu giao nhau, các kiểu dư thừa có thể được phát hiện mà không thực hiện tải lớp sẽ dẫn đến lỗi thời gian biên dịch. Điêu nay bao gôm
Mỗi loại giải quyết tên chỉ có thể xảy ra một lần. Các loại như interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());2 dẫn đến lỗi
Điều này không đảm bảo rằng loại là "tối thiểu", vì làm như vậy sẽ yêu cầu tải tất cả các loại lớp đã sử dụng
Ví dụ: nếu interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());3 và interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4 là bí danh của lớp thời gian chạy, thì interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());5 vẫn là một loại giao lộ hợp pháp, mặc dù nó có thể được giảm xuống thành interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());3 hoặc interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4. Tương tự, nếu interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());8, thì interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());5 cũng là một loại giao lộ hợp pháp, mặc dù nó có thể được giảm xuống chỉ còn interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4
___interface CountableIterator extends Iterator, Countable {}9gõ ngữ pháp
Do sự mơ hồ của trình phân tích cú pháp với việc khai báo tham số by-ref trong khi sử dụng trình phân tích cú pháp LR(1) hiện tại, ngữ pháp và từ vựng được sửa đổi để tạo các mã thông báo khác nhau cho ký tự class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }1 tùy thuộc vào việc nó có được theo sau bởi một biến (biến thể) hay không
Do đó, ngữ pháp trông như sau
____________ 31phương sai
Các loại giao lộ tuân theo các quy tắc phương sai PHP tiêu chuẩn đã được sử dụng để kiểm tra kiểu và kế thừa
Các kiểu trả về là hiệp biến (con phải là kiểu con)
Các loại tham số là trái ngược (con phải là siêu kiểu)
Các loại thuộc tính là bất biến (con phải là kiểu con và siêu kiểu)
Thay đổi duy nhất là cách các loại giao lộ tương tác với phân loại, với hai quy tắc bổ sung
interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());3 là một kiểu con của class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }3 nếu với mọi class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }4, interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());3 là một kiểu con của class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }4
class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }7 là một kiểu con của interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4 nếu tồn tại một class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }9 sao cho class A { private Traversable&Countable $countableIterator; public function setIterator(Traversable&Countable $countableIterator): void { $this->countableIterator = $countableIterator; } public function getIterator(): Traversable&Countable { return $this->countableIterator; } }9 là một kiểu con của interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4
Sau đây, một số ví dụ về những gì được phép và những gì không được đưa ra
loại tài sản
Các loại thuộc tính là bất biến, có nghĩa là các loại phải giữ nguyên trong quá trình kế thừa. Tuy nhiên, loại “giống nhau” có thể được thể hiện theo những cách khác nhau
Các loại giao lộ mở rộng khả năng trong khu vực này. Ví dụ: interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());5 và interface CountableIterator extends Iterator, Countable {}93 đại diện cho cùng một loại. Ví dụ sau đây cho thấy một trường hợp phức tạp hơn
____________ 44Trong ví dụ này, giao điểm interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());5 thực sự đại diện cho cùng loại với chỉ interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4 và kế thừa này là hợp pháp, mặc dù loại không giống nhau về mặt cú pháp
Chính thức, chúng tôi đi đến kết quả này như sau. Đầu tiên, loại cha mẹ interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());5 là một loại con của interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4. Thứ hai, interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4 là một kiểu con của interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());5, bởi vì interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4 là một kiểu con của interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());3 và interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4 là một kiểu con của interface A {} interface B {} interface AB extends A, B {} class Test implements A, B {} function foo(AB $v) { var_dump($v); } foo(new Test());4
Thêm và loại bỏ các loại giao lộ
Việc thêm các loại giao lộ ở vị trí trả về và xóa các loại giao lộ ở vị trí tham số là hợp pháp
____________ 55Phương sai của các thành viên giao điểm cá nhân
Tương tự như vậy, có thể hạn chế một phần tử giao nhau ở vị trí quay lại hoặc mở rộng một phần tử giao nhau ở vị trí tham số
____________ 56Tất nhiên, điều tương tự cũng có thể được thực hiện với nhiều thành phần giao điểm cùng một lúc và được kết hợp với việc thêm/xóa các loại đã đề cập trước đó
Phương sai của loại nút giao với loại cấp bê tông
Vì mục đích sử dụng chính của các loại giao lộ là để đảm bảo nhiều giao diện được triển khai, nên một lớp cụ thể hoặc giao diện triển khai tất cả các giao diện có trong giao lộ được coi là một kiểu con và do đó có thể được sử dụng khi cho phép đồng phương sai
__________ 10Hơn nữa, có thể sử dụng một loại liên kết của các lớp/giao diện cụ thể khi mỗi thành viên của liên kết triển khai tất cả các giao diện trong giao lộ
____________ 11Lý do tại sao điều này có thể xảy ra là do sự kết hợp của các lớp/giao diện cụ thể ít tổng quát hơn tập hợp các lớp có thể thỏa mãn kiểu giao nhau
Chế độ gõ cưỡng chế
Vì các loại tiêu chuẩn không được phép trong các loại giao nhau thuần túy, nên không cần xem xét chế độ nhập cưỡng chế
Các loại tài sản và tài liệu tham khảo
Các tham chiếu đến các thuộc tính đã nhập với các loại giao nhau tuân theo ngữ nghĩa được nêu trong các thuộc tính đã nhập RFC
__________ 12Nếu các thuộc tính đã nhập là một phần của bộ tham chiếu, thì giá trị được kiểm tra đối với từng loại thuộc tính. Nếu kiểm tra loại không thành công, TypeError được tạo và giá trị của tham chiếu không thay đổi
Sự phản xạ
Để hỗ trợ các loại giao lộ, một lớp mới interface CountableIterator extends Iterator, Countable {}14 được thêm vào
__________ 13Phương thức interface CountableIterator extends Iterator, Countable {}15 trả về một mảng gồm các interface CountableIterator extends Iterator, Countable {}16 là một phần của giao lộ. Các loại có thể được trả về theo thứ tự tùy ý không khớp với khai báo loại ban đầu. Các loại cũng có thể bị biến đổi tương đương
Ví dụ: loại interface CountableIterator extends Iterator, Countable {}17 có thể trả về các loại theo thứ tự interface CountableIterator extends Iterator, Countable {}18 thay vì. Yêu cầu duy nhất đối với Reflection API là loại được đại diện cuối cùng là tương đương
Phương thức interface CountableIterator extends Iterator, Countable {}19 trả về một biểu diễn chuỗi của loại cấu thành một biểu diễn mã hợp lệ của loại trong ngữ cảnh không được đặt tên. Nó không nhất thiết phải giống như những gì đã được sử dụng trong mã gốc
ví dụ
__________ 14
Thay đổi không tương thích ngược
RFC này không chứa bất kỳ thay đổi không tương thích ngược nào
Tuy nhiên, mã dựa trên interface CountableIterator extends Iterator, Countable {}16 hiện tại có thể cần được điều chỉnh để hỗ trợ xử lý mã sử dụng các loại giao lộ
Phiên bản PHP được đề xuất
Phiên bản nhỏ tiếp theo, tôi. e. PHP8. 1
Phạm vi tương lai
Các tính năng được thảo luận sau đây không phải là một phần của đề xuất này
Các loại hỗn hợp (i. e. trộn các hiệp hội và các loại giao lộ)
Mặc dù nguyên mẫu ban đầu [2] cho thấy rằng việc hỗ trợ interface SeekableCountableIterator extends CountableIterator, SeekableIterator {}1 mà không có bất kỳ nhóm nào có vẻ khả thi, nhưng vẫn còn nhiều cân nhắc khác (e. g. phản ánh), mà cụ thể là các quy tắc phương sai và kiểm tra, sẽ tăng lên đáng kể và dễ bị lỗi
Cũng có ý kiến cho rằng các loại tổng hợp không nên dựa vào quyền ưu tiên của các công đoàn mà được nhóm lại với nhau một cách rõ ràng
Vì vậy, chúng tôi xem xét một cách tiếp cận từng bước bằng cách chỉ cho phép giao lộ thuần túy trước tiên là cách tốt nhất về phía trước
Nhập bí danh
Khi các kiểu ngày càng trở nên phức tạp, có thể đáng để cho phép sử dụng lại các khai báo kiểu. Có hai cách chung để điều này có thể hoạt động. Một là bí danh địa phương, chẳng hạn như
__________ 15Trong trường hợp này, interface CountableIterator extends Iterator, Countable {}42 là một biểu tượng chỉ hiển thị cục bộ và sẽ được phân giải thành loại interface CountableIterator extends Iterator, Countable {}43 ban đầu trong quá trình biên dịch
Khả năng thứ hai là một typedef đã xuất
___interface CountableIterator extends Iterator, Countable {}6Cần lưu ý rằng việc đưa vào đề xuất này sẽ thêm các cân nhắc bổ sung cho các bí danh loại vì có thể viết các loại hỗn hợp như thể nhóm được hỗ trợ. Tuy nhiên, cơ sở để hỗ trợ điều này có mặt trong đề xuất này