PHP para Iniciantes: Classes e Objetos - Constantes

Se você leu meus artigos anteriores, já tens uma certa noção do que é uma constante em uma linguagem de programação, certo? Só por precaução, eu vou deixar esse link do artigo Constantes - O básico.

As constantes em classes são declaradas da mesmíssima forma que as constantes do escopo global sem uso da função define(). Note que eu estou falando em declaração, não em uso e muito menos em escopo.

Ou seja, se para criar a constante CARNAVAL eu faço const CARNAVAL = "vale-night", o mesmo se aplicaria dentro de uma classe:

<?php
class Exemplo {
    const CARNAVAL = "vale-night";
}

E temos aqui a mesma sintaxe e limitações que menciono no artigo Constantes - Sintaxe, que são, em resumo:

  • o primeiro caractere do nome da constante precisa ser uma letra ou underline (_);
  • os próximos caracteres podem ser letras, underline ou números;
  • o valor atribuído à constante só pode ser de tipos de dados primitivos escalares ou arrays contendo somente tipos (...) escalares.

Se alguma linha desse resumo te confundiu, peço que releia o artigo, tudo bem? Lá está um pouco mais detalhado.

Ok, Kiko, então o que muda?

A forma de acionar essa constante. Como ela está sendo declarada dentro de uma classe, é óbvio que, para acessar seu valor, você vai precisar usar o nome da classe de alguma forma. É uma sintaxe bem simples: NOME_DA_CLASSE::NOME_DA_CONSTANTE.

O que são esses quatro pontos (dois pontos duas vezes), Kiko?

Lembra que, quando você quer acessar uma propriedade de um objeto, você usa o operator de objeto ->? Assim como existe esse operador, temos um para acessar as propriedades de uma classe, que é os quatro pontos ::.

Portanto, se formos acessar aquela constante da classe anterior, seria algo como:

echo Exemplo::CARNAVAL; //vale-night

Hummm, interessante, Kiko! Então toda constante nas classes é pública?

NÃO

Antigamente eram, mas hoje o PHP nos dá a possibilidade de controlar a visibilidade das constantes nas classes. No artigo anterior, sobre propriedades, eu mencionei detalhes sobre como funcionam as visibilidades private, protected e public, certo? Nas constantes funcionam da mesma forma! Então deixo o link para revisar caso não lembre, hehe.

Por isso, é seguro dizer que constantes em classe tem escopos totalmente diferentes das constantes globais. Se você pode fazer uma constante ser visível somente dentro da classe, não tem como outra parte do código acessar aquilo, sabe? Diferente de constantes globais que podem ser acessadas de qualquer parte depois que forem declaradas.

Dito isso, vamos falar sobre um ponto muito importante.

Boas práticas

Não, eu não vou dizer como você tem de escrever o seu código. Eu vou falar o que é considerado boas práticas pela comunidade de desenvolvimento. Na real, é errado chamar algo de bom ou mal, porque o sentido aqui é sobre criar um padrão sólido que deixe o código compreensível e de fácil manutenção. Toda vez que alguém mencionar "boas práticas", lembre-se disso, tá bom?

Tá, Kiko, mas diz aí, o que que a comunidade diz sobre constantes em classes?

Não somente sobre constantes em classes, mas constantes no geral. É dito que é uma boa prática escrever as constantes com todas as letras maiúsculas, por exemplo. Também é importantíssimo que você controle bem as visibilidades das constantes: não tem porque algo ser visível em escopos onde sua existência não importa, sabe? Porém, se você não tem certeza de onde vai usar, tudo bem deixar no escopo global.

E outro detalhe que, na minha opinião, foi a minha maior lição de vida e que posso confirmar por experiência que impacta diretamente na manutenabilidade de código é: nunca deixe números mágicos no seu código.

Quando falamos em números mágicos, não estamos falando somente de números. Qualquer valor misterioso que está aplicado diretamente no código deve ser substituído por uma constante com um nome que descreva o que é aquele valor.

Por exemplo, se eu faço:

if ($status == 0) {
    throw new Exception();
}

Você entende qual é o status validado?

Meme: Nazaré fazendo conta

Não, né? Agora se eu faço:

if ($status == STATUS_INDEFINIDO) {
    throw new Exception();
}

Fica bem mais claro, concorda?

Meme: pense nisso

Nesse exemplo, a pessoa que está desenvolvendo provavelmente saberia que o status 0 é o status indefinido. Porém, parte do desenvolvimento é sobre escrever códigos para que OUTROS DESENVOLVEDORES façam manutenção no futuro. Então você deve pensar: se um novato pegar esse código, ele vai entender rapidamente o que está acontecendo?

É isso. Constantes funcionam bem aqui.

Legal, Kiko... Mas você usou uma constante global ali, não foi?

Sim! E falando em status, aquela constante poderia ser o valor de um Enum.

O que é isso, Kiko?

Se jogar no Google "o que é enum?", irá encontrar a definição mais comum que é:

(...) Enum(eração) é um tipo de dado abstrato, cujos valores são atribuídos a exatamente um elemento de um conjunto finito de identificadores escolhidos pelo programador (...)

E é isso mesmo. Enums servem para definir os possíveis valores de alguma coisa, atribuindo um nome mais claro para cada um desses valores. Antes do PHP 8.1, não existia nenhuma implementação de enums nativamente, então nós criávamos classes com constantes públicas dentro delas para fazer esse mapa e evitar números mágicos no código. Por exemplo:

class StatusEnum {
    const INDEFINIDO = 0;
    const PUBLICADO = 1;
    const RASCUNHO = 2;
}

// (...)

if ($status == StatusEnum::INDEFINIDO) {
    throw new Exception();
}

Com uma Enum bem definida no código, você não teria a necessidade de decorar os possíveis valores que pode atribuir.

Ah, Kiko, e como são os Enums no PHP 8.1?

MARAVILHOSOS

enum StatusEnum: int {
    case INDEFINIDO = 0;
    case PUBLICADO = 1;
    case RASCNHO = 2;
}

// (...)

if ($status == StatusEnum::INDEFINIDO) {
    throw new Exception();
}

Ah, Kiko, a usabilidade não é a mesma?

Absolutamente não. No caso dos Enums no PHP 8.1, nós temos as Enums e as backed values Enums. Atualmente só funciona com os tipos int e string. Se você faz apenas uma Enum (sem informar tipagem e valores), o interpretador automaticamente implementa um método cases que retorna um array com todos os cases que você criou.

Já se você usa o backed values enums, informando uma tipagem e os valores de cada case, o interpretador adiciona dois métodos:

  • from, que serve até pra validar se um valor é um dos cases presente na Enum, disparando exception se não for;
  • tryFrom, que é o mesmo que o from exceto que não dispara erro, apenas retorna nulo se não for um valor válido.

São métodos que teríamos de implementar na mão, se fôssemos desenvolver com classes, sabe? E eles vem nativamente.

Tá, Kiko, mas quando eu devo usar um backed value enum e quando não usar?

Quando você estiver desenvolvendo uma Enum cujos valores não importam fora do código, tanto a nível de resposta de requisição quanto a enviar para o banco de dados, você não precisa usar o backed value enum. Já se você está criando algo que será armazenado no banco ou que irá preencher a resposta da aplicação, é interessante atribuir os valores.

Eu já não vou dar mais detalhes sobre Enum por agora porque vai ser conteúdo de outro artigo, tudo bem? Só escrevi o necessário para complementar a importância das constantes no seu código.

Enums são sempre globais, porque estão ali para deixar o seu código mais legível em qualquer parte dele. Mas é válido lembrar que teremos constantes que não precisam ser globais, podem ser um dado específico de uma classe, sabe? Tem de fazer sentido estar ali ou não. Fechado??

Por hoje é só! Curtiu? Comenta e compartilha!! Eu demorei bem mais do que o esperado para voltar a escrever, né?! Tanto que só consegui tempo agora no carnaval, hahahaha. Feliz carnaval pra você, se estiver lendo antes/durante. Se já passou, bem, que sua vida seja verdadeiramente feliz!

Não sei o quanto vou demorar para escrever a continuação, por isso solta aí nos comentários "faz ele escrever, Jéssica!" para eu mostrar para minha esposa, assim ela vai me instigar mais a escrever também! HUEHUEHUE

Inté!!