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

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

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



답변

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


답변

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

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

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

PHP 7.4부터 작동합니다.


답변

PHP 8은 문제를 해결할 “정적 반환 유형”을 추가합니다.

이 RFC를 확인하십시오 : https://wiki.php.net/rfc/static_return_type


답변

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

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

설명:

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

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

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


답변