Giao diện PHP 7, gợi ý kiểu trả về và tự


89

CẬP NHẬT : PHP 7.4 hiện hỗ trợ hiệp phương sai và phương sai giải quyết vấn đề chính được nêu ra trong câu hỏi này.


Tôi đã gặp phải vấn đề gì đó với việc sử dụng hàm trả về kiểu gợi ý trong PHP 7. Tôi hiểu rằng gợi ý : selfcó nghĩa là bạn dự định cho một lớp triển khai tự trả về. Do đó, tôi đã sử dụng : selftrong các giao diện của mình để chỉ ra điều đó, nhưng khi tôi cố gắng triển khai thực tế giao diện, tôi đã gặp lỗi tương thích.

Sau đây là một minh chứng đơn giản về vấn đề tôi gặp phải:

interface iFoo
{
    public function bar (string $baz) : self;
}

class Foo implements iFoo
{

    public function bar (string $baz) : self
    {
        echo $baz . PHP_EOL;
        return $this;
    }
}

(new Foo ()) -> bar ("Fred") 
    -> bar ("Wilma") 
    -> bar ("Barney") 
    -> bar ("Betty");

Sản lượng mong đợi là:

Fred Wilma Barney Betty

Những gì tôi thực sự nhận được là:

PHP Lỗi nghiêm trọng: Khai báo Foo :: bar (int $ baz): Foo phải tương thích với iFoo :: bar (int $ baz): iFoo trong test.php trên dòng 7

Vấn đề là Foo là một triển khai của iFoo, theo như tôi có thể nói việc triển khai phải hoàn toàn tương thích với giao diện nhất định. Tôi có lẽ có thể khắc phục sự cố này bằng cách thay đổi giao diện hoặc lớp triển khai (hoặc cả hai) để trả về gợi ý giao diện theo tên thay vì sử dụng self, nhưng hiểu biết của tôi là về mặt ngữ selfnghĩa "trả về phiên bản của lớp mà bạn vừa gọi phương thức trên ". Do đó, việc thay đổi nó thành giao diện có nghĩa là về lý thuyết, tôi có thể trả về bất kỳ trường hợp nào của thứ gì đó triển khai giao diện khi ý định của tôi dành cho thể hiện được gọi là thứ sẽ được trả về.

Đây là một sự giám sát trong PHP hay đây là một quyết định thiết kế có chủ ý? Nếu là bản cũ thì liệu có cơ hội thấy nó được sửa trong PHP 7.1 không? Nếu không thì đâu là cách trả về chính xác gợi ý rằng giao diện của bạn mong muốn bạn trả về thể hiện mà bạn vừa gọi phương thức trên để xâu chuỗi?


Tôi nghĩ rằng đó là một lỗi trong gợi ý kiểu trả về PHP, có lẽ bạn nên nâng nó lên như một lỗi ; nhưng bất kỳ sửa chữa khó có thể nhận được vào PHP 7.1 ở giai đoạn cuối này
Mark Baker

Vì phiên bản beta cuối cùng của 7.1 đã xuất hiện trực tuyến cách đây vài ngày, nên rất khó có khả năng bất kỳ bản sửa lỗi nào xảy ra với 7.1.
Charlotte Dunois

Không quan tâm, bạn đang đọc phần giải thích của mình về cách hoạt động của selfkiểu trả về?
Adam Cameron

@Adam: Có vẻ như nó hợp lý selfkhi có nghĩa là "Trả lại phiên bản mà bạn đã gọi cái này, chứ không phải một số phiên bản khác triển khai cùng một giao diện". Tôi nhớ hình như Java đã có một kiểu trả về tương tự (mặc dù nó được một lúc kể từ khi tôi đã làm bất kỳ lập trình Java)
GordonM

1
Chào Gordon. Chà, trừ khi nó được ghi lại là hoạt động ở đâu đó, tôi sẽ không tin điều gì có thể hợp lý là hiện thực. TBH với tình huống tôi mô tả, tôi chỉ cần khai báo hết mức có thể và sử dụng iFoo làm kiểu trả về. Có tình huống nào mà điều đó sẽ không thực sự hoạt động không? (Tôi nhận ra đây là "lời khuyên" / ý kiến chứ không phải là "một câu trả lời", mà nói.
Adam Cameron

Câu trả lời:


94

selfkhông tham chiếu đến cá thể, nó tham chiếu đến lớp hiện tại. Không có cách nào để một giao diện chỉ định rằng phải trả lại cùng một phiên bản - sử dụng selftheo cách bạn đang cố gắng sẽ chỉ thực thi rằng phiên bản trả về phải thuộc cùng một lớp.

Điều đó nói rằng, các khai báo kiểu trả về trong PHP phải là bất biến trong khi những gì bạn đang cố gắng là đồng biến.

Việc sử dụng của bạn selftương đương với:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

mà không được phép.


Các Return khai báo kiểu RFCnày để nói :

Việc thực thi kiểu trả về đã khai báo trong quá trình kế thừa là bất biến; điều này có nghĩa là khi một kiểu con ghi đè một phương thức cha thì kiểu trả về của con phải khớp chính xác với phương thức cha và không thể bị bỏ qua. Nếu cha mẹ không khai báo kiểu trả về thì con được phép khai báo một kiểu.

...

RFC này ban đầu đề xuất các kiểu trả về hiệp biến nhưng đã được thay đổi thành bất biến vì một số vấn đề. Có thể thêm các kiểu trả về hiệp phương sai vào một thời điểm nào đó trong tương lai.


Trong lúc này, điều tốt nhất bạn có thể làm là:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}

18
Điều đó nói rằng, tôi dự kiến sẽ có sự quay trở lại kiểu gợi ý staticđể làm việc, nhưng nó thậm chí không được công nhận
Mark Baker

Tôi đã nghĩ rằng đó sẽ là trường hợp, và đó là cách tôi sẽ giải quyết vấn đề. Tuy nhiên, tôi sẽ thích chỉ sử dụng: self nếu có thể vì nếu một lớp đang triển khai một giao diện thì giá trị trả về của self cũng mặc nhiên là giá trị trả về của một phiên bản của giao diện.
GordonM

19
Paul, việc xóa các bình luận bạn đã xóa ở đây thực sự có hại vì (A) nó làm mất thông tin quan trọng và (B) nó làm gián đoạn luồng thảo luận so với các bình luận khác. Tôi không thấy lý do gì khiến nhận xét của bạn dựa vào Mark và Gordon lại cần bị xóa. Trên thực tế, bạn đang làm điều này khắp nơi và nó cần phải dừng lại. Hoàn toàn không có lý do chính đáng nào để quay lại câu hỏi cũ và xóa tất cả các bình luận của bạn, phá hủy hoàn toàn luồng thảo luận. Trong thực tế, nó có hại và gây rối.
Cody Grey

Có một lời nói đầu quan trọng cho một phần trích dẫn của bạn được xem ở đây: " Kiểu trả về hiệp phương sai được coi là kiểu âm thanh và được sử dụng trong nhiều ngôn ngữ khác (C ++ và Java nhưng không phải C #, tôi tin rằng). RFC ban đầu đề xuất kiểu trả về hiệp phương sai này nhưng đã được thay đổi thành bất biến vì một số vấn đề. ". Tôi tò mò về những vấn đề mà PHP gặp phải. Lựa chọn thiết kế của họ gây ra một số hạn chế cũng gây ra các vấn đề kỳ lạ với các đặc điểm, khiến chúng trở nên vô dụng trong nhiều trường hợp trừ khi bạn nới lỏng các hạn chế kiểu trả về (như được thấy trong một số câu trả lời bên dưới). Rất bực bội.
John Pancoast

1
@MarkBaker kiểu trả về staticđang được thêm vào PHP 8.
Ricardo Boss

16

Nó cũng có thể là một giải pháp mà bạn không xác định rõ ràng kiểu trả về trong Giao diện, chỉ trong PHPDoc và sau đó bạn có thể xác định kiểu trả về nhất định trong các triển khai:

interface iFoo
{
    public function bar (string $baz);
}

class Foo implements iFoo
{
    public function bar (string $baz) : Foo  {...}
}

2
Hoặc thay vì Foochỉ sử dụng self.
thay vào đó

2

Trong trường hợp, khi Bạn muốn buộc từ giao diện, phương thức đó sẽ trả về đối tượng, nhưng kiểu đối tượng sẽ không phải là kiểu giao diện mà là chính lớp, khi đó Bạn có thể viết theo cách này:

interface iFoo {
    public function bar (string $baz) : object;
}

class Foo implements iFoo {
    public function bar (string $baz) : self  {...}
}

Nó hoạt động kể từ PHP 7.4.



0

Đây có vẻ như là hành vi mong đợi đối với tôi.

Chỉ cần thay đổi Foo::barphương thức của bạn để trả về iFoothay vì selfvà được thực hiện với nó.

Giải trình:

selfnhư được sử dụng trong giao diện có nghĩa là "một loại đối tượng iFoo."
selfnhư được sử dụng trong việc triển khai có nghĩa là "một đối tượng của kiểu Foo".

Do đó, các kiểu trả về trong giao diện và cách thực hiện rõ ràng là không giống nhau.

Một trong những nhận xét đề cập đến Java và liệu bạn có gặp vấn đề này hay không. Câu trả lời là có, bạn sẽ gặp vấn đề tương tự nếu Java cho phép bạn viết mã như vậy - điều mà nó không. Vì Java yêu cầu bạn sử dụng tên của kiểu thay vì selfphím tắt của PHP , bạn sẽ không bao giờ thực sự thấy điều này. (Xem tại đây để thảo luận về một vấn đề tương tự trong Java.)


Như vậy là khai báo self, tương tự như khai báo MyClass::class?
peterchaula

1
@Laser Vâng, đúng như vậy.
Moshe Katz

4
Nhưng nếu Foo cụ IFoo sau đó Foo là theo định nghĩa của loại IFoo
GordonM
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.