PHP para Iniciantes: Constantes - Sintaxe

Agora que você já sabe o básico sobre constantes, vamos falar um pouco sobre sua sintaxe e as possíveis formas de declaração. E sim, o interpretador tem ações diferentes de acordo com a forma que você declara uma constante. Bateu a curiosidade? Então vamos.

A escrita

As regras de escrita de uma constante são iguais às regras de variáveis, exceto que constante não pode ser precedida pelo caractere $. Dito isso, outra grande diferença entre eles - a mais óbvia - é que constantes não podem ser alteradas nem "anuladas" depois de receber um valor, isto é, uma vez declarado um dado constante, essa informação permanecerá inalterada até o fim da execução do script.

Voltando a falar de escrita, os caracteres do nome de uma constante devem seguir as regras:

  • o primeiro deve ser uma letra ou underline;
  • os demais, se houver, devem ser letra, número e/ou underline.

O que nos leva aos exemplos:

  • OI123;
  • DB_HOST;
  • __PARECE_LEGAL__ ~ __MAS_NAO_EH__;
  • etc.

Obs.: esse último exemplo começando e encerrando com underlines é extremamente arriscado, pois é como as constantes mágicas do PHP são escritas. Isso significa dizer que, se por um acidente do destino alguma versão futura do PHP criar uma constante mágica com o mesmo nome que você estava usando, teu projeto vai parar de rodar, rs. Evite fazer constantes iniciando e terminando com underline!

A declaração (owwwn -Q)

Para declarar uma constante, nós podemos usar tanto a função define() quanto a palavra reservada const.

E qual a diferença, Kiko?

const tem uma limitação gritante: você só pode declarar dessa forma inserindo valores de tipos de dados primitivos escalares ou arrays contendo somente tipos (...) escalares. Você lembra quais são? Vale a pena revisar na Introdução aos tipos de dados primitivos, mas vou resumir: booleanos, inteiros, pontos flutuantes e strings.

É dito que também é possível criar constantes com o tipo especial resource, mas dado que se trata de uma referência a um recurso externo à aplicação, é melhor não tentar fazer isso. Você pode gerar um gravíssimo problema de desempenho (onde um recurso permanece inalterável e, por exemplo, deixa de transmitir bytes - ou até mesmo é impedido de encerrar algo).

Ok... Então se usarmos a função declare podemos tornar um objeto constante, Kiko?

NÃO!

Cof, desculpe, não. Esqueci de mencionar a diferença né? É bem simples... Com const, você somente pode usar dados constantes, jamais expressões. Então você não consegue, por exemplo, usar o retorno de uma função na definição da constante.

Com o define, você consegue!

<?php

$umaFuncao = function() {
    return 'umRetorno';
};

const CONSTANTE_QUEBRADA = $umaFuncao(); // isso vai dar erro
define('CONSTANTE_OK', $umaFuncao()); // isso vai funcionar

Tá, mas por que isso é importante, Kiko?

Acontece que, algumas vezes na vida, nós podemos precisar criar uma constante que tem mais de uma possibilidade de existir. Ela não é dinâmica - o dado precisa ser constante ao longo do ciclo de vida da execução do script. Porém, o dado que será inserido pode variar.

Obs.: o exemplo a seguir é uma ofensa ao conceito de i18n (internacionalização). Considere apenas como um exemplo amigável para dar contexto, jamais faça isso, rs.

Por exemplo, digamos que eu resolvi criar uma constante com meu nome e sobrenome. Se você estiver acessando de países ocidentais, "nome + sobrenome" estará na ordem correta. Já em países orientais, não. Como você resolveria essa troca? Com um if?

<?php

// suponha que a variável $ocidental é um booleano que indica se é ou não, okay?
if ($ocidental) {
    const NOME = 'Kaique Garcia';
} else {
    const NOME = 'Garcia Kaique';
}

Ok, eu sei que é um exemplo bem besta pois poderia ser reduzido a uma linha com equação ternária, mas ainda preciso considerar que você não sabe o que é isso pois ainda não mencionei, beleza? (e se realmente não souber, não tem problema! Pode pesquisar se quiser, mas só vamos falar disso lááá pra frente!)

Ah, Kiko, como você faria?

Olha, se tem uma certa complexidade na hora de gerar um dado que será constante, por que não centralizar o gerador dessa informação em uma outra classe e apenas capturar o resultado na declaração da constante? Por exemplo:

<?php //NameBuilder.php

class NameBuilder { // BIRLLL
    public function __invoke(bool $isOcidental): string
    {
        if ($isOcidental) {
            return 'Kaique Garcia';
        }
        return 'Garcia Kaique';
    }
}

E aí substituiríamos aquela chamada por:

<?php // index.php

require_once("NameBuilder.php");
$nameBuilder = new NameBuilder();

// para testar, mude os valores manualmente para ver na prática
$ocidental = true;
define('NOME', $nameBuilder($ocidental));

var_dump(NOME); // string(Kaique Garcia), no caso do valor informado acima

Parece mais organizado? Bem, só funciona com a função define()! Pode fazer o teste trocando pela declaração const.

Você realmente pode se deparar com situações onde será necessário criar uma constante "dinâmica". Pensando nisso, é possível consultar uma constante a partir de uma string representando seu nome, seja pra saber se ela existe (função defined(), um d no final) ou qual o seu valor (constant()). Por exemplo:

<?php

// OBS.: esse teste pode não funcionar em todos os ambientes. Se você rodar e se deperar com erro dizendo que a função 'constant' não existe, experimente rodar no Shell do PHP, através do comando no terminal "php -a"

const MEU_URSINHO = 'POO';
const POO = 'QUE';

$constante = 'MEU_URSINHO';

// como saber se a constante da string presente na var $constante existe?
$existe = defined($constante); // bool
var_dump($existe); // bool(true)

// como resgatar o valor da constante?
$valor = constant($constante);
var_dump($valor); // string(POO)

// como saber qual constante utilizei? ah...
var_dump($constante); // string(MEU_URSINHO)

// e no caso desse exemplo da pra pegar a constante da constante, que nem variável variável, mas isso é loucura pessoal, não precisa quebrar a cabeça pensando nisso
var_dump(constant(constant($constante))); // string(QUE)

Obtendo a lista de constantes declaradas

Isso é bem simples, tem uma função pronta pra você: get_defined_constants(). Ele retorna um array com a assinatura <string, mixed>, que significa que as chaves são textos e os valores podem ser qualquer tipo de dado (de acordo com as regras da declaração de constantes). A chave representará o nome da constante. Por exemplo:

<?php

const NOME = 'Kaique Garcia';

var_dump(get_defined_constants()); // array( (... todas as constantes do PHP), "NOME" => "Kaique Garcia");

Constantes em classes

Enquanto escrevemos uma classe, a única forma de declarar uma constante vinculada a ela é usando a palavra const. Nesse cenário, a constante poderá ter sua visibilidade restringida. É o mesmo caso dos atributos: públicos (public), protegidos (protected) e privados (private). Se você não define a visibilidade, ela usa a padrão, que é pública. É recomendado que sempre determine uma para evitar ambiguidades... Afinal, vai que o PHP resolve mudar o padrão, né?

Então temos:

<?php

class ExemploPai {
    public const PUBLICA = 1;
    protected const PROTEGIDA = 2;
    private const PRIVADA = 3;

    public function __invoke(): int
    {
        // dentro dessa classe, todas as constantes acimas são acessíveis
        return self::PRIVADA;
    }
}

class ExemploFilho extends ExemploPai {
    public function __invoke(): int
    {
        // dentro dessa classe que é originada a partir da classe ExemploPai
        // somente as constantes públicas e protegidas são acessíveis
        return self::PROTEGIDA;
    }
}

class ExemploForasteiro {
    public function __invoke(): int
    {
        // dentro daqui, que não é originada a partir da classe ExemploPai nem ExemploFilho,
        // somente é possível pegar a constante pública
        // repare que, como ExemploFilho foi criado a partir de ExemploPai, ele tem a mesma constante vinculada a si!
        return ExemploPai::PUBLICA + ExemploFilho::PUBLICA;
    }
}

$pai = new ExemploPai;
$filho = new ExemploFilho;
$forasteiro = new ExemploForasteiro;

var_dump($pai()); // const PRIVADA = int(3)
var_dump($filho()); // const PROTEGIDA = int(2)
var_dump($forasteiro()); // const PUBLICA + const PUBLICA = int(1) + int(1) = int(2)

Conclusão

Devemos refletir com sabedoria antes de encher nossos códigos com const ou define. Não faz sentido usar define se o seu dado é do tipo escalar. Você pode economizar muito mais espaço e melhorar a legibilidade do código com a declaração mais bonita. Já se o tipo tem alguma condição para ser definido, então arrume o seu código para centralizar esse controle em outro lugar e declare com o define.

Se fizer sentido uma constante ser uma informação de uma classe, usa o const com a definição de visibilidade necessária. Não crie public se a informação só interessar à classe, beleza?

Curtiu? Comenta e compartilha! Quero ver pessoas empolgadas com PHP o tempo inteiro, hehe. No próximo artigo falaremos sobre constantes pré-definidas. Não vai ser sobre todas, porque são muitas, mas falaremos das principais que o interpretador deixa disponível para consultarmos. Não perca!

Inté!