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


89

업데이트 : 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)와 호환되어야합니다 : 7 행의 test.php의 iFoo

문제는 Foo가 iFoo의 구현이므로 구현이 주어진 인터페이스와 완벽하게 호환되어야한다고 말할 수 있습니다. 아마도 인터페이스 나 구현 클래스 (또는 둘 다)를 변경하여 인터페이스를 사용하는 대신 이름으로 힌트를 반환하여이 문제를 해결할 수 self있지만, 내 이해는 의미 상 self"방금 메서드를 호출 한 클래스의 인스턴스를 반환" 을 의미 한다는 것입니다. ". 따라서 인터페이스로 변경하면 이론적으로 호출 된 인스턴스에 대한 의도가 반환 될 때 인터페이스를 구현하는 모든 인스턴스를 반환 할 수 있습니다.

이것은 PHP에 대한 감독입니까, 아니면 의도적 인 설계 결정입니까? 전자의 경우 PHP 7.1에서 수정 된 것을 볼 수 있습니까? 그렇지 않다면 인터페이스가 연결을 위해 방금 호출 한 인스턴스를 반환 할 것으로 예상한다는 것을 암시하는 올바른 반환 방법은 무엇입니까?


PHP return type-hinting의 오류라고 생각합니다. 아마도 버그 로 제기해야합니다 . 그러나이 후반기에 PHP 7.1에 어떠한 수정도 적용되지 않을 것 같습니다
Mark Baker

7.1의 마지막 베타 버전이 며칠 전에 온라인 상태가 되었기 때문에 7.1에 수정 사항이 적용될 가능성은 거의 없습니다.
Charlotte Dunois

흥미롭게도 self반환 유형이 작동 하는 방식에 대한 해석을 어디에서 읽고 있습니까?
Adam Cameron

@Adam : self"이를 호출 한 인스턴스를 반환하고 동일한 인터페이스를 구현하는 다른 인스턴스를 반환하지 않음"을 의미 하는 것이 논리적으로 보입니다 . Java가 비슷한 반환 유형을 가졌음을 기억하는 것 같습니다 (자바 프로그래밍을 한 지
오래되었지만

1
안녕 고든. 어딘가에서 작동하도록 문서화되어 있지 않으면 논리적 인 것이 현실이라고 믿지 않을 것입니다. 내가 설명하는 상황에서 TBH는 가능한 한 선언적이며 반환 유형으로 iFoo를 사용합니다. 실제로 작동하지 않는 상황이 있습니까? (나는 말했다, 이것이 "조언"가 아니라 "대답"이외 / 의견을 알고 있습니다.
아담 카메론에게

답변:


94

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

18
그게 내가의 반환 형식 - 힌트 예상했을 말했다 static일에,하지만조차 인식되지 않습니다
마크 베이커

나는 그것이 사실이 될 것이라고 생각했고 그것이 문제를 해결하는 방법입니다. 그러나 클래스가 인터페이스를 구현하는 경우 self의 반환 값도 암시 적으로 인터페이스 인스턴스의 반환 값이기 때문에 가능하면 : self를 사용하는 것을 선호했습니다.
GordonM

19
Paul, 여기서 삭제 한 댓글을 삭제하는 것은 (A) 중요한 정보를 잃고 (B) 다른 댓글과 관련된 토론 흐름을 방해하기 때문에 실제로 해 롭습니다. Mark와 Gordon에 의존하는 귀하의 의견을 삭제해야하는 이유를 알 수 없습니다. 사실, 당신은이 일을 모든 곳에서하고 있고, 그만둬야합니다. 1 년 된 질문으로 돌아가서 모든 의견을 제거하여 토론의 흐름을 완전히 파괴 할 이유가 전혀 없습니다. 사실, 그것은 해롭고 파괴적입니다.
Cody Gray

여기에 표시된 인용문의 일부에 대한 중요한 서문이 있습니다. " 공변 반환 유형은 유형 사운드로 간주되며 다른 많은 언어에서 사용됩니다 (C ++ 및 Java이지만 C #은 아님).이 RFC는 원래 공변 반환 유형을 제안했지만 몇 가지 문제로 인해 불변으로 변경되었습니다. " PHP에 어떤 문제가 있는지 궁금합니다. 그들의 디자인 선택은 특성에 이상한 문제를 일으키는 몇 가지 제한을 유발하여 반환 유형 제한을 풀지 않는 한 (아래 답변에서 볼 수 있듯이) 많은 경우에 다소 쓸모 없게 만듭니다. 매우 실망 스럽습니다.
John Pancoast

1
@MarkBaker 반환 유형 static이 PHP 8에 추가되고 있습니다.
Ricardo Boss

16

또한 인터페이스에서 반환 유형을 명시 적으로 정의하지 않고 PHPDoc에서만 명시 적으로 정의한 다음 구현에서 특정 반환 유형을 정의 할 수있는 솔루션이 될 수 있습니다.

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

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

2
또는 대신에 Foo단지 사용 self.
대신

2

인터페이스에서 강제로 사용하려는 경우 해당 메소드는 객체를 반환하지만 객체 유형은 인터페이스 유형이 아니라 클래스 자체가 될 경우 다음과 같이 작성할 수 있습니다.

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

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

PHP 7.4부터 작동합니다.



0

이것은 나에게 예상되는 동작처럼 보입니다.

대신 Foo::bar반환하도록 방법을 변경 하고 수행하십시오.iFooself

설명:

self인터페이스에서 사용 된 것처럼 "유형의 객체"를 의미합니다 iFoo.
self구현에 사용 된대로 "유형의 객체"를 의미합니다 Foo.

따라서 인터페이스의 반환 유형과 구현은 분명히 동일하지 않습니다.

댓글 중 하나는 Java와이 문제가 있는지 여부를 언급합니다. 대답은 그렇습니다. Java 가 이와 같은 코드를 작성하도록 허용했다면 동일한 문제가 발생 합니다. Java는 PHP의 self바로 가기 대신 유형의 이름을 사용해야하므로 실제로는 이것을 볼 수 없습니다. ( Java 의 유사한 문제에 대한 논의는 여기 를 참조 하십시오 .)


그래서 선언은 선언 self과 유사 MyClass::class합니까?
peterchaula

1
@Laser 네, 그렇습니다.
Moshe Katz

4
푸 구현은 다음 iFoo하지만 푸 타입 iFoo의 정의입니다
GordonM
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.