Accessing outside variable using anonymous function as params
Asked Answered
M

2

108

Basically I use this handy function to processing db rows (close an eye on PDO and/or other stuff)

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

With this function I can simply do:

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

Let's say now I need to concatenate all $r['title'] in a var (this is just an example).

How could I do that? I was thinking something like this, but it's not very elegant:

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

echo $result;
Monophony answered 6/12, 2011 at 17:11 Comment(1)
Same technique on an earlier asked question: How to increment count in the replacement string when using preg_replace?Ursel
S
212

You have to use use as described in docs:

Closures may also inherit variables from the parent scope. Any such variables must be declared in the function header. Inheriting variables from the parent scope is not the same as using global variables. Global variables exist in the global scope, which is the same no matter what function is executing.

Code:

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

But beware (taken from one of comments in previous link):

use() parameters are early binding - they use the variable's value at the point where the lambda function is declared, rather than the point where the lambda function is called (late binding).

Shell answered 6/12, 2011 at 17:15 Comment(8)
Shouldn't that global deceleration be removed ?Latent
+1 for emphasizing the early binding. However I guess in the example above when use (&$result) is passed by reference it doesn't really matter?Defrock
@DimitryK Yes, reference is used here to bypass default behaviour (early binding).Underling
Thanks so very much. I have been digging all around to do somethign that in JavaScript would have been obvious.Sexed
@Xaerxess: is there a workaround for the early binding of use?Giveaway
@Giveaway The basic use is early binding - if you mean workaround for late binding - you would pass the variable through use by reference - using & => use (&$result) and alter $result variable before you call the anonymous function (or something, that calls it)Commencement
Since class instances are always passed by reference you wont need to use & for them. (unless you completely overwrite the instance).Aenneea
For JS people: early binding means lexical scope (just like in a JS closure) while late binding means dynamic scope.Lurk
B
0

What about rewriting 'fetch' to call $func only once ?

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

This way you would call $func only once and re-process the array once fetched? Not sure about the performance even tho calling 200 times a function doesn't sound like a good idea.

Boddie answered 6/12, 2011 at 17:20 Comment(1)
Yes you are right. However, you could use mysql_fetch_row() instead of mysql_fetch_assoc() if you're that interested in gaining a few ms here and there... it's just awfully hard to deal with as you'd have to know your columns position. By doing so, you pass from 0.205 to 0.180 on 2000 requests of 30 rows each.Boddie

© 2022 - 2024 — McMap. All rights reserved.