Reputation: 282895
The signature for my method looks like this:
public function ProgramRuleFilter(&$program, $today=null) {
When I invoke it like this,
$programs = array_filter($programs, array($this,'ProgramRuleFilter'));
Everything works as expected. The ProgramRuleFilter
method updates the $program
array and then returns true/false if it succeeded which correctly filters $programs
.
However, now I want to pass an extra argument to the filter, $today
. How can I do that?
I'm trying to invoke it like this:
$programs = array_filter($programs, new CallbackArgs(array($this,'ProgramRuleFilter'),$today));
Using this little class as a wrapper:
class CallbackArgs {
private $callback;
private $args;
function __construct() {
$args = func_get_args();
$this->callback = array_shift($args);
$this->args = $args;
}
function __invoke(&$arg) {
return call_user_func_array($this->callback, array_merge(array($arg),$this->args));
}
}
But the programs aren't being updated, so somewhere along the line it lost the reference to the original object. I'm not sure how to fix this.
Upvotes: 3
Views: 3781
Reputation: 282895
I wrote a new method to handle it:
public static function array_filter_args($array, $callback) {
$args = array_slice(func_get_args(),2);
foreach($array as $key=>&$value) {
if(!call_user_func_array($callback, array_merge(array(&$value),$args))) {
unset($array[$key]);
}
}
return $array;
}
Called like this:
$programs = ArrayHelper::array_filter_args($programs, array($this,'ProgramRuleFilter'), $today);
I didn't know you could do this array(&$value)
, but I thought I'd try it, and it looks like it works. I'm guessing that array_merge
is the culprit that dereferences the variable otherwise.
Upvotes: 1
Reputation: 1485
The second argument to array_filter
must be a callback; which means that array_filter
itself will be calling your filter function. There is no way to tell array_filter
to call that function in any other way, so you'll need to find a way to get the value of $today
into your function some other way.
This is a perfect example of when to use a closure, which will let you bind some data (in this case, the value of $today
) into a function / callback. Assuming you are using PHP 5.3 or later:
// Assuming $today has already been set
$object = $this; // PHP 5.4 eliminates the need for this
$programs = array_filter( $programs, function( $x ) use ( $today, $object ){
return $object->ProgramRuleFilter( $x, $today );
});
This defines a closure inline, using the values of $today
and $object
from the parent scope, and then just calls your existing function ProgramRuleFilter
on that $object. (The somewhat unusual $object = $this
gets around the fact that otherwise, the closure would not be able to call a method on your object instance. But in PHP 5.4, you can replace $object
with $this
inside the closure.)
Now, this is a somewhat inelegant way to do it, because all this closure does is hand off the work to the ProgramRuleFilter
function. A better way would be to use the closure instead of the function. So:
// Assuming $today has already been set
$filter = function( $x ) use ( $today ){
// Cut and paste the contents of ProgramRuleFilter() here,
// and make it operate on $x and $today
};
$programs = array_filter( $programs, $filter );
Which variation works best for you will depend on the implementation of the rest of your app. Good luck!
Upvotes: 10