PHP para Iniciantes: Constantes Pré-definidas

O PHP tem muitas constantes pré-definidas. Parte delas é gerada por extensões, o que significa que, se não estiverem instaladas, não existirão no código. Ainda assim, algumas de suas constantes são geradas dinamicamente (ou magicamente), possuindo contextos diferentes.

Na minha visão, isso fere a lógica de que constantes não mudam, concorda? Tem constante que, se invocada em um lugar, tem um valor. Se acionada em outro, muda.

Ué, então a constante está variando, Kiko?

Não necessariamente. Nesses casos, o que ocorre é que essas constantes pré-definidas tem um ciclo de vida útil completamente diferente. Algumas só vão existir dentro de funções ou métodos (ex: __FUNCTION__ e __METHOD__, respectivamente), outras no escopo global mas criadas individualmente em cada contexto de arquivo (ex: __DIR__).

Então a falsa sensação de que a constante está mudando de valor nada mais é que uma má compreensão do significado de cada uma delas. Por isso, é importante sempre dar uma estudada nas constantes encapsuladas com underlines (__blabla__) para saber se elas tem o comportamento esperado antes de usar. Se a constante não tiver esse encapsulamento, pode ficar tranquilo(a), pois só as encapsuladas tem o valor condicionado.

Você pode ver as definições de algumas delas na documentação oficial do PHP sobre constantes pré-definidas. Ainda assim, lá não tem todas. Convém pesquisar especificamente a que irá utilizar, beleza?

Então vamos falar de algumas bem interessantes! Ao menos as que a gente mais encontra por aí.

1. __DIR__

Toda vez que você encontrar um código alheio que possa ter alguma lógica de include() ou require(), você possivelmente irá encontrar esse carinha por aí. Isso porque ele é uma referência do diretório absoluto onde o script atual está.

Por exemplo, se o script que você está rodando agora estiver dentro da pasta /var/www/my-api/, independente do nome do arquivo, provavelmente a constante __DIR__ terá esse valor.

Por que "provavelmente", Kiko?

Porque essa composição de diretório absoluto varia pra cada sistema operacional. A informação pode vir com a barra normal (/) ou a invertida (\), por exemplo. A questão é que vai ser uma referência para iniciar alguma coisa naquele diretório. Sacou? Pode fazer um teste:

<?php

var_dump(__DIR__);

E se eu executar o código PHP diretamente no Shell, Kiko?

(pra quem não lembra, você pode executar códigos PHP diretamente no CLI sem criar um arquivo usando o comando php -a, entrando no shell interativo. Você também pode rodar um comando inteiro usando php -r)

Então ele vai usar o diretório onde o shell foi iniciado! Afinal, se você rodar algum comando para interagir com algum arquivo, ele vai considerar o diretório onde o terminal estava "estacionado".

Entretanto, tem uma outra constante que não deveria existir no shell mas está lá...

2. __FILE__

Se __DIR__ é o diretório atual, __FILE__ deve ser o nome do arquivo, certo?

ERROOOOOOOOU

Quer dizer, mais ou menos. Ele armazena o caminho absoluto até o arquivo que está sendo executado, incluindo seu diretório. Porém, seu profundo significado apenas diz que se trata de um arquivo e não um shell. Dito isso, faria sentido ela não existir no terminal, concorda?

O fato é que ela existe e você pode fazer esses testes aí:

string("Command line code")

php -r "var_dump(__FILE__);"

string("php shell code")

php -a
> var_dump(__FILE__);

Incrível, né? Mesmo não sendo um arquivo, a constante está lá. Então muito cuidado na hora de considerar ela no seu código, embora seja raro colocar algo para rodar diretamente no shell.

3. __FUNCTION__

Essa é bem interessante pois ela carrega o nome da função que está executando agora. Pode não ser tão útil assim no dia-a-dia, mas se você está escrevendo um log na mão, descobrir o nome da função usando essa constante pode ser melhor do que ficar escrevendo textos. Vai que a função muda de nome em uma atualização?

<?php

function somar(int $a, int $b): int
{
    var_dump(__FUNCTION__); // string("somar")
    return $a + $b;
}

somar(1, 2);

Ainda assim, há cenários em que ela simplesmente não vai funcionar. É o caso das funções anônimas. Eu ainda não cheguei nesse assunto, mas, resumindo, trata-se de uma função que não tem nome (ata). Como eu te daria um nome se ela não tem? :hehe:

Nesses casos, o PHP irá te informar que se trata de uma closure, carregando o nome "{closure}".

<?php

$minhaVar = function() {
    var_dump(__FUNCTION__); // string("{closure}")
};

$minhaVar();

Outra parada bem interessante sobre essa constante é que ela não se limita à funções. Também pode ser aplicada em métodos de classes, tendo o mesmo comportamento de dizer o nome do callable que está sendo executado naquele instante.

<?php

class Calculadora {
    public function somar(int $a, int $b): int
    {
        var_dump(__FUNCTION__); // string("somar")
        return $a + $b;
    }
}

$calculadora = new Calculadora;
$calculadora->somar(1,2);

E falando em classes, se você quiser saber o nome da classe, também temos uma...

4. __CLASS__

Como o próprio nome diz: é o nome da classe do contexto atual do código. Se você buscar esse valor fora de uma classe, terá um texto vazio como resposta. Afinal, até ali não tem nenhum nome de classe presente, certo?

Mas dentro de classes a história é diferente. Ele vai obter o nome da classe onde o método está assinado.

<?php

class Pai {
    public function dump(): void
    {
        var_dump(__CLASS__);
    }
}

class Filho extends Pai {
    public function dump2(): void
    {
        var_dump(__CLASS__);
    }
}

$pai = new Pai();
$filho = new Filho();

$pai->dump(); // string("Pai")
$filho->dump(); // string("Pai")
$filho->dump2(); // string("Filho")

Percebe que mesmo acionando o método dump na classe Filho continuou retornando 'Pai'? Bem, é onde o método foi assinado. Caso houvesse um override (escrever o método com o mesmo nome no filho), o valor teria mudado.

Se sua vontade é saber a classe que está acionando o método e não onde o método foi assinado, use static::class ao invés dessa constante.

<?php

class Pai {
    public function dump(): void
    {
        var_dump(static::class);
    }
}

class Filho extends Pai { }

$pai = new Pai;
$filho = new Filho;

$pai->dump(); // string("Pai")
$filho->dump(); // string("Filho")

Tá, Kiko, quando eu vou usar essa constante?

Sinceramente, não sei. Se você é um(a) iniciante, eu espero que fique longe dela por um bom tempo. Somente quando você for fazer factories talvez seja interessante considerar isso, mas arrisco dizer que as abordagens que fizer serão muito mais ligadas ao static::class do que a essa constante. E sobre essa mágica static::class, irei explicar melhor o que é isso nos artigos de classes, ok?!

Então eu já falei de __FUNCTION__ e de __CLASS__, mas e se você quiser saber, ao mesmo tempo, a classe e o nome do método que está sendo invocado?

5. __METHOD__

Nessa constante, você recebe uma mistura de __CLASS__ + :: + __FUNCTION__, obtendo a referência completa para chamar o método referenciado. Você também pode acionar essa constante dentro de funções, o que a fará ter o mesmo comportamento de __FUNCTION__, mas aí seu código vai ter uma legibilidade meio confusa. Vamos considerar somente o uso dentro de classes, beleza?

<?php

class Pai {
    public function dump(): void
    {
        var_dump(__METHOD__);
    }
}

class Filho extends Pai { }

$pai = new Pai;
$filho = new Filho;

$pai->dump(); // string("Pai::dump")
$filho->dump(); // string("Pai::dump")

Como pode notar, da mesma forma que __CLASS__, essa constante está presa à assinatura do método. Se foi assinado na classe Pai, vai sempre retornar Pai:: como prefixo.

Obs.: uma coisa que acho muito errada nessa constante é que ela retorna o nome sob a sintaxe {CLASS}::{FUNCTION}, como se a chamada da função fosse estática, isto é, presa somente à classe e não à instância. As funções no exemplo acima não são estáticas. É preciso gerar um objeto (ex: $pai e $filho) para poder acioná-las. Dito isso, esse tipo de escrita pode gerar alguma confusão se você usar como referência no código.

6. Constantes para dados padrões de tipos de dados primitivos

Calma, não estou dizendo que int é uma constante, muito menos bool e as demais. Porém, é dito que os booleanos são frequentemente interpretados como verdade ou falso. No código, isso não é diferente. Foram criadas duas constantes para representar esses valores:

  • true: o binário 0b1 em booleano;
  • false: o binário 0b0 (...).

Além deles, também temos a null para representar os nulos. Note que essas constantes são case insensitive, isto é, você pode variar as letras maiúsculas e minúsculas como quiser e ainda assim acessarão o mesmo valor.

7. PHP_EOL

EOL é uma abreviação para End Of Line, cuja tradução seria Fim De Linha. É bem utilizada em códigos que são executados no terminal, pois essa constante insere caracteres especiais para fazer uma quebra de linha. Em alguns sistemas operacionais, trata-se da combinação "\r\n". Em outros, somente "\n". Como essa quebra pode variar, é considerado uma boa prática não escrever isso diretamente nos textos e sim concatenar com a constante PHP_EOL.

<?php

// não faça isso, mesmo que funcione
echo "Uma linha\n";

// faça isso
echo "Outra linha" . PHP_EOL;

Oh, Kiko... Por que essa constante não tá encapsulada com underlines?

Lembra que eu mencionei lá no começo que somente as constantes encapsuladas com underline tem aquele poder de variar seu valor a depender do contexto? É sobre isso. Essa constante tem um valor permanente para o sistema operacional que está executando, ou seja, acredito ser impossível trocar de SO de um script pro outro, concorda?

Nesse caso, é seguro dizer que o mesmo valor do início da execução estará presente no final. Por isso, essa função não é encapsulada com underlines (se fosse, seria considerada uma constante mágica, nosso tópico do próximo artigo).

8. PHP_VERSION

Essa é bem óbvia, né? Traz a versão do PHP. Acho que nem tem o que dizer...

<?php

var_dump(PHP_VERSION);

Você pode precisar dela na hora de desenvolver uma lib onde seja necessário criar algum esquema de retrocompatibilidade entre diferentes versões do PHP. Acontece bastante pra quem faz SDKs, que são bibliotecas de código para facilitar a integração com aplicações externas. Muitas vezes é necessário fornecer um código que funcione do PHP 5.6 ao PHP 8 e isso não é muito trivial de fazer bem feito. No geral, você pode ter repositórios diferentes para cada versão, mas isso torna difícil manter todos atualizados...

E aí, uma das estratégias é desenvolver tudo no mesmo repositório e centralizar o spl_autoload num factory baseado na versão do PHP. É bem louco, mas funciona. Se os desenvolvedores separarem bem os domínios, dá pra manter o código atualizado para várias versões simultaneamente, sem a necessidade do cliente configurar alguma coisa.

Obs.: se você não entendeu nada do que eu acabei de falar sobre SDKs, não se preocupe. Recomendo fazer pesquisas sobre isso pois mesmo não sendo tópico dessa série PHP para Iniciantes é bem provável que eu mencione isso mais vezes.

Enfim, por hoje é só! Tem muitas outras constantes que poderíamos estar falando mas boa parte delas será assunto do nosso próximo artigo: constantes mágicas. Eu já dei o spoiler sobre os underlines e mostrei algumas delas aqui, mas tem outras com valores bem úteis, ok? Acompanha de perto que vai ser legal.

Inté!