Quando você passa uma variável para uma função no PHP, ela recebe por padrão uma cópia do valor — mexer nessa cópia lá dentro não altera a sua variável original. Colocar um & antes do parâmetro muda isso: a função passa a receber a própria variável, e qualquer alteração feita lá dentro aparece fora. É essa a diferença inteira. O que confunde vem depois: arrays também são copiados (mesmo sendo "grandes"), e objetos parecem referência mas não são — uma distinção que só aparece num caso específico, e que este artigo demonstra na prática.
O básico: cópia vs a própria variável
function dobra(int $n): void { $n *= 2; }
$x = 5;
dobra($x);
echo $x; // 5 — a função dobrou a CÓPIA dela, não o seu $x
O $n de dentro da função é uma variável nova, que começou com o mesmo valor. Ela vive e morre ali. Com o &, não existe variável nova: $n e $x passam a ser dois nomes para o mesmo valor na memória.
function adicionarTaxa(float $valor): void {
$valor = $valor * 1.1; // altera só a cópia local
}
$preco = 100.0;
adicionarTaxa($preco);
echo $preco; // 100 — nada mudou (bug silencioso)function adicionarTaxa(float &$valor): void {
$valor = $valor * 1.1; // altera a variável de fora
}
$preco = 100.0;
adicionarTaxa($preco);
echo $preco; // 110Repare que o & fica na declaração da função, não na chamada. Passar & na hora de chamar (adicionarTaxa(&$preco)) foi removido do PHP na versão 5.4 e hoje é erro de sintaxe.
Arrays também são cópias (e isso não custa caro)
Aqui mora a primeira surpresa: no PHP, array é um valor, não um objeto. Ele é copiado como qualquer escalar:
function adicionar(array $lista): void { $lista[] = 99; }
$itens = [1, 2];
adicionar($itens);
echo json_encode($itens); // [1,2] — o 99 morreu dentro da função
A reação natural é: "então passar um array grande por valor é lento, melhor usar & por performance". Não é. O PHP usa uma técnica chamada copy-on-write: ao copiar, ele não duplica nada — apenas marca que os dois nomes apontam para os mesmos dados. A duplicação só acontece se (e quando) alguém escrever. Medindo com um array de 200 mil itens:
$grande = range(1, 200000);
$copia = $grande; // custo de memória: 0 bytes
$copia[0] = 'x'; // agora sim: +4,2 MB (duplicou de verdade)
Ou seja: se a função só lê o array, passar por valor é gratuito. Usar & "por otimização" numa função que não modifica nada não acelera nada — só torna o código mais difícil de entender. Referência é uma decisão de semântica (quero alterar o original?), nunca de performance.
Objetos: parecem referência, mas não são
Esta é a parte que gera mais confusão — e a explicação circula errada por aí. Objetos não são passados por referência. Eles são passados por valor, só que o valor de uma variável de objeto não é o objeto: é um identificador que aponta para ele (um handle). A cópia copia o identificador, e o identificador copiado aponta para o mesmo objeto.
Parece filigrana, mas produz um comportamento observável — e é assim que se prova:
1. Alterar uma propriedade: afeta o original
A função recebe uma cópia do identificador, mas ele aponta para o mesmo objeto. Mexer no objeto é visível de fora.
class Conta { public int $saldo = 100; }
function saca(Conta $c): void { $c->saldo -= 30; }
$conta = new Conta;
saca($conta);
echo $conta->saldo; // 70 ← mudou!
2. Reatribuir o parâmetro: NÃO afeta o original
Aqui está a prova. Trocar $c por um objeto novo só troca a cópia do identificador, que é local. Sua variável lá fora continua apontando para o objeto antigo.
function troca(Conta $c): void {
$c = new Conta; // $c local passa a apontar para outro objeto
$c->saldo = 0;
}
troca($conta);
echo $conta->saldo; // 70 ← NÃO mudou
3. Com &, aí sim reatribui
Com referência, $c e $conta são o mesmo nome. Trocar um troca o outro.
function trocaRef(Conta &$c): void {
$c = new Conta;
$c->saldo = 0;
}
trocaRef($conta);
echo $conta->saldo; // 0 ← agora mudou
A conclusão
Se objetos fossem realmente passados por referência, o passo 2 teria funcionado. Ele não funciona — logo, o que é copiado é o identificador, não o objeto. Você só precisa de & com objetos quando quer substituir o objeto inteiro, o que é raro.
// alterar estado → não precisa de &
// substituir o objeto → precisa de &
Diagnóstico: essa função vai mexer na minha variável?
A resposta está sempre na assinatura: se o parâmetro tem &, a função pode alterar a sua variável. Sem &, não pode (exceto propriedades de objeto, pelo motivo acima). Vale saber que várias funções nativas recebem por referência e é por isso que "alteram sozinhas":
$nums = [3, 1, 2];
sort($nums); // sort(array &$array) — altera o original
echo json_encode($nums); // [1,2,3]
preg_match('/(\d+)/', 'abc 42', $m); // $m é preenchido por referência
echo json_encode($m); // ["42","42"]
sort, shuffle, array_push, array_shift, settype e o terceiro argumento de preg_match funcionam assim. Se uma função "não devolve nada" mas mexe nos seus dados, procure o & na documentação dela.
Uma consequência prática: parâmetro por referência exige uma variável, não um valor solto. Passar um literal dá erro:
function f(&$x) { $x = 1; }
f(5);
// Error: f(): Argument #1 ($x) could not be passed by reference
A armadilha clássica do foreach
Uma função recebe function processar(array $dados) (sem &) e faz $dados['status'] = 'ok';. Depois da chamada, o array de fora tem a chave status?
Ver resposta
Resposta certa: Não — arrays são copiados por valor; a alteração morre na função
Array no PHP é valor, não objeto — diferente de linguagens como Java ou JavaScript, onde array é referência. Sem &, a função trabalha numa cópia (que graças ao copy-on-write só é materializada nessa hora). Para o efeito persistir, a função precisa retornar o array modificado (o caminho recomendado) ou declarar array &$dados.
Quando usar & — e quando não usar
Use quando:
- A função existe para modificar o que você passou, e isso é evidente pelo nome (
ordenarLista(array &$lista)). - Você precisa de um "parâmetro de saída" extra, no estilo do
preg_match($p, $s, $m). - Precisa substituir um objeto inteiro dentro da função (caso raro).
Evite quando:
- Você poderia simplesmente retornar o valor novo.
$lista = ordenar($lista);é mais fácil de ler, testar e depurar do que uma função que muda coisas à distância. - O argumento é "performance" — o copy-on-write já resolve isso, como os números acima mostram.
- A função faz várias coisas e o
&vira um efeito colateral escondido. Alteração invisível é a fonte de bug mais cara de rastrear: o valor muda e nada no ponto da chamada indica isso.
A regra de bolso: prefira retornar; use & quando modificar o original for o propósito declarado da função, e deixe isso óbvio no nome.
Resumo de decisão
| Situação | Comportamento | Precisa de &? |
|---|---|---|
| Escalar (int, string, bool) | Cópia do valor | Sim, para alterar o original |
| Array | Cópia do valor (copy-on-write) | Sim, para alterar o original |
| Objeto — alterar propriedade | Mesma instância | Não |
| Objeto — substituir por outro | A troca é local | Sim |
$b = $a com objetos |
Compartilham a instância | Use clone para separar |
| Só ler um array grande | Cópia custa 0 bytes | Não (não é otimização) |
| Poderia retornar o resultado | — | Não — prefira return |
Em uma frase: o PHP copia valores, e o & desliga essa cópia — objetos só parecem exceção porque o que é copiado é o endereço deles, não o conteúdo.
A seção "References Explained" cobre o que uma referência realmente é no PHP (dois nomes para o mesmo valor), o que ela faz e o que ela não faz — inclusive por que referência não é ponteiro de C.
A página de passagem por referência detalha as regras dos parâmetros com &: o que pode ser passado, o que gera erro e como as funções nativas usam esse recurso.