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.