Interfaces de Objetos

Interfaces de objetos permitem a criação de códigos que especificam quais métodos uma classe deve implementar, sem definir como esses métodos serão implementados. Interfaces compartilham o namespace com classes e traits, então eles não podem usar os mesmos nomes.

Interfaces são definidas da mesma forma que classes, mas com a palavra-chave interface substituindo class e com nenhum dos métodos tendo seu conteúdo definido.

Todos os métodos declarados em uma interface devem ser públicos, essa é a natureza de uma interface.

Na prática, interfaces servem a dois propósitos distintos:

  • Elas permitem os desenvolvedores de criar objetos de classes diferentes que podem ser substituídos dado que eles implementam a mesma ou as mesmas interfaces. Um exemplo seriam os serviços variados de acesso a banco de dados, vários sistemas de pagamentos, ou estratégias de cache. Implementações diferentes podem ser substituídas sem requerer modificações nos códigos que as usam.
  • Para permitir que uma função ou método aceite e opere em um parâmetro que se molda a uma interface, ao mesmo tempo que não se importa como a funcionalidade é implementada. Estas interfaces são conhecidas como Iterable, Cacheable, Renderable, e assim por diante, e descrevem o comportamento significativo.

Interfaces podem definir métodos mágicos para exigir que as classes implementantes também implementem esses métodos.

Note:

Apesar de possível, incluir construtores em interfaces é altamente desencorajado. Fazer isso reduz significativamente a flexibilidade do objeto implementante da interface. Além disso, construtores não são verificados pelas regras de herança, o que pode causar comportamentos inconsistentes ou inesperados.

implements

Para implementar uma interface, o operador implements é utilizado. Todos os métodos na interface devem ser implementados na classe; não fazê-lo resultará em um erro fatal. Classes podem implementar mais de uma interface se assim for desejado, separando cada interface com uma vírgula.

Warning

Uma classe que implemente uma interface pode utilizar um nome diferente para seus parâmetros, em relação à interface. Entretanto, o PHP 8.0 suporta argumentos nomeados, ou seja, chamadores podem usar os nomes de parâmetros conforme definidos na interface implementada. Por essa razão, é altamente recomendado que desenvolvedores utilizem os mesmos nomes de parâmetros que da interface implementada.

Note:

Interfaces podem ser estendidas como as classes, usando o operador extends.

Note:

A classe que implementa a interface precisa declarar todos os métodos da interface com uma assinatura compatível. Uma classe pode implementar várias interfaces que declarem um método com o mesmo nome. Neste caso, a implementação precisa seguir as regras de compatibilidade de assinaturas de todas as interfaces. É possível assim aplicar covariância e contravariância.

Constantes

É possível ter constantes em interfaces. Constantes de interfaces funcionam exatamente como constantes de classes, com exceção de não podem ser sobrescritas por uma classe/interface herdeira.

Exemplos

Example #1 Exemplo de Interface

<?php

// Declara a interface 'Template'
interface Template
{
    public function setVariable($name, $var);
    public function getHtml($template);
}

// Implementa a interface
// Exemplo correto
class WorkingTemplate implements Template
{
    private $vars = [];

    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }

    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }

        return $template;
    }
}

// Exemplo incorreto
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
class BadTemplate implements Template
{
    private $vars = [];

    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
}
?>

Example #2 Interfaces estendíveis

<?php
interface A
{
    public function foo();
}

interface B extends A
{
    public function baz(Baz $baz);
}

// This will work
class C implements B
{
    public function foo()
    {
    }

    public function baz(Baz $baz)
    {
    }
}

// Isso não funcionará, e gerará um erro fatal
class D implements B
{
    public function foo()
    {
    }

    public function baz(Foo $foo)
    {
    }
}
?>

Example #3 Compatibilidade de variância com interfaces múltiplas

<?php
class Foo {}
class Bar extends Foo {}

interface A {
    public function myfunc(Foo $arg): Foo;
}

interface B {
    public function myfunc(Bar $arg): Bar;
}

class MyClass implements A, B
{
    public function myfunc(Foo $arg): Bar
    {
        return new Bar();
    }
}
?>

Example #4 Herança de várias interfaces

<?php
interface A
{
    public function foo();
}

interface B
{
    public function bar();
}

interface C extends A, B
{
    public function baz();
}

class D implements C
{
    public function foo()
    {
    }

    public function bar()
    {
    }

    public function baz()
    {
    }
}
?>

Example #5 Interfaces com constantes

<?php
interface A
{
    const B = 'Constante de interface';
}

// Imprime: Constante de interface
echo A::B;


class B implements A
{
    const B = 'Constante de classe';
}

// Imprime: Constante de classe
// Anteriormente ao PHP 8.1.0 isto não funcionaria porque não era possível
// sobrescrever constantes.
echo B::B;
?>

Example #6 Interfaces with abstract classes

<?php
interface A
{
    public function foo(string $s): string;

    public function bar(int $i): int;
}

// Uma classe abstrata pode implementar apenas uma parte da interface.
// Classes que extendam a classe abstrata precisam implementar o resto.
abstract class B implements A
{
    public function foo(string $s): string
    {
        return $s . PHP_EOL;
    }
}

class C extends B
{
    public function bar(int $i): int
    {
        return $i * 2;
    }
}
?>

Example #7 Extendendo e implementando ao mesmo tempo

<?php

class One
{
    /* ... */
}

interface Usable
{
    /* ... */
}

interface Updatable
{
    /* ... */
}

// A ordem de palavras chave aqui é importante. 'extends' precisa aprecer primeiro.
class Two extends One implements Usable, Updatable
{
    /* ... */
}
?>

Uma interface, juntamente com a declaração de tipo, fornecem uma boa maneira de garantir que um objeto em particular possua determinados métodos. Veja o operador instanceof e declaração de tipo.