[email protected] (41) 99669-5791
Compartilhe

O PHP 8.3 trouxe uma novidade interessante para constantes de classes: a capacidade de declarar um tipo para essas constantes. Essa novidade visa garantir a compatibilidade de tipos das constantes quando classes filhas e implementações de interfaces as sobrescrevem.

Antes do PHP 8.3, não era possível aplicar a compatibilidade de tipos de forma programática.

Agora, a partir do PHP 8.3, as constantes de classe podem declarar um tipo após a palavra-chave const:

class Test {
    const string TEST_CONSTANT = 'test';
}

A necessidade de tipos em constantes de classe está em garantir que todas as subclasses que sobrescrevem constantes de classe não alterem o tipo dessas constantes.

Quando uma constante de classe é declarada com um tipo, o PHP a aplica na própria declaração e em subclasses/implementações seguindo a covariância. Além disso, o PHP não faz coerção de valores de constantes e é sempre considerado estritamente tipado.

Se uma constante de classe for declarada com um tipo diferente da declaração, o PHP gera um erro fatal:

class Test {
const string CONST = 1;
}

Erro fatal: Não é possível usar int como valor para a constante de classe Test::CONST do tipo string

Aqui estão alguns exemplos válidos de constantes de classe que utilizam modificadores de visibilidade de constantes (adicionados no PHP 7.1), constantes de classe finais (adicionadas no PHP 8.1) e suporte para constantes em traits (adicionado no PHP 8.3). Além disso, constantes declaradas em Enums também suportam a adição de tipos a elas.

Classes com constantes tipadas

class Test {
    // constante pública com tipo "string"
    const string FOO = 'test';

    // constante protegida com tipo "string"
    protected const string GARPLY = 'test';

    // constante protegida final com tipo "string"
    final protected const string WALDO = 'test';
}

Traits com constantes tipadas

trait TestTrait {
    // constante protegida final com tipo "string"
    final protected const string WALDO = 'test';
}

Interfaces com constantes tipadas

interface TestInterface {
    public const string TEST = 'test';
}

Enums com constantes tipadas

enum TestEnum: string {
    public const string TEST = 'test';
}

Tipos suportados e Coerção de Tipos

Constantes de classe suportam tipos PHP padrão, tipos nulos, tipos de união, tipos de interseção e tipos DNF.

Os seguintes tipos não são suportados para tipos de constantes de classe:

  • Tipos void e never: Esses tipos são destinados apenas para serem usados como tipos de retorno.
  • callable: Este tipo é dependente do contexto e não é suportado em propriedades tipadas. No entanto, o tipo Closure é permitido.

As seguintes declarações não são permitidas:

class Test {
    const void FOO = 'test';
    const never FOO = 'test';
    const callable FOO = 'test';
}

Erro fatal: A constante de classe Test::FOO não pode ter o tipo void

Erro fatal: A constante de classe Test::FOO não pode ter o tipo never

Erro fatal: A constante de classe Test::FOO não pode ter o tipo callable

Comportamento de Tipagem Estrita

Independentemente do comportamento de strict_types declarado no script PHP, as constantes de classe são sempre avaliadas de forma estrita.

declare(strict_types=0);

class Test {
    const string CONST = 1;
}

Erro fatal: Não é possível usar int como valor para a constante de classe Test::CONST do tipo string na /mnt/w/localhost/test/test.php na linha 4

Variação

Assim como os tipos de retorno, os tipos de constantes de classe também podem ser “estreitados” ou mantidos iguais em uma subclasse ou implementação. Isso segue o Princípio de Substituição de Liskov.

class ParentClass {
    public const string|int VALUE = 'MyValue';
}

class ChildClass extends ParentClass {
    public const string VALUE = 'MyValue';
}

Se uma constante de classe tentar alterar ou ampliar a declaração (o que significa que são incompatíveis), o PHP emite um erro fatal em tempo de compilação:

class ParentClass {
    public const string|int VALUE = 'MyValue';
}

class ChildClass extends ParentClass {
    public const string|int|float VALUE = 'MyValue';
}

Erro fatal: O tipo de ChildClass::VALUE deve ser compatível com ParentClass::VALUE do tipo string|int

Omissão da declaração de tipo de constante

Um ponto a ser observado é que se uma classe pai declarar um tipo para uma constante, todas as subclasses também devem declarar um tipo compatível. Omissão do tipo de constante de classe é considerada um tipo incompatível.

Por exemplo, no trecho abaixo, ChildClass::VALUE é declarado sem um tipo. Como a constante de classe ParentClass::VALUE é declarada com um tipo, isso causa um erro fatal:

class ParentClass {
    public const string|int VALUE = 'MyValue';
}

class ChildClass extends ParentClass {
    public const VALUE = 'MyValue';
}

Erro fatal: O tipo de ChildClass::VALUE deve ser compatível com ParentClass::VALUE do tipo string|int

Declarar um tipo para constantes de classe não fornecidas é uma alteração incompatível com versões anteriores, pois todas as subclasses também devem declarar tipos compatíveis.

No PHP 8.3, as extensões principais Zip, SNMP e Phar declaram constantes de classe, o que representa uma mudança que pode quebrar a compatibilidade com versões anteriores.

Mudanças na API de Reflexão

É possível obter o tipo de constantes de classe usando a API de Reflexão.

A classe ReflectionClassConstant suporta dois métodos adicionais no PHP 8.3:

class ReflectionClassConstant implements Reflector {
    //...

    public function getType(): ?ReflectionType {}
    public function hasType(): bool {}
}

O método hasType() retorna se a constante é declarada com um tipo.

O método getType() retorna null se a constante de classe for declarada sem tipo ou um objeto ReflectionType se um tipo for declarado.

Exemplo de uso:

class Test {
    const string MY_CONST = 'MyConst';
}

$reflector = new ReflectionClassConstant('Test', 'MY_CONST');

$reflector->hasType(); // true
$reflector->getType(); // "string"

Compartilhe