PHP para Iniciantes: Variáveis - Variáveis variáveis

Talvez o título mais confuso da história, mas logo tudo fará sentido, tenha paciência. A questão sobre variáveis variáveis é que você precisa se perguntar: o que é uma variável?

E a resposta é simples: qualquer coisa que varia. #stonks

Então se você tem duas coisas que variam e uma coisa que varia entre essas duas coisas que variam, você tem uma variável que varia, uma variável variável.

QUE

É punk, eu sei... Esse é tipo de assunto que fica mais claro com exemplos, então vamos começar de trás pra frente.

Propriedades variáveis

Como cientista de dados,
quero armazenar uma informação capaz de referenciar o algoritmo que deve ser acionado por cada indivíduo,
para poder acionar o respectivo algoritmo no código quando for invocado.

Com essa história de usuário, eu quero:

  • construir ao menos 2 algoritmos distintos (leia-se dois métodos, não importa o que façam, rs);
  • construir uma classe capaz de receber um desses algoritmos ao instanciar um objeto;
  • acionar o algoritmo de cada instância para executar.

No PHP, tem uma forma de se fazer isso sem muito esforço, passando uma referência da função diretamente pro atributo da classe, virando um callable. Mas e se eu precisar armazenar isso no banco de dados? Então não dá pra simplesmente passar um callalbe. Você precisa ter isso como um dado representando o nome do método que precisa ser acionado...

Parece complexo? Então vamos ao código:

<?php // PHP 8!

class Exemplo {
    public function __construct(
        private string $metodo
    ) { }

    private function algoritmo1(int|float $numero): int|float
    {
        return $numero * 2;
    }

    private function algoritmo2(int|float $numero): int|float
    {
        return $numero * 5;
    }

    public function rodar(int|float $numero): int|float
    {
        return $this->{$this->metodo}($numero);
    }
}

$x2 = new Exemplo('algoritmo1');
$x5 = new Exemplo('algoritmo2');

var_dump($x2->rodar(3)); // int(6)
var_dump($x2->rodar(4)); // int(8)
var_dump($x2->rodar(5)); // int(10)
var_dump($x2->rodar(3.3)); // float(6.6)

var_dump($x5->rodar(3)); // int(15)
var_dump($x5->rodar(4)); // int(20)
var_dump($x5->rodar(5)); // int(25)
var_dump($x5->rodar(3.3)); // float(16.5)

Mas Kiko, como isso é uma variável variável?

A mágica está no método Exemplo->rodar. Note que o código não está nada bom, afinal, se você colocar um dado no atributo metodo que não seja o nome válido, dará erro. Mas considerando a proposta, a variável variável acontece na hora de acionar esse método, no trecho $this->{$this->metodo}($numero). Isso está explicitamente dizendo:

  1. $this->...($numero), o que é ...?
  2. ... = $this->metodo, que é 'algoritmo1' no $x2 e 'algoritmo2' no $x3;
  3. portanto, para cada objeto, ... terá um valor diferente, resultando:
    • $this->algoritmo1($numero) no $x2;
    • $this->algoritmo2($numero) no $x3.

Mas Kiko, ainda não entendi, o que isso tem a ver com variável variável?

Acontece que eu inverti o assunto propositalmente para termos um exemplo palpável... Para chegarmos às variáveis variáveis, podemos reescrever tudo isso aí em forma de funções e as coisas ficarão ainda mais claras.

Variáveis variáveis

<?php // PHP 8

function rodar(string $funcao, int|float $numero): int|float
{
    $x2 = fn () => $numero * 2;
    $x5 = fn () => $numero * 5;
    return $$funcao($numero);
}

var_dump(rodar('x2', 3)); // int(6)
var_dump(rodar('x2', 4)); // int(8)
var_dump(rodar('x2', 5)); // int(10)
var_dump(rodar('x2', 3.3)); // float(6.6)

var_dump(rodar('x5', 3)); // int(15)
var_dump(rodar('x5', 4)); // int(20)
var_dump(rodar('x5', 5)); // int(25)
var_dump(rodar('x5', 3.3)); // float(16.5)

Ok, eu sei que tem muita coisa pra falar... Do nada um arrow function, além de agregar tipos de dados na declaração da função... Pretendo abordar esses assuntos nos artigos sobre funções, beleza? Por agora, foca no $$funcao.

Quando você usa mais de um cifrão em uma variável, você está invocando n variáveis sequencialmente. E isso é uma operação recursiva: de dentro pra fora.

Em nosso exemplo, você está acessando, primeiramente, a variável $funcao, que pode ter os valores 'x2' e 'x5'. Depois, você acessa a variável cujo nome vem da operação anterior: ${$funcao}, que vira ${'x2'} => $x2 ou ${'x5'} => $x5.

Por fim, acionamos a função: $x2($numero) ou $x5($numero).

Ficou mais claro? Bom, o motivo de ter contado tanta historinha é que a maioria dos exemplos que você pode encontrar na internet são bem mais genéricos, e agora vou mostrar a profundidade por trás das variáveis variáveis.

<?php

$enem = "prova";
$prova = "dificil";
$dificil = "ilusao";
$ilusao = "ficarRico";
$ficarRico = "trabalhar";
$trabalhar = "seFormar";
$seFormar = "enem";

var_dump($enem); // string(prova)
var_dump($$enem); // string(dificil), $$enem = $prova = 'dificil'
var_dump($$$enem); // string(ilusao), $$$enem = $$prova = $dificil = 'ilusao'
var_dump($$$$enem); // string(ficarRico), $$$$enem = $$$prova = $$dificil = $ilusao = 'ficarRico'
var_dump($$$$$enem); // string(trabalhar), $$$$$enem = $$$$prova = $$$dificil = $$ilusao = $ficarRico = 'trabalhar'
var_dump($$$$$$enem); // string(seFormar), $$$$$$enem = $$$$$prova = $$$$dificil = $$$ilusao = $$ficarRico = $trabalhar = 'seFormar'
var_dump($$$$$$$enem); // string(enem), $$$$$$$enem = $$$$$$prova = $$$$$dificil = $$$$ilusao = $$$ficarRico = $$trabalhar = $seFormar = 'enem'
var_dump($$$$$$$$enem); // string(prova), ou seja, $$$$$$$$enem = (......) = $enem = 'prova'

E tome variável variando!

Assim como toda variável, você não pode criar variáveis com palavras reservadas. No caso, até o momento é o $this. Você também não pode referenciar variáveis globais. Se for a intenção, basta usar a variável pré-definida $_GLOBALS ao invés de tentar invocar chamando global $$$var, que não funciona. Use $_GLOBALS[$$var] no lugar.

Curtiu? Deu pra entender tudo direitinho? Bom, eu gosto muito dessas coisas que dão nó na nossa mente, por isso que esse assunto é o meu favorito. Escrevi esse artigo 100% da minha cabeça, consultei a documentação somente depois de escrever pra ver se não esqueci algum assunto, mas falei todinho: prova que tá gravado na alma, com carinho. Se você também achou interessante, compartilha com os amigos! E se ficou com alguma dúvida, não hesite em perguntar! Fechado?

Inté!