O básico

class

A definição de uma classe começa com a palavra chave class, seguida do nome da classe, seguido de um par de chaves que englobam as definições de propriedades e métodos pertencentes à classe.

O nome de uma classe tem de ser válido, que não seja uma palavra reservada do PHP. Um nome de classe válido começa com uma letra ou sublinhado, seguido de qualquer sequência de letras, números e sublinhados. Como uma expressão regular, pode ser expressada assim: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$.

Uma classe pode conter suas próprias constantes, variáveis (chamadas de "propriedades") e funções (chamadas de "métodos").

Example #1 Definição Simples de uma Classe

<?php
class SimpleClass
{
    // declaração de propriedade
    public $var = 'um valor padrão';

    // declaração de método
    public function displayVar() {
        echo $this->var;
    }
}
?>

A pseudo-variável $this está disponível quando um método é chamado a partir de um contexto de um objeto. $this é o valor do objeto chamado.

Warning

Chamar um método não estático de maneira estática lança um Error. Anteriormente ao PHP 8.0.0, isto iria gerar um aviso de descontinuação, e $this estaria indefinido.

Example #2 Alguns exemplos da pseudo variável $this

<?php
class A
{
    function foo()
    {
        if (isset($this)) {
            echo '$this está definida (';
            echo get_class($this);
            echo ")\n";
        } else {
            echo "\$this não está definida.\n";
        }
    }
}

class B
{
    function bar()
    {
        A::foo();
    }
}

$a = new A();
$a->foo();

A::foo();

$b = new B();
$b->bar();

B::bar();
?>

Saída do exemplo acima no PHP 7:

$this está definida (A)

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 27
$this não está definida.

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 20
$this não está definida.

Deprecated: Non-static method B::bar() should not be called statically in %s  on line 32

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 20
$this não está definida.

Saída do exemplo acima no PHP 8:

$this está definida (A)

Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27
Stack trace:
#0 {main}
  thrown in %s  on line 27

Classes somente leitura

A partir do PHP 8.2.0, uma classe pode ser marcada com o modificador readonly. Marcar uma classe como readonly irá acrescentar o modificador readonly em cada propriedade declarada, e prevenir a criação de propriedades dinâmicas. Além disso será impossível de acrescentar suportes a propriedades dinâmicas utilizando o atributo AllowDynamicProperties. Acrescentar o atributo em classes somente leitura irá disparar um erro de compilação.

<?php
#[\AllowDynamicProperties]
readonly class Foo {
}

// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>

Como as propriedades estáticas ou sem tipo não podem ser marcadas com o modificador readonly, as classes somente leitura também não podem declará-las:

<?php
readonly class Foo
{
    public $bar;
}

// Fatal error: Readonly property Foo::$bar must have type
?>
<?php
readonly class Foo
{
    public static int $bar;
}

// Fatal error: Readonly class Foo cannot declare static properties
?>

Uma classe readonly pode ser estendida se, e somente se, a classe filha também ser uma classe readonly.

new

Para criar uma instância de uma classe, a instrução new deve ser utilizada. Um objeto sempre será criado a não ser que a classe tenha um construtor definido que dispare uma exceção em caso de erro. Classes devem ser definidas antes de instanciadas (e em alguns casos isso é obrigatório).

Se uma variável contendo uma string com o nome da classe for utilizado com new, uma nova instância da classe será criada. Se a classe estiver dentro de um namespace, seu nome qualificado completo deve ser utilizado ao fazer isto.

Note:

Se não há argumentos a serem passados para o construtor da classe, os parênteses após o nome da classe podem ser omitidos.

Example #3 Criando uma instância

<?php
$instance = new SimpleClass();

// Também pode ser feito com uma variável:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
?>

A partir do PHP 8.0.0, usar new com expressões arbitrárias é suportado. Isso permite instanciação mais complexa se a expressão produzir uma string. As expressões devem ser envolvidas em parênteses.

Example #4 Criando uma instância usando uma expressão arbitrária

No exemplo fornecido mostramos múltiplos exemplos de expressões arbitrárias válidas que produzem um nome de classe. Isto mostra uma chamada para uma função, concatenação de string, e a constante ::class.

<?php

class ClasseA extends \stdClass {}
class ClasseB extends \stdClass {}
class ClasseC extends ClasseB {}
class ClasseD extends ClasseA {}

function obterAlgumaClasse(): string
{
    return 'ClasseA';
}

var_dump(new (obterAlgumaClasse()));
var_dump(new ('Classe' . 'B'));
var_dump(new ('Classe' . 'C'));
var_dump(new (ClasseD::class));
?>

Saída do exemplo acima no PHP 8:

object(ClasseA)#1 (0) {
}
object(ClasseB)#1 (0) {
}
object(ClasseC)#1 (0) {
}
object(ClasseD)#1 (0) {
}

No contexto da classe, é possível criar um novo objeto com new self e new parent.

Ao atribuir uma instância de uma classe já criada, a uma variável nova, a variável nova irá acessar a mesma instância do objeto que foi atribuído. Este comportamento se mantém ao se passar instâncias a uma função. Uma cópia de um objeto criado pode ser feita clonando o mesmo.

Example #5 Atribuição de Objetos

<?php

$instance = new SimpleClass();

$assigned   =  $instance;
$reference  =& $instance;

$instance->var = '$assigned terá esse valor';

$instance = null; // $instance e $reference tornam-se nulos

var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>

O exemplo acima produzirá:

NULL
NULL
object(SimpleClass)#1 (1) {
   ["var"]=>
     string(30) "$assigned terá esse valor"
}

É possível usar algumas formas de criar instâncias de um objeto:

Example #6 Criando novos objetos

<?php

class Test
{
    public static function getNew()
    {
        return new static();
    }
}

class Child extends Test {}

$obj1 = new Test();  // Usando o nome da classe
$obj2 = new $obj1(); // Usando a variável que contém o objeto
var_dump($obj1 !== $obj2);

$obj3 = Test::getNew();   // Usando o método da classe
var_dump($obj3 instanceof Test);

$obj4 = Child::getNew();  // Usando o método através da classe filha
var_dump($obj4 instanceof Child);

?>

O exemplo acima produzirá:

bool(true)
bool(true)
bool(true)

É possível acessar um membro do objeto recém criado em uma expressão simples:

Example #7 Acessando um membro de um novo objeto criado

<?php
echo (new DateTime())->format('Y');
?>

O exemplo acima produzirá algo semelhante a:

2016

Note: Anteriormente ao PHP 7.1, os argumentos não são avaliados se não houver um construtor definido.

Propriedades e métodos

Propriedades e métodos de classe vivem em "namespaces" separados, de forma que é possível ter uma propriedade e método com mesmos nomes. A referência a propriedades e métodos tem a mesma notação, e a decisão de se uma propriedade será acessada ou uma chamada a um método feita, depende somente do contexto, ou seja, se está tentando acessar uma variável ou chamar um método.

Example #8 Acesso a propriedade vs. chamar um método

<?php
class Foo
{
    public $bar = 'propriedade';

    public function bar() {
        return 'métod';
    }
}

$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;

O exemplo acima produzirá:

propriedade
método

Isto significa que chamar diretamente uma função anônima atribuída a uma propriedade não é possível. Em vez disso, por exemplo, a propriedade deve primeiro ser atribuída a uma variável. É possível chamar uma propriedade diretamente colocando-a entre parênteses.

Example #9 Chamando uma função anônima armazenada em uma propriedade

<?php
class Foo
{
    public $bar;

    public function __construct() {
        $this->bar = function() {
            return 42;
        };
    }
}

$obj = new Foo();

echo ($obj->bar)(), PHP_EOL;

O exemplo acima produzirá:

42

extends

Uma classe pode herdar constantes, métodos e propriedades de outra classe usando a palavra-chave extends na declaração da classe. Não é possível herdar múltiplas classes; uma classe só pode herdar uma classe base.

Os métodos e propriedades herdados podem ser sobrescritos declarando-os com o mesmo nome definido na classe base. Entretanto, se a classe mãe definiu um método ou constante como final, eles não poderão ser sobrescritos. É possível acessar os métodos sobrescritos ou propriedades estáticas referenciado-os com parent::.

Note: A partir do PHP 8.1.0, constantes podem ser declaradas como finais.

Example #10 Herança simples de classe

<?php
class ExtendClass extends SimpleClass
{
    // Redefine o método pai
    function displayVar()
    {
        echo "Classe Herdeira\n";
        parent::displayVar();
    }
}

$extended = new ExtendClass();
$extended->displayVar();
?>

O exemplo acima produzirá:

Classe Herdeira
um valor padrão

Regras de compatibilidade de assinaturas

Ao sobrescrever um método, sua assinatura precisa ser compatível com a do método original. Caso contrário um erro fatal é emitido, ou, antes do PHP 8.0.0, um alerta E_WARNING seria gerado. Uma assinatura é compatível se ela respeita as regras de variância, se ela transforma um parâmetro obrigatório em opcional, se ela adiciona novos parâmetros opcionais, e se ela não restringe ou apenas aumenta a visibilidade. Isso é conhecido como Princípio de Substituição de Liskov, ou LSP. O construtor, e membros private não precisam seguir essas regras de compatibilidade, e portanto não há emissão de erros fatais no caso de assinaturas incompatíveis.

Example #11 Métodos compatíveis

<?php

class Base
{
    public function foo(int $a) {
        echo "Valid\n";
    }
}

class Extend1 extends Base
{
    function foo(int $a = 5)
    {
        parent::foo($a);
    }
}

class Extend2 extends Base
{
    function foo(int $a, $b = 5)
    {
        parent::foo($a);
    }
}

$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);

O exemplo acima produzirá:

Valid
Valid

Os exemplos a seguir demonstram que um método derivado, que remove um parâmetro ou transforma um parâmetro opcional em mandatório, não é compatível com o método original.

Example #12 Erro fatal quando um método derivado remove um parâmetro

<?php

class Base
{
    public function foo(int $a = 5) {
        echo "Válido?\n";
    }
}

class Extend extends Base
{
    function foo()
    {
        parent::foo(1);
    }
}

A saída do exemplo acima no PHP 8 é semelhante a:

Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13

Example #13 Erro fatal quando um método derivado transforma um parâmetro opcional em mandatório

<?php

class Base
{
    public function foo(int $a = 5) {
        echo "Válido?\n";
    }
}

class Extend extends Base
{
    function foo(int $a)
    {
        parent::foo($a);
    }
}

A saída do exemplo acima no PHP 8 é semelhante a:

Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Warning

Renomear um parâmetro em uma classe derivada não é uma quebra de assinatura. Entretanto isso é desencorajado porque ocasionará um Error se argumentos nomeados forem utilizados na chamada.

Example #14 Erro ao utilizar argumentos renomeados em uma classe derivada

<?php

class A {
    public function test($foo, $bar) {}
}

class B extends A {
    public function test($a, $b) {}
}

$obj = new B;

// Passando parâmetros de acordo com o contrato de A::test()
$obj->test(foo: "foo", bar: "bar"); // ERROR!

O exemplo acima produzirá algo semelhante a:

Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14
Stack trace:
#0 {main}
  thrown in /in/XaaeN on line 14

::class

A palavra-chave class também pode ser utilizada para resolução de nome de classes. Pode-se obter o nome completo e qualificado da classe ClassName utilizando ClassName::class. Isso é particularmente útil em classes com namespaces.

Example #15 Resolução de nome da classe

<?php
namespace NS {
    class ClassName {
    }

    echo ClassName::class;
}
?>

O exemplo acima produzirá:

NS\ClassName

Note:

A resolução do nome de classe através de ::class é uma transformação em tempo de compilação. Isso significa que no momento em que o texto do nome da classe é criado, o auto carregamento ainda não ocorreu. Como consequência, nomes de classe são expandidos mesmo se a classe não existir. Não é emitido erro nestes casos.

Example #16 Resolução de um nome de classe ausente

<?php
print Does\Not\Exist::class;
?>

O exemplo acima produzirá:

Does\Not\Exist

A partir do PHP 8.0.0, a constante ::class também pode ser utilizada em objetos. Essa resolução acontece em tempo de execução, e não em tempo de compilação. O efeito é o mesmo de chamar get_class() em um objeto.

Example #17 Resolução de nome de objeto

<?php
namespace NS {
    class ClassName {
    }
}
$c = new ClassName();
print $c::class;
?>

O exemplo acima produzirá:

NS\ClassName

Métodos e propriedades nullsafe

A partir do PHP 8.0.0, propriedades e métodos também podem ser acessados com o operador "nullsafe": ?->. O operador nullsafe funciona da mesma forma que os acessos de métodos e variáveis, exceto que se o objeto referenciado é null, então null será retornado ao invés de uma exceção ser lançada. Se uma derreferência for parte de uma cadeia de chamadas, o resto da cadeia é ignorado.

O resultado é similar a encapsular cada acesso dentro de um teste is_null(), mas mais compacto.

Example #18 Operador nullsafe

<?php

// A partir do PHP 8.0.0, esta linha:
$result = $repository?->getUser(5)?->name;

// É o equivalente ao seguinte código:
if (is_null($repository)) {
    $result = null;
} else {
    $user = $repository->getUser(5);
    if (is_null($user)) {
        $result = null;
    } else {
        $result = $user->name;
    }
}
?>

Note:

O operador nullsafe é melhor utilizado quando null é considerado um valor válido e esperado de uma propriedade ou retorno de método. Para indicar um erro, uma exceção lançada é preferível.