type - string php 7




Interfaces PHP 7, dicas de tipo de retorno e auto (4)

Caso você queira forçar a partir da interface, esse método retornará um objeto, mas o tipo de objeto não será o tipo de interface, mas a própria classe, e você poderá escrever desta maneira:

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

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

Está funcionando desde o PHP 7.4.

Eu encontrei um problema com o uso de dicas de tipo de retorno no PHP 7. Meu entendimento é que hinting : self significa que você pretende que uma classe de implementação retorne por si mesma. Portanto, usei : self em minhas interfaces para indicar isso, mas quando tentei realmente implementar a interface, obtive erros de compatibilidade.

A seguir, é apresentada uma demonstração simples do problema em que me deparei:

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

A saída esperada foi:

Fred Wilma Betney, Barney

O que realmente recebo é:

Erro fatal do PHP: Declaração de Foo :: bar (int $ baz): Foo deve ser compatível com iFoo :: bar (int $ baz): iFoo no test.php na linha 7

O fato é que Foo é uma implementação do iFoo, pelo que eu sei que a implementação deve ser perfeitamente compatível com a interface fornecida. Eu poderia presumivelmente corrigir esse problema alterando a interface ou a classe de implementação (ou ambas) para retornar a dica pelo nome, em vez de usar self , mas meu entendimento é que semanticamente self significa "retornar a instância da classe que você acabou de chamar de método em ". Portanto, alterá-lo para a interface significaria, em teoria, que eu poderia retornar qualquer instância de algo que implemente a interface quando minha intenção for a instância invocada.

Isso é um descuido no PHP ou uma decisão deliberada de design? Se for o primeiro, existe alguma chance de vê-lo corrigido no PHP 7.1? Caso contrário, qual é a maneira correta de retornar sugerindo que sua interface espera que você retorne a instância que acabou de chamar o método para encadeamento?


Executando alguns testes com o PHP 7.3, não consigo reclamar (mesmo com estrito) quando faço isso ...

interface A {
 function f() {}
}

interface B {
 function f():self {}
}

Meu teste foi interrompido ou o PHP mudou algumas coisas. De um modo geral, se você reduzir os possíveis tipos de retorno, isso tende a não interromper o POO. Como em qualquer classe, usar esse método pode lidar com qualquer retorno, incluindo um subconjunto de tipos de retorno. O oposto é aproximadamente o caso dos parâmetros.

Eles implementaram isso no 7.2.


Também pode ser uma solução, que você não define explicitamente o tipo de retorno na Interface, apenas no PHPDoc e pode definir o tipo de retorno certo nas implementações:

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

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

self não se refere à instância, refere-se à classe atual. Não há como uma interface especificar que a mesma instância deve ser retornada - usar self da maneira que você está tentando apenas aplicaria que a instância retornada fosse da mesma classe.

Dito isto, as declarações de tipo de retorno no PHP devem ser invariantes, enquanto o que você está tentando é covariante.

Seu uso de self é equivalente a:

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

class Foo implements iFoo
{

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

o que não é permitido.

As declarações de tipo de retorno RFC têm a dizer :

A imposição do tipo de retorno declarado durante a herança é invariável; isso significa que quando um subtipo substitui um método pai, o tipo de retorno do filho deve corresponder exatamente ao pai e não pode ser omitido. Se o pai não declarar um tipo de retorno, o filho poderá declarar um.

...

Este RFC propôs originalmente tipos de retorno covariantes, mas foi alterado para invariante devido a alguns problemas. É possível adicionar tipos de retorno covariantes em algum momento no futuro.

Por enquanto, pelo menos, o melhor que você pode fazer é:

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

class Foo implements iFoo
{

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




type-hinting