PHP 7 인터페이스, 반환 유형 힌트 및 자체

업데이트: 현재 PHP 7.4는 이 질문에서 제기된 주요 문제를 해결하는 공분산위반을 지원합니다.


PHP 7에서 리턴 타입 힌트를 사용하는 것에 문제가 발생했습니다.내 이해로는 힌트는: self실장 클래스가 반환되는 것을 의미합니다.그래서 나는 사용했다.: self인터페이스를 실제로 실장하려고 했을 때 호환성 오류가 발생하였습니다.

다음은 제가 직면한 문제의 간단한 예시입니다.

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"); 

예상되는 출력은 다음과 같습니다.

프레드 윌마 바니 베티

내가 실제로 얻는 것은:

PHP 치명적 오류:Foo 선언: bar(int $baz):Foo는 테스트의 iFoo::bar(int $baz): iFoo와 호환되어야 합니다.7행의 php

문제는 Foo는 iFoo의 구현이기 때문에 구현이 주어진 인터페이스와 완벽하게 호환되어야 한다는 것입니다.인터페이스 또는 구현 클래스(또는 둘 다) 중 하나를 변경하여 인터페이스를 이름으로 힌트를 반환함으로써 이 문제를 해결할 수 있습니다.self하지만 제 이해로는 그건 의미론적으로self는 “방금 메서드를 호출한 클래스의 인스턴스를 반환한다”는 의미입니다.따라서 인터페이스를 인터페이스로 변경하는 것은 이론적으로 호출된 인스턴스에 대한 의도일 때 인터페이스를 구현하는 모든 인스턴스를 반환할 수 있다는 것을 의미합니다.

이것은 PHP의 실수입니까, 아니면 고의적인 설계 결정입니까?전자의 경우 PHP 7.1에서 수정되는 것을 볼 수 있습니까?그렇지 않은 경우, 당신의 인터페이스가 방금 메서드라고 불렀던 인스턴스를 체인을 위해 반환할 것을 암시하는 올바른 반환 방법은 무엇입니까?



질문에 대한 답변



편집자 주: 아래 답변은 구식입니다.PHP7.4.0으로서 다음 사항은 완전히 합법적입니다.

<?php Interface I{
public static function init(?string $url): self; } class C implements I{
public static function init(?string $url): self{
return new self();
} } $o = C::init("foo"); var_dump($o); 
  • 3v4l: https://3v4l.org/VYbGn

원답:

self는 인스턴스를 참조하지 않고 현재 클래스를 참조합니다.인터페이스를 사용하여 동일한 인스턴스를 반환하도록 지정할 수 없습니다.self반환된 인스턴스가 동일한 클래스의 인스턴스여야 합니다.

즉, PHP의 반환 유형 선언은 불변이어야 하며, 공변형이어야 합니다.

사용방법self는 다음과 같습니다.

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

그건 허용되지 않습니다.


반환 유형 선언 RFC에는 다음과 같이 기재되어 있습니다.

상속 중 선언된 반환 유형의 시행은 변경되지 않습니다. 즉, 하위 유형이 상위 메서드를 재정의하는 경우 하위 유형의 반환 유형은 상위 메서드와 완전히 일치해야 하며 생략할 수 없습니다.부모가 반환 유형을 선언하지 않으면 자녀가 반환 유형을 선언할 수 있습니다.

이 RFC는 당초 공변 반환 유형을 제안했지만 몇 가지 문제로 인해 불변으로 변경되었습니다.향후 어느 시점에 공변량 반환 유형을 추가할 수 있습니다.


당분간은 최소한 다음을 수행할 수 있습니다.

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



또, 인터페이스내에서 명시적으로 반환 타입을 정의하지 않고, PHPDoc에서만 특정의 반환 타입을 실장내에서 정의할 수도 있습니다.

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



PHP 8은 문제를 해결하는 “static return type”을 추가합니다.

다음 RFC를 확인해 주세요.https://wiki.php.net/rfc/static_return_type




내가 보기엔 이건 예상한 행동이야.

변경만 하면 됩니다.Foo::bar반환 방법iFoo대신self끝장낼 수갑시다

설명:

self인터페이스에서 사용되는 것은 “유형의 객체”를 의미합니다.iFoo.”
self구현에서 사용되는 것은 “유형의 객체”를 의미합니다.Foo.”

따라서 인터페이스와 구현의 리턴 타입은 분명히 같지 않습니다.

댓글 중 하나에 Java와 이 문제가 발생하는지 여부가 기재되어 있습니다.답은 ‘그렇다’입니다. Java가 코드를 쓸있도록 허용했다면 같은 문제가 생겼을 것입니다. 하지만 그렇지 않습니다.자바에서는 PHP의 이름 대신 타입의 이름을 사용해야 하기 때문에self숏컷은 실제로 볼 수 없습니다.(Java에서의 유사한 문제에 대한 자세한 내용은 여기를 참조하십시오.)