A diferença cabe numa frase: callable é um tipo que descreve qualquer coisa que dá para chamar — o nome de uma função numa string, um par [objeto, 'método'], um objeto com __invoke, uma função anônima. Já Closure é uma classe concreta, o objeto que o PHP cria quando você escreve function () {} ou fn () =>. Daí a relação: todo Closure é callable, mas nem todo callable é Closure. Entender isso resolve de imediato três dúvidas práticas — por que public callable $cb; dá fatal error, por que $this->cb() não chama a sua closure, e o que a sintaxe strtoupper(...) do PHP 8.1 realmente faz.
As sete formas de callable
O jeito mais rápido de fixar a diferença é ver as formas lado a lado. Todas as sete abaixo funcionam em array_map, usort e qualquer parâmetro tipado como callable — mas só duas são objetos Closure:
1. Nome de função numa string
A forma mais antiga. É só uma string; o PHP resolve o nome na hora de chamar.
$c = 'strtoupper';
is_callable($c); // true
$c instanceof Closure; // false — é uma string!
2. Método estático (duas escritas)
Tanto o array [Classe::class, 'metodo'] quanto a string 'Classe::metodo' funcionam.
$c1 = [Fmt::class, 'up'];
$c2 = 'Fmt::up';
is_callable($c1); // true
$c1 instanceof Closure; // false — é um array
3. Método de instância
Um array com o objeto e o nome do método. Muito usado como callback.
$c = [$obj, 'low'];
is_callable($c); // true
$c instanceof Closure; // false — array de novo
4. Objeto com __invoke
Qualquer objeto cuja classe tenha o método mágico __invoke pode ser chamado como se fosse função.
class Fmt { public function __invoke($s) { return "[$s]"; } }
$obj = new Fmt;
$obj('oi'); // "[oi]"
$obj instanceof Closure; // false — é um Fmt, não um Closure
5. Função anônima e arrow function
Só aqui o PHP cria um objeto da classe Closure. As duas formas produzem a mesma classe.
$c1 = function ($s) { return "<$s>"; };
$c2 = fn ($s) => "{{$s}}";
$c1 instanceof Closure; // true
$c2 instanceof Closure; // true
O resumo
Sete formas, todas is_callable() === true. Apenas duas são instanceof Closure. callable é a descrição; Closure é uma das implementações.
string | 'Classe::m' | [Classe,'m'] | [$obj,'m'] | __invoke → callable, NÃO Closure
function () {} | fn () => → callable E Closure
Por que callable não pode ser tipo de propriedade
Aqui está a consequência prática mais concreta da diferença — e um erro que trava muita gente:
class Botao {
public callable $aoClicar; // Fatal error: Property Botao::$aoClicar
} // cannot have type callable
O motivo não é capricho: callable depende do contexto onde é resolvido. A string 'self::publico', por exemplo, só faz sentido dentro da classe que a escreveu; [$this, 'metodo'] depende do objeto atual; e a visibilidade importa — is_callable([$obj, 'metodoPrivado']) devolve true de dentro da classe e false de fora. Um valor cujo significado muda conforme quem olha não pode ser guardado numa propriedade com segurança, então o PHP simplesmente proíbe.
Closure, por ser uma classe comum, não tem esse problema: ele já é um objeto autocontido, com o escopo embutido.
class Botao {
public callable $aoClicar; // Fatal error na hora de carregar a classe
}
// e mesmo em parâmetro, callable aceita qualquer coisa:
function registrar(callable $cb) { }
registrar('strtoupper'); // passa uma string —
// você não pode chamar ->bindTo() nelaclass Botao {
public function __construct(
public Closure $aoClicar, // funciona, e garante um objeto
) {}
}
function registrar(Closure $cb) { }
registrar(strtoupper(...)); // convertido em Closure (PHP 8.1+)
registrar(fn () => null); // sempre um objeto de verdadeA ponte: strtoupper(...) do PHP 8.1
Se Closure é o tipo mais seguro mas você tem um callable "velho" em mãos, existe uma conversão de uma linha. A first-class callable syntax (PHP 8.1) transforma qualquer callable num Closure — os ... são literais, você digita exatamente assim:
$f = strtoupper(...); // Closure a partir de uma função
$g = $obj->low(...); // Closure a partir de um método de instância
$h = Fmt::up(...); // Closure a partir de um método estático
$f instanceof Closure; // true
Antes do 8.1, o equivalente era Closure::fromCallable('strtoupper') — ainda funciona e é o que você usa em código que precisa rodar em PHP 7.1–8.0. A vantagem da sintaxe nova, além de mais curta, é que ela é resolvida em tempo de compilação: erro de digitação no nome do método vira erro na hora, não um callable inválido descoberto só quando alguém chama.
O que só o Closure oferece
Como Closure é uma classe, ele tem métodos — e é aí que ele deixa de ser "só uma função" e vira uma ferramenta:
Captura de escopo. Uma closure criada dentro de um método já vem com o $this embutido, sem você fazer nada:
class Carrinho {
private array $itens = ['a', 'b', 'c'];
public function contador(): Closure {
return function () { return count($this->itens); }; // $this vem junto
}
}
(new Carrinho)->contador()(); // 3 — acessa a propriedade privada
Reatribuir o $this com bindTo / call. Você pode "emprestar" uma closure para outro objeto, inclusive furando o encapsulamento — recurso valioso em testes:
$espiao = function () { return $this->itens; };
$espiao->call(new Carrinho); // ["a","b","c"]
Closure::bind($espiao, new Carrinho, Carrinho::class)(); // idem
Controle de captura com use. A closure clássica congela uma cópia do valor no momento em que é criada; com & ela acompanha a variável:
$x = 1;
$foto = function () use ($x) { return $x; };
$vivo = function () use (&$x) { return $x; };
$x = 99;
$foto(); // 1 ← fotografou na criação
$vivo(); // 99 ← acompanha
A arrow function captura o escopo automaticamente (sem escrever use), mas sempre por valor — não existe captura por referência em fn () =>. Se você precisa que a variável externa mude junto, use a forma longa com use (&$x).
Por que class Botao { public callable $aoClicar; } gera um fatal error, enquanto public Closure $aoClicar; funciona?
Ver resposta
Resposta certa: Porque callable depende do contexto e não é um tipo válido para propriedades
O significado de um callable muda conforme onde ele é resolvido: 'self::metodo' só vale dentro da classe, [$this, 'm'] depende do objeto atual, e a visibilidade altera o resultado de is_callable(). Guardar isso numa propriedade e resolver depois, em outro escopo, seria ambíguo — então o PHP proíbe (Property X cannot have type callable). Closure não tem esse problema porque é um objeto que já carrega o escopo dentro de si.
Qual usar em cada lugar
| Onde | Use | Por quê |
|---|---|---|
| Parâmetro de função pública/API | callable |
Aceita todas as formas — mais flexível para quem chama |
Parâmetro quando você vai usar bindTo/call |
Closure |
Só o objeto tem esses métodos |
| Propriedade de classe | Closure |
callable é proibido (fatal error) |
| Retorno de função | Closure |
callable como retorno pode devolver uma string — o chamador não pode confiar que é objeto |
| Converter um callable "velho" | funcao(...) (8.1+) ou Closure::fromCallable() |
Vira um Closure de verdade |
| Chamar closure de propriedade | ($this->cb)() |
$this->cb() procura um método |
| Verificar se dá para chamar | is_callable($x) |
Cobre as sete formas; respeita visibilidade |
| Verificar se é objeto closure | $x instanceof Closure |
Só as anônimas e arrow fn passam |
A regra de bolso que resume o artigo: aceite callable na entrada (seja generoso com quem chama), guarde e devolva Closure (seja específico no que você garante) — e converta com (...) na fronteira entre os dois.
A página do tipo callable lista todas as formas aceitas pelo PHP, com as regras de resolução e visibilidade de cada uma — inclusive as que estão sendo depreciadas.
A classe Closure tem métodos que transformam funções anônimas em ferramenta: bindTo, call, fromCallable. Vale ver a interface completa para entender o que só o objeto permite.
A sintaxe funcao(...) do PHP 8.1 é a ponte oficial entre os dois mundos. A página explica o que ela aceita, o que resolve em tempo de compilação e como se compara ao Closure::fromCallable().