Dia 7 — Funções: PHP e MySQL

Informatica · Conteudo · publicado em 02/10/2026
Aula 1

Declarar funções

Declarar funções

Uma função é um bloco de código com nome, parâmetros e retorno. Serve para nomear uma lógica, evitar repetição e passar um comportamento para outra parte do código aceitar. Em PHP, a declaração usa function, o nome vem depois e o corpo fica entre chaves.

Como se usa

function somar(int $a, int $b): int { return $a + $b; }
echo somar(10, 5);   // 15
  • Parâmetro é o nome declarado na assinatura ($a). Argumento é o valor passado na chamada (10). O manual chama qualquer um de parâmetro; a distinção importa para ler a chamada.
  • O corpo só roda quando a função é chamada. Uma função declarada e nunca chamada não produz efeito, nem erro.
  • return encerra a função e entrega o valor. Sem return, a função devolve null implicitamente.
  • O tipo declarado depois dos dois-pontos é o tipo de retorno: : int, : string, : void, : array, : bool, : float.
  • void significa que a função não devolve nada. Ela serve para efeito colateral (imprimir, gravar, mexer em array por referência), e return; sozinho é permitido.

Escopo

Variável de fora não entra na função. $taxa = 0.15; não existe dentro de uma função que não a recebeu como argumento — ler $taxa lá dentro é erro de *undefined variable*. Tudo que a função usa, ela recebe por argumento, e o que ela produz, devolve por return.

function total(float $preco, float $taxa): float { return $preco * (1 + $taxa); }

Para um valor fixo que não muda, use const no topo do arquivo ou um static dentro da função. const declarado dentro do corpo de uma função é erro de sintaxe.

Como declarar bem

SituaçãoComo
Lógica reaproveitada em dois lugaresfunction nomeada no topo do arquivo ou em arquivo próprio
Bloco longo dentro de outra funçãofunção aninhada, se só é usada ali
Lógica que só existe como valorclosure, que é a aula do dia 8
Configuração que não mudaconst ou define, não função
DetalheComportamento
Nome de funçãocase-insensitive: SOMAR() e somar() são a mesma função
Declaração repetidaerro fatal *Cannot redeclare*
Função chamada antes da linha functionfunciona: funções são carregadas antes da execução
Função dentro de if ou foreachsó existe depois que o bloco roda; chamar antes é erro
return no meioignora todo o código que vem depois dentro da função

Funções não fazem parte do objeto. Não dá require "dentro" de uma função para importar coisa: o include roda no escopo da função e as funções importadas ficam restritas a ela.

Exemplo

<?php
// ============================================================
// AULA 1 - Declarar funcoes: function, return e escopo
// ============================================================

declare(strict_types=1);

// Uma funcao tem nome, parametros e retorno. O parametro e o nome
// declarado; o argumento e o valor passado na chamada.
function somar(int $a, int $b): int
{
    return $a + $b;
}

echo "somar(10, 5): " . somar(10, 5) . "\n";
// Passar float onde se espera int lanca TypeError: a funcao nao converte.
echo "somar(0.5, 0.25) com parametro int: ";
try {
    somar(0.5, 0.25);
} catch (TypeError $erro) {
    echo $erro->getMessage() . "\n";
}

// Sem return, a funcao devolve null implicitamente.
function cumprimentar(string $nome): void
{
    echo "  Ola, $nome!\n";
}

echo "\nFuncao void (nao devolve valor):\n";
cumprimentar('Ana');
cumprimentar('Bruno');
var_dump(cumprimentar('Carla'));   // devolve null

// return no MEIO da funcao encerra tudo: o que vier depois e ignorado.
function classifica(float $nota): string
{
    if ($nota >= 7.0) {
        return 'aprovado';
    }
    if ($nota >= 5.0) {
        return 'recuperacao';
    }
    return 'reprovado';
}

echo "\nClassificacao:\n";
foreach ([9.5, 6.0, 3.0] as $nota) {
    echo "  nota $nota: " . classifica($nota) . "\n";
}

// Escopo: variavel de fora NAO entra na funcao.
$taxa = 0.15;
echo "\nTaxa visivel aqui fora: $taxa\n";

function aplicarTaxa(float $valor, float $taxa): float
{
    // echo $taxa;   // daria erro: undefined variable
    return $valor * (1 + $taxa);
}

echo "Total com a taxa passada como argumento: " . aplicarTaxa(100.00, $taxa) . "\n";

// A mesma logica com a taxa fixa dentro da funcao, sem vir de fora.
function totalComTaxaFixa(float $preco): float
{
    return $preco * 1.15;
}

echo "Total com taxa fixa interna: " . totalComTaxaFixa(100.00) . "\n\n";

// Funcao declarada DEPOIS da chamada: funciona, porque o PHP carrega
// as funcoes declaradas no arquivo antes de executar o codigo.
echo "Chamada antes da declaracao: " . areaRetangulo(3, 4) . "\n";

function areaRetangulo(int $base, int $altura): int
{
    return $base * $altura;
}

// Nome de funcao nao diferencia maiusculas: SOMAR() e somar() sao a mesma.
echo "\nsomar e SOMAR sao a mesma funcao: " . SOMAR(2, 3) . "\n";

// Funcao aninhada: so existe dentro da externa e so depois que ela roda.
function processar(array $itens): array
{
    function somentePositivo(int $n): bool
    {
        return $n > 0;
    }

    return array_values(array_filter($itens, 'somentePositivo'));
}

echo "\nAninhada (definida dentro da funcao): " . implode(', ', processar([-2, 5, 0, 7, -1, 3])) . "\n";

Saída real

somar(10, 5): 15
somar(0.5, 0.25) com parametro int: somar(): Argument #1 ($a) must be of type int, float given, called in /w/aula1.php on line 19

Funcao void (nao devolve valor):
  Ola, Ana!
  Ola, Bruno!
  Ola, Carla!
NULL

Classificacao:
  nota 9.5: aprovado
  nota 6: recuperacao
  nota 3: reprovado

Taxa visivel aqui fora: 0.15
Total com a taxa passada como argumento: 115
Total com taxa fixa interna: 115

Chamada antes da declaracao: 12

somar e SOMAR sao a mesma funcao: 5

Aninhada (definida dentro da funcao): 5, 7, 3
Aula 2

Tipos de parâmetro e retorno

Tipos de parâmetro e retorno

Com declare(strict_types=1) no topo, os tipos declarados na assinatura passam a valer de verdade: o PHP passa a recusar valores do tipo errado em vez de convertê-los sozinho. É a linha que separa código previsível de código que aceita "5" onde se espera 5.

Como se usa

declare(strict_types=1);

function(float $preco, int $qtd, string $cupom = ''): float

O declare é por arquivo, não por função: vale para todas as chamadas feitas dentro daquele arquivo, e só para chamadas feitas a partir dele. O tipo do argumento é conferido no momento da chamada.

Tipos que se pode declarar

TipoAceitaObservação
intinteiro4, -7
floatreal4.5; um int é aceito onde se espera float
stringtexto
boolverdadeiro ou falso
arrayarrayqualquer dos três formatos
mixedqualquer valoro que o PHP chamava de "sem tipo"
voidsó no retornoa função não devolve nada
neversó no retornoa função sempre lança erro e nunca devolve
objectqualquer objeto
callablefunção que pode ser chamadaparâmetro ou retorno
? antes do tipoanulável?string aceita string e null
int ou stringuniãoaceita um dos tipos listados, até 8

União e interseção

A união (|) é a alternativa ao PHP 8.0 e resolve a função que aceitava int ou null sem transformar tudo em mixed. A interseção (&) é o espelho, disponível do PHP 8.1, e serve para objeto que precisa implementar duas ou mais interfaces ao mesmo tempo: Countable&ArrayAccess.

O que acontece na hora de errar

SituaçãoO que o PHP faz
f(int $n) chamada com "texto"TypeError
f(int $n) chamada com "5"TypeError (com strict_types, sem ele aceitaria)
f(int $n): string que devolve 5TypeError no return
f(?int $n) chamada com nullaceita
f(int $n) chamada com 4.5TypeError, mesmo sem strict_types: float para int é perda de informação
f(float $n) chamada com 4aceita, o int é promovido a float

O TypeError é uma exceção e sai do script se ninguém tratar. A forma de tratar é o try/catch, ou a checagem antes da chamada, com is_int, is_string e afins.

Sem declare(strict_types=1), f(int $n) chamada com "5" converte e roda; com o declare no arquivo, ela lança TypeError. A mesma assinatura se comporta de dois jeitos dependendo de uma linha no topo do arquivo — por isso ele vai no primeiro arquivo, e não em todos.

Exemplo

<?php
// ============================================================
// AULA 2 - Tipos de parametro e retorno
// ============================================================

declare(strict_types=1);

// Com strict_types, o tipo declarado na assinatura passa a valer.
// Sem ele, o PHP converte o valor sozinho e aceita "5" onde quer 5.

function somar(int $a, int $b): int
{
    return $a + $b;
}

// int e aceito onde se espera float: o int e promovido, sem perda.
function media(float $a, float $b): float
{
    return ($a + $b) / 2;
}

echo "somar(10, 5) com dois int: " . somar(10, 5) . "\n";
echo "media(7, 5) com int em parametro float: " . media(7, 5) . "\n\n";

// O tipo errado lanca TypeError, que sai do script se ninguem tratar.
echo "Passando string onde se espera int:\n";
try {
    somar('dez', 5);
} catch (TypeError $erro) {
    echo "  TypeError: " . $erro->getMessage() . "\n";
}

echo "\nPassando float com parte decimal onde se espera int:\n";
try {
    somar(4.5, 5);
} catch (TypeError $erro) {
    echo "  TypeError: " . $erro->getMessage() . "\n";
}

// Um unico tipo de letra, dois retornos validos: union type (? e |).
function describe(?int $codigo): string
{
    return $codigo === null ? 'sem codigo' : "codigo $codigo";
}

function somaOuTexto(int|float $a, int|float $b): int|float
{
    return $a + $b;
}

echo "\nRetorno anulavel (?):\n";
echo "  " . describe(null) . "\n";
echo "  " . describe(404) . "\n";

echo "\nUnion type (int|float no parametro e no retorno):\n";
echo "  somaOuTexto(3, 4)     = " . somaOuTexto(3, 4) . " (int)\n";
echo "  somaOuTexto(3.5, 4.25) = " . somaOuTexto(3.5, 4.25) . " (float)\n";

// mixed e o tipo que aceita qualquer valor: substitui a falta de tipo.
function resumo(mixed $valor): string
{
    return match (true) {
        is_null($valor)   => 'null',
        is_bool($valor)   => 'bool',
        is_int($valor)    => "int ($valor)",
        is_float($valor)  => "float ($valor)",
        is_string($valor) => "string ($valor)",
        is_array($valor)  => 'array com ' . count($valor) . ' itens',
        default           => 'outro tipo: ' . get_debug_type($valor),
    };
}

echo "\nO tipo mixed passa por qualquer valor:\n";
foreach ([42, 3.14, 'texto', true, null, [1, 2, 3]] as $valor) {
    echo "  " . resumo($valor) . "\n";
}

// void: a funcao nao devolve nada. never: ela sempre lanca erro.
function registrar(string $mensagem): void
{
    echo "  [log] $mensagem\n";
}

function validarSenha(string $senha): string
{
    if (strlen($senha) < 8) {
        throw new InvalidArgumentException('senha precisa de 8 caracteres ou mais');
    }

    return 'senha valida';
}

echo "\nvoid apenas executa:\n";
registrar('inicio do relatorio');

echo "never lanca excecao e nunca retorna:\n";
try {
    validarSenha('123');
} catch (InvalidArgumentException $erro) {
    echo "  InvalidArgumentException: " . $erro->getMessage() . "\n";
}
echo "  resultado valido: " . validarSenha('senhaforte') . "\n\n";

// O tipo de retorno tambem e conferido: devolver o tipo errado e TypeError.
function mediaComErro(array $notas): string
{
    return array_sum($notas) / count($notas);
}

try {
    mediaComErro([8.0, 6.0]);
} catch (TypeError $erro) {
    echo "Retorno do tipo errado tambem lanca TypeError:\n  " . $erro->getMessage() . "\n";
}

Saída real

somar(10, 5) com dois int: 15
media(7, 5) com int em parametro float: 6

Passando string onde se espera int:
  TypeError: somar(): Argument #1 ($a) must be of type int, string given, called in /w/aula2.php on line 28

Passando float com parte decimal onde se espera int:
  TypeError: somar(): Argument #1 ($a) must be of type int, float given, called in /w/aula2.php on line 35

Retorno anulavel (?):
  sem codigo
  codigo 404

Union type (int|float no parametro e no retorno):
  somaOuTexto(3, 4)     = 7 (int)
  somaOuTexto(3.5, 4.25) = 7.75 (float)

O tipo mixed passa por qualquer valor:
  int (42)
  float (3.14)
  string (texto)
  bool
  null
  array com 3 itens

void apenas executa:
  [log] inicio do relatorio
never lanca excecao e nunca retorna:
  InvalidArgumentException: senha precisa de 8 caracteres ou mais
  resultado valido: senha valida

Retorno do tipo errado tambem lanca TypeError:
  mediaComErro(): Return value must be of type string, float returned
Aula 3

Parâmetros especiais

Parâmetros especiais

Além dos parâmetros com tipo, a assinatura aceita valor padrão, nomeação na chamada, quantidade variável de argumentos, tipo anulável e desempacotamento de array. São os recursos que tornam uma função utilizável em código real, sem posição fixa de argumento.

Como se usa

function conectar(string $host, string $user, string $senha, int $porta = 3306, int $timeout = 5): PDO

O valor padrão entra depois dos obrigatórios, nunca antes. O parâmetro com padrão se torna opcional: a chamada pode omiti-lo, e o PHP usa o valor declarado.

Os quatro recursos

RecursoSintaxeO que faz
Valor padrãoint $limite = 10o argumento pode ser omitido
Nomeadoconectar(host: $h, porta: 5432)argumento pela ordem que você quiser, do PHP 8.0
Variádicoint ...$valoresrecebe quantos argumentos houver, num array
Spreadf(...$array)passa um array como se fossem argumentos soltos
Anulável?int $idaceita int ou null
Promoçãopublic function __construct(private string $nome)cria e tipa a propriedade junto, em classe

Argumento nomeado e variádico juntos resolvem a função de configuração com muitas opções: o que tem nome goes pelo nome, o que sobra cai no variádico.

function log(string $nivel, string $mensagem, ?string $arquivo = null, string ...$extras): void
log('erro', 'conexao recusada');
log('erro', 'conexao recusada', arquivo: '/var/log/app.log');
log('info', 'iniciado', null, 'host', 'porta');

A ordem que o PHP exige

Na assinatura, a ordem é rígida: parâmetro obrigatório, depois o com valor padrão, depois o variádico. O variádico tem que ser o último, senão é erro de sintaxe. E a tipagem é uniforme: se um parâmetro declara tipo, todos os outros também declaram, ou a declaração é ignorada.

SituaçãoComportamento
Argumento nomeado para parâmetro inexistenteError: Unknown named parameter
f($a) com f(int $x, int $y = 2)$y fica 2
f(x: 1) com f(int $x)funciona, é o parametro nomeado
Variádico recebe zero argumentoo array fica vazio, sem erro
f(...$vazio)chama f() sem argumento nenhum

Nomeado não muda o valor padrão: conectar(porta: 3306) é idêntico a conectar(...) sem o argumento. O que muda é a ordem — dá para chamar a função máxima usando só timeout: 10, sem repetir os outros. O erro clássico é digitar o nome do parâmetro com maiúscula ou em português: o nome é case-sensitive e tem que ser o da assinatura.

Exemplo

<?php
// ============================================================
// AULA 3 - Parametros especiais: padrao, nomeado, variadico, spread
// ============================================================

declare(strict_types=1);

// --- Valor padrao: o parametro com valor na assinatura vira opcional ---
function somar(float $preco, float $quantidade = 1.0, float $desconto = 0.0): float
{
    return ($preco * $quantidade) * (1 - $desconto);
}

echo "Com todos os argumentos:   " . somar(25.90, 3, 0.10) . "\n";
echo "Usando o padrao da qtd:   " . somar(25.90) . "\n";
echo "Usando so o desconto:     " . somar(25.90, desconto: 0.50) . "\n\n";

// --- Argumento nomeado: a ordem deixa de importar (PHP 8.0) ---
function conectar(string $host, string $user, string $senha, int $porta = 3306, int $timeout = 5): string
{
    // A senha nunca aparece na saida: so o nome da variavel entra no log.
    return "$user@$host:$porta (timeout {$timeout}s, variavel \$senha configurada)";
}

echo "Argumento nomeado, fora de ordem:\n";
echo "  " . conectar(host: 'localhost', user: 'app', senha: 'oculta', timeout: 10) . "\n";
echo "  " . conectar('localhost', 'app', 'oculta', 5432) . " (posicional)\n\n";

// Nome inexistente e erro, e o nome do parametro e case-sensitive.
try {
    conectar(hostname: 'localhost', user: 'app', senha: 'x');
} catch (Error $erro) {
    echo "Nome de parametro errado: " . $erro->getMessage() . "\n\n";
}

// --- Parametro variadico: quantos argumentos vierem, num array ---
// A uniao string|int no variadico aceita valores de tipos diferentes,
// porque um unico tipo escalar nao daria conta dos dois.
function registrar(string $nivel, string $mensagem, string|int ...$extras): string
{
    $detalhe = $extras === [] ? 'sem detalhe' : implode(', ', array_map('strval', $extras));

    return "[$nivel] $mensagem ($detalhe)";
}

echo "Variadico:\n";
echo "  " . registrar('erro', 'conexao recusada') . "\n";
echo "  " . registrar('erro', 'conexao recusada', 'tentativa', 3) . "\n";

// Com o variadico, a func nao precisa checar se sobrou argumento:
// um array de $extras vazio e o resultado normal de uma chamada sem extras.
function somenteExtras(string $fixo, string ...$extras): array
{
    return $extras;
}

var_dump(somenteExtras('so o fixo'));
echo "\n";

// --- Spread: passa um array como se fossem argumentos soltos ---
$argumentos = [10.0, 2.0, 0.20];
echo "Spread chamando somar(...): " . somar(...$argumentos) . "\n";

// Com o spread, o array vira os argumentos soltos da chamada, na ordem
// em que os parametros foram declarados. str_pad($string, $length, ...).
$argumentos = ['PHP', 8, '.'];
echo "str_pad(...\$argumentos):   " . str_pad(...$argumentos) . "\n";
echo "somar(...[10.0, 2.0]):     " . somar(...[10.0, 2.0]) . "\n\n";

// --- Tipo anulavel: aceita o tipo ou null ---
function buscar(int|null $id): string
{
    return $id === null ? 'listar todos' : "buscar o id $id";
}

echo "Anulavel (?int ou int|null):\n";
echo "  " . buscar(null) . "\n";
echo "  " . buscar(42) . "\n\n";

// --- Primeiro operando obrigatorio, depois os opcionais, variadico por ultimo ---
function montar(string $cabecalho, array $colunas, string $separador = ' | ', bool $cabecalhoEmMaiusculo = true): string
{
    $linha = implode($separador, $colunas);

    return $cabecalhoEmMaiusculo ? "$linha\n" . str_repeat('-', mb_strlen($linha)) . "\n$linha" : $linha;
}

echo "Montagem de tabela simples:\n";
echo montar('Tabela', ['id', 'nome', 'nota']) . "\n";
echo "\nCom separador e sem maiuscula:\n";
echo montar('Tabela', ['id', 'nome'], ', ', false) . "\n";

Saída real

Com todos os argumentos:   69.93
Usando o padrao da qtd:   25.9
Usando so o desconto:     12.95

Argumento nomeado, fora de ordem:
  app@localhost:3306 (timeout 10s, variavel $senha configurada)
  app@localhost:5432 (timeout 5s, variavel $senha configurada) (posicional)

Nome de parametro errado: Unknown named parameter $hostname

Variadico:
  [erro] conexao recusada (sem detalhe)
  [erro] conexao recusada (tentativa, 3)
array(0) {
}

Spread chamando somar(...): 16
str_pad(...$argumentos):   PHP.....
somar(...[10.0, 2.0]):     20

Anulavel (?int ou int|null):
  listar todos
  buscar o id 42

Montagem de tabela simples:
id | nome | nota
----------------
id | nome | nota

Com separador e sem maiuscula:
id, nome
Aula 4

Passagem por referência e callbacks

Passagem por referência e callbacks

Por padrão, PHP passa argumentos por valor: a função trabalha numa cópia e a variável de quem chamou fica intacta. O & antes do $ inverte isso — a função passa a mexer na variável original. E callable é o tipo que declara que um parâmetro é outra função, o que permite passar comportamento em vez de dado.

Como se usa

function acrescentarTaxa(float &$valor, float $taxa): void { $valor *= (1 + $taxa); }
$preco = 100.00;
acrescentarTaxa($preco, 0.15);   // $preco passa a valer 115.00

O & vai no parâmetro, não na chamada. Na chamada, passa-se a variável normal, e o PHP cuida do resto. Com & no array_pop e no usort, a mesma regra vale.

Por valor contra por referência

SituaçãoComportamento
function f(int $x)$x é uma cópia; a variável de fora não muda
function f(int &$x)$x é a mesma variável; a de fora muda
Objeto passado por valora variável é cópia, mas o objeto é o mesmo (objetos são referências)
f($array)cópia rasa: os elementos escalares são copiados
f($array) com objetos dentroos objetos continuam compartilhados
unset($parametro) com &apaga a variável de quem chamou

A consequência prática: o & só é seguro quando o objetivo é justamente modificar. Função que recebe array só para ler funciona melhor sem &, porque evita que ela altere a lista de quem chamou por acidente.

Callbacks

callable é um tipo como int ou string, e vale para o nome de uma função, uma closure, o nome de um método ('Classe::metodo') ou um array [objeto, 'metodo']. É o tipo do segundo argumento de array_map, array_filter, array_reduce e do comparador de usort.

function aplicar(array $valores, callable $operacao): array { /* ... */ }
aplicar([1, 2, 3], 'strval');
aplicar([1, 2, 3], fn (int $n): int => $n ** 2);
Tipo de callbackComo passar
Função interna'strtoupper'
Função nomeada'minhaFuncao'
Closure$closure
Arrow functionfn ($v) => $v * 2
Método estático'MinhaClasse::processar'
Método de objeto[$objeto, 'processar']

Callback que devolve 1 ou 0 no lugar de true/false funciona igual em array_filter — o PHP converte para booleano. O que quebra é devolver string: '0' é falsy, '1' é truthy, mas 'sim' e 'nao' também são truthy, então um array_filter que devolve 'não' deixa a linha passar.

Exemplo

<?php
// ============================================================
// AULA 4 - Passagem por referencia e callbacks
// ============================================================

declare(strict_types=1);

// --- Por padrao o PHP passa por VALOR: a funcao trabalha numa copia ---
function formatarNome(string $nome): string
{
    $nome = strtoupper($nome);

    return $nome;
}

$titulo = 'relatorio mensal';
$resultado = formatarNome($titulo);
echo "Por valor: a variavel de fora continua '$titulo', o retorno e '$resultado'\n\n";

// --- Com &, a funcao mexe na variavel original. O & vai no PARAMETRO ---
function acrescentarDesconto(float &$valor, float $desconto): void
{
    $valor *= (1 - $desconto);
}

$preco = 100.00;
acrescentarDesconto($preco, 0.15);
echo "Por referencia: \$preco agora vale $preco\n";

$outroPreco = 50.00;
acrescentarDesconto($outroPreco, 0.50);
echo "                \$outroPreco agora vale $outroPreco\n\n";

// Funcao com & nao aceita literal nem resultado de expressao:
// o argumento tem que ser uma variavel.
try {
    acrescentarDesconto(80.00, 0.10);
} catch (Error $erro) {
    echo "Passar literal para parametro com &: " . $erro->getMessage() . "\n\n";
}

// --- Um array passa por valor tambem: a funcao nao altera a lista de fora ---
function adicionarItem(array $lista, string $item): array
{
    $lista[] = $item;

    return $lista;
}

$original = ['a', 'b'];
$comC = adicionarItem($original, 'c');
echo "Array por valor: a original tem " . count($original) . " itens, o retorno tem " . count($comC) . "\n";
echo "                 conteudo do retorno: " . implode(', ', $comC) . "\n\n";

// --- Callable: o parametro e outra funcao, nao um dado ---
function aplicar(array $valores, callable $operacao): array
{
    $saida = [];
    foreach ($valores as $item) {
        $saida[] = $operacao($item);
    }

    return $saida;
}

function dobro(int $n): int
{
    return $n * 2;
}

echo "Callback como funcao interna: " . implode(', ', aplicar([1, 2, 3], 'strval')) . "\n";
echo "Callback como arrow function: " . implode(', ', aplicar([1, 2, 3], fn (int $n): int => $n ** 2)) . "\n";
echo "Callback como funcao nomeada: " . implode(', ', aplicar([5, 6], 'dobro')) . "\n\n";

// --- callable aceita metodo estatico e metodo de objeto ---
class Calculadora
{
    public static function quadrado(float $n): float
    {
        return (float) ($n ** 2);
    }

    public function cubo(float $n): float
    {
        return (float) ($n ** 3);
    }
}

$calc = new Calculadora();
echo "Metodo estatico: " . implode(', ', aplicar([2, 3], Calculadora::class . '::quadrado')) . "\n";
echo "Metodo de objeto: " . implode(', ', aplicar([2, 3], [$calc, 'cubo'])) . "\n\n";

// --- O proprio PHP usa callable em array_map, array_filter e usort ---
$notas = [7.5, 4.0, 9.0, 6.5];
$maiores = array_filter($notas, fn (float $n): bool => $n >= 7.0);
usort($maiores, fn (float $a, float $b): int => $b <=> $a);
echo "array_filter + usort: " . implode(', ', $maiores) . "\n\n";

// --- O detalhe do array_filter com retorno string ---
$linhas = ['a' => 10, 'b' => 0, 'c' => 30];
$porBooleano = array_filter($linhas, fn (int $n): bool => $n > 0);
$porTexto    = array_filter($linhas, fn (int $n): string => $n > 0 ? 'sim' : 'nao');

echo "Callback devolvendo bool:  " . json_encode($porBooleano) . "\n";
echo "Callback devolvendo texto: " . json_encode($porTexto) . " (a string 'nao' e truthy)\n";
echo "No PHP 8 o falsy padrao e null: " . json_encode(array_values(array_filter([0, '', null, 'x'], fn ($v) => $v))) . "\n";

Saída real

Por valor: a variavel de fora continua 'relatorio mensal', o retorno e 'RELATORIO MENSAL'

Por referencia: $preco agora vale 85
                $outroPreco agora vale 25

Passar literal para parametro com &: acrescentarDesconto(): Argument #1 ($valor) could not be passed by reference

Array por valor: a original tem 2 itens, o retorno tem 3
                 conteudo do retorno: a, b, c

Callback como funcao interna: 1, 2, 3
Callback como arrow function: 1, 4, 9
Callback como funcao nomeada: 10, 12

Metodo estatico: 4, 9
Metodo de objeto: 8, 27

array_filter + usort: 9, 7.5

Callback devolvendo bool:  {"a":10,"c":30}
Callback devolvendo texto: {"a":10,"b":0,"c":30} (a string 'nao' e truthy)
No PHP 8 o falsy padrao e null: ["x"]