PHP trước

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 {}
9

gõ 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

____________ 31

phươ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

____________ 44

Trong 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

____________ 55

Phươ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ố

____________ 56

Tấ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

__________ 10

Hơ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ộ

____________ 11

Lý 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

Nế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

__________ 12

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

__________ 13

Phươ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ư

__________ 15

Trong 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 {}
6

Cầ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

Ý nghĩa thuần khiết trong PHP là gì?

Hàm thuần túy là hàm chứa cả hai câu lệnh này về hàm. Hàm luôn đánh giá cùng một giá trị kết quả với (các) giá trị đối số giống nhau .

ArrayShape PHP là gì?

các hàm php. Và #[ArrayShape] là sự phát triển rất được mong đợi của mô tả mảng của PHPDoc . Cũng sẽ có #[Không dùng nữa] , #[Pure] và #[Immutable].

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

Thuộc tính là gì. Thuộc tính là các phần tử siêu dữ liệu nhỏ được thêm vào cho các lớp PHP, hàm, bao đóng, thuộc tính lớp, phương thức lớp, hằng số và thậm chí trên các lớp ẩn danh . Nhận xét PHP DocBlock có lẽ là ví dụ quen thuộc nhất. Những nhận xét này phần nào được cấu trúc với "chú thích" @param.