Funções anônimas e closures no PHP

Saiba mais sobre funções anônimas e closures em PHP e quais as vantagens e peculiaridades de cada um deles.

Ir para o artigo

O PHP 5.3 trouxe o conceito de funções anônimas e closures que, de forma simples, permitem a criação de funções que não têm um nome especificado.

Num primeiro momento, talvez seu uso possa não ser tão evidente e, não raramente, muitos programadores não entendem seu conceito e/ou desconhecem as potencialidades de funções anônimas no PHP. Se este é o seu caso, continue lendo e veja que, apesar de não terem tanto uso quanto as “tradicionais” funções do PHP, as funções anônimas são bastante úteis em determinados casos.

Explicação de funções anônimas

Comumente, a definição de uma função no PHP segue esta estrutura:

Quando você define uma função como esta, um nome lhe é atribuído (no caso, “myFunctionName”). O PHP então permite que, no código, seja possível se referir a esta função (o que é conhecido por “chamar a função”) usando seu nome. Por exemplo, para se chamar a função do exemplo acima, basta:

Funções anônimas no PHP são semelhantes a funções regulares que contêm um bloco de código que é executado quando são chamadas. Eles também podem aceitar argumentos e valores de retorno.

A principal diferença é que funções anônimas não têm nome. Aqui está um exemplo de código que cria uma função anônima simples:

Há duas diferenças sutis, mas importantes, entre o exemplo acima e uma definição de função normal:

  • Não há um nome entre a palavra-chave “function” e o parêntese de abertura. Isso informa ao PHP que uma função anônima está sendo criada.
  • Há um ponto-e-vírgula após a definição da função. Isso ocorre porque as definições de funções anônimas são expressões, enquanto definições de funções regulares são construções de código.

O código acima é perfeitamente válido, mas não é muito útil… Uma vez que a função anônima não tem nome, você não a pode referenciar em qualquer outro ponto do código, então, ela nunca pode ser chamada!

No entanto, uma vez que uma função anônima é uma expressão (tal como um número ou uma string), é possível fazer várias coisas úteis/interessantes. Por exemplo:

  • Atribuí-la a uma variável e, então, chamá-la mais tarde usando o nome dessa variável (é possível até guardar um monte de funções anônimas diferentes num array);
  • Passá-la a outra função, que pode chamá-la mais tarde (isso é conhecido como callback);
  • Retorná-la (return) de uma função externa, de modo que ela acesse variáveis ​​da “função exterior”. Isto é conhecido como um closure.

Atribuindo funções anônimas à variáveis

Quando uma função anônima é definida, é possível armazená-la em uma variável, assim como qualquer outro valor:

Uma vez que isso tenha sido feito, é possível chamar a função usando o nome da variável, tal como seria chamada uma função normal:

É possível, inclusive, armazenar várias funções dentro de um array:

Uma vez que isso tenha sido feito, o código pode decidir qual a função a ser chamada em tempo de execução. Por exemplo:

Usando funções anônimas como callbacks

Um uso comum de funções anônimas é para criar simples funções de callback inline. Uma função callback é uma função que foi criada e, em seguida, passada a outra função como um argumento. Uma vez que se tem acesso à sua função de retorno de chamada, a função “receptora” pode chamá-la sempre que seja preciso. Isto permite que, de uma maneira fácil, a customização do comportamento da função receptora.

Muitas funções nativas do PHP aceitam retornos de chamada e é possível, também, escrever suas próprias funções de aceitação de callbacks, como mostrado à frente.

Usando array_map() para executar uma callback em cada elemento de um array

A função array_map() do PHP aceita uma função de callback ou um array como argumento; então, ela caminha através dos elementos no array e, para cada elemento, chama a respectiva callback com o valor do elemento e a callback precisa retornar o novo valor a ser usado para o elemento; array_map(), em seguida, substitui o valor do elemento com valor de retorno da callback. Uma vez que todo o processo é feito, array_map() retorna o array modificado.

array_map() trabalha em uma cópia do array passado. O array original não é alterado.

Aqui está um exemplo de como é possível usar array_map() com uma função de callback:

Embora esse código funcione, é um pouco incômodo criar uma função regular separada apenas para atuar como uma callback simples. Ao invés disso, é possível criar a callback como uma função anônima:

Esta abordagem economiza uma linha de código, mas, mais importante, evita sobrecarregar o arquivo PHP com uma função separada que só é usada no contexto de callback.

Ordenação personalizada com usort()

Outro uso comum de callbacks é com a função usort() do PHP. Esta função permite ordenar arrays usando uma função de callback personalizada. Isso é particularmente útil quando é preciso ordenar um array de objetos ou arrays associativos, já que só uma função personalizada num contexto de programação específico pode resolver/atuar em tais complexas estruturas.

Por exemplo:

Agora, como exemplo, é preciso classificar o array em ordem de idade crescente. Não é possível usar funções nativas de ordenação de array, já que que estas não sabem nada sobre a chave “age”. Ao invés disso, é possível chamar usort() e passar em uma função anônima de callback que ordena o array por “age”, desse jeito:

Criando closures com funções anônimas

Outro uso comum de funções anônimas é para criar closures. Closure é uma função que mantém o acesso às variáveis ​​em seu escopo mesmo se este escopo não exista mais.

Por exemplo:

  1. Uma função, myFunction(), contém uma variável local (ou parâmetro) chamado $myVar;
  2. A função também define e retorna uma função anônima que acessa $myVar;
  3. Algum outro código chama myFunction() e recebe a função anônima, a qual ele armazena em $anonFunction. A essa altura, claro, myFunction() terminou de executar;
  4. Se o código agora chama $anonFunction(), a função anônima ainda pode acessar a variável local $myVar que estava em myFunction(), embora myFunction() não esteja mais sendo executada! A função anônima, juntamente com a sua referência para a variável $myVar, constituem um closure.
É um pouco confuso, mas o manual do PHP se refere a funções anônimas como closures. Eles não são a mesma coisa! Na verdade, funções anônimas são usadas para criar closures.

No início, pode ser difícil entender o conceito de closures, mas, uma vez que você tenha entendido, eles permitem que você escreva código limpo, poderoso e flexível. Alguns exemplos de closure podem tornar as coisas mais claras.

Um closure simples

Um exemplo simples de como criar um closure através de uma função anônima:

getGreetingFunction()

getGreetingFunction() inicializa uma variável local, $timeOfDay, e define e retorna uma função anônima (descrita abaixo).

A função anônima

A função anônima manipula a variável local $timeOfDay de getGreetingFunction(), convertendo sua primeira letra em maiúscula e retornando uma string de saudação que contém o valor de $timeOfDay.

  • A palavra-chave use. Normalmente, a função anônima não teria acesso à variável $timeOfDay, já que esta é local apenas no escopo de getGreetingFunction(). No entanto, a palavra-chave use diz ao PHP para deixar a função anônima acessar $timeOfDay. Isso permite criar o closure.
  • Ampersand (“e comercial”). O ampersand (&) antes de $timeOfDay diz ao PHP para passar por referência a variável $timeOfDay na função anônima ao invés de apenas copiar o valor da variável – isso permite que a função anônima manipule $timeOfDay diretamente. Estritamente falando, uma função de closure deve sempre acessar variáveis ​​em seu escopo “fechado” por referência. Dito isso, se não for preciso alterar o valor de uma variável, é possível omitir o ampersand para passar a variável por valor.
  • Chamando getGreetingFunction(). getGreetingFunction() é chamada e pega-se a função anônima de retorno, que é armazenada na variável $greetingFunction. Nesse ponto, getGreetingFunction() terminar sua execução. Em circunstâncias normais, sua variável local $timeOfDay teria saído do escopo e desaparecido. No entanto, já que se criou um closure através de função anônima (agora em $greetingFunction), a função anônima ainda pode acessar a variável $timeOfDay.
  • Chamando a função anônima. A função anônima é chamada. Ela manipula o valor da variável $timeofDay dentro do closure, convertendo sua primeira letra em maiúscula e, em seguida, retornando uma saudação contendo o novo valor de $timeOfDay.

Resumidamente, é assim que se cria um closure em PHP. Esse é um exemplo trivial, mas o importante a salientar é que a função anônima retornada ainda pode acessar a variável $timeOfDay mesmo depois de a função “enclausurada” ter terminado de executar.

Usando closures para passar dados adicionais a callbacks

Quando você passar uma função de callback para usort() e usort() chama uma callback, esta callback recebe os 2 argumentos passados ​​pela usort(), ou seja, os 2 valores no array para comparar.

Mas e se for preciso que a callback receba dados extra? Por exemplo, tomando o exemplo com usort() do início do artigo, seria possível passar um argumento adicional $sortKey para especificar qual é a chave que deve usada para classificar o array (“name” ou “age”). No entanto, usort() é que chama a callback. Uma vez que ela é chamada dentro do escopo de usort(), não se tem a chance de passar argumentos adicionais para a callback no momento em que este é chamado.

Closures para o resgate! Ao retornar a callback de dentro de outra função e criando um closure, é possível obter a função exterior a aceitar $sortKey como parâmetro e, em seguida, passar $sortKey para a callback dentro do closure. Aqui está o código completo:

Array de pessoas

Primeiro, a criação do array de pessoas. Cada pessoa é representada por um array associativo com duas chaves: name e age.

getSortFunction()

Em seguida, foi definida a função getSortFunction() que aceita um parâmetro $SortKey. Esta é a chave usada para ordenar o array.

Criação do closure

getSortFunction() define e retorna uma função anônima, que é a função de ordenação que será passada para usort(). A função anônima aceita os habituais 2 elementos de array para classificar, $personA e $personB, mas, também, usa a palavra-chave use para acessar o parâmetro $sortKey, que está dentro do escopo da função enclausurada getSortFunction(). Isso cria o closure. Em seguida, $sortKey é usada para determinar qual chave usar para a comparação (ou name ou age).

Uma vez que um closure foi criado, a função anônima ainda tem acesso ao valor do parâmetro $sortKey após getSortFunction() terminar sua execução.

Ordenando o array

Finalmente, o código ordena o array, primeiro pelo nome e, em seguida, pela idade. Para fazer isso, ele chama getSortFunction() passando a chave de classificação (nome ou age). getSortFunction() retorna uma função anônima apropriada que usa a chave de ordenação fornecida. O código, em seguida, passa essa função anônima para usort(), juntamente com o array para ordenar.

É possível usar esse truque a qualquer momento que seja preciso passar dados adicionais para uma callback.

Conclusão sobre closures e funções anônimas no PHP

Closures e funções anônimas em PHP são um tema amplo e sutil que, como você viu, demandam uma tenção especial no início do aprendizado, já que possuem algumas peculiaridades interessantes e bastante úteis.

Tome este artigo como uma base de referência para o aprendizado, leia quantas vezes forem necessárias até entender tudo e, depois, continue seus estudos sobre o tema, procurando exemplos e códigos que aplicam funções anônimas e/ou closures para se familiarizar ainda mais.

Depois disso, se tiver alguma dica interessante a respeito ou alguma dica ou macete importante de como usá-los em trechos de códigos específicos, coloque nos comentários para também ajudar quem está interessado em aprender mais sobre funções anônimas e closures em PHP.

Usamos cookies para melhorar sua experiência e para ajudar a entender como nosso site é usado. Ao utilizar este site, você aceita o uso de cookies. Consulte nossa política de privacidade para obter mais informações.