Acessando variável externa usando função anônima como parâmetros

93

Basicamente, eu uso esta função útil para processar linhas db (preste atenção no PDO e / ou outras coisas)

function fetch($query,$func) {
    $query = mysql_query($query);   
    while($r = mysql_fetch_assoc($query)) {
        $func($r);
    }
}

Com esta função, posso simplesmente fazer:

fetch("SELECT title FROM tbl", function($r){
   //> $r['title'] contains the title
});

Digamos que agora eu precise concatenar tudo $r['title']em uma var (este é apenas um exemplo).

Como posso fazer isso? Eu estava pensando algo assim, mas não é muito elegante:

$result = '';
fetch("SELECT title FROM tbl", function($r){
   global $result;
   $result .= $r['title'];
});

echo $result;
dinâmico
fonte

Respostas:

188

Você deve usar useconforme descrito nos documentos :

Os fechamentos também podem herdar variáveis ​​do escopo pai. Todas essas variáveis ​​devem ser declaradas no cabeçalho da função. Herdar variáveis ​​do escopo pai não é o mesmo que usar variáveis ​​globais. As variáveis ​​globais existem no escopo global, que é o mesmo, não importa qual função está sendo executada.

Código:

$result = '';
fetch("SELECT title FROM tbl", function($r) use (&$result) {
   $result .= $r['title'];
});

Mas cuidado (retirado de um dos comentários no link anterior):

Os parâmetros use () são vinculação antecipada - eles usam o valor da variável no ponto onde a função lambda é declarada, em vez do ponto onde a função lambda é chamada (vinculação tardia).

Xaerxess
fonte
1
Essa desaceleração global não deveria ser removida?
aziz punjani
19
1 para enfatizar o early binding. No entanto eu acho que no exemplo acima quando use (&$result)é passado por referência realmente não importa?
Dimitry K
4
@DimitryK Sim, a referência é usada aqui para contornar o comportamento padrão (vinculação inicial).
Xaerxess
3
@machineaddict O básico use é a vinculação inicial - se você quer dizer uma solução alternativa para a vinculação tardia - você passaria a variável usepor referência - usando &=> use (&$result)e alteraria a $resultvariável antes de chamar a função anônima (ou algo que a chame)
jave.web
1
Uma vez que as instâncias de classe são sempre passadas por referência, você não precisará usar & para elas. (a menos que você substitua completamente a instância).
Joel Harkes
0

Que tal reescrever 'fetch' para chamar $ func apenas uma vez?

function fetch($query,$func) {
    $query = mysql_query($query);   
    $retVal = array();
    while($r = mysql_fetch_assoc($query)) {
        $retVal[] = $r;
    }
    $func($retVal);
}

Desta forma, você chamaria $ func apenas uma vez e reprocessaria o array uma vez obtido? Não tenho certeza sobre o desempenho, mesmo que chamar 200 vezes uma função não soe como uma boa ideia.

user103307
fonte
Sim você está certo. No entanto, você pode usar mysql_fetch_row () ao invés de mysql_fetch_assoc () se você estiver interessado em ganhar alguns ms aqui e ali ... é terrivelmente difícil de lidar porque você teria que saber a posição das colunas. Ao fazer isso, você passa de 0,205 para 0,180 em 2.000 solicitações de 30 linhas cada.
user103307