Bind Paramaters to a Callable in PHP
This could save a few keystrokes in some cases. It's certainly not important functionality, but it could be useful in some cases.
Create a class that takes a callable and list of paramaters into its constructor, then implement the __invoke
magic method to make the new object callable as well. You can wrap the instantiation in a static function (BoundClosure::bind($callable,$params)
) or in a global function (function bindParams($callable,$params)
).
This class will do it. Just instantiate it (ex: new BoundClosure('implode','-')
) or use the static (ex: BoundClosure::bindParams('implode','-')
);
class BoundClosure {
public function __construct($callable,...$params){
$this->callable = $callable;
$this->params = $params;
}
public function __invoke(...$args){
$params = array_merge($this->params,$args);
return call_user_func_array($this->callable,$params);
}
static function bindParams($callable,...$params){
$closure = new BoundClosure($callable,...$params);
return $closure;
}
}
Some improvements / tips
-
What if you want to leave the first paramater open & bind to the second paramater? For example, maybe you want to get multiple substrings using the different haystacks, but the same start & length. So you want to change
substr($haystack,4,6)
(to get the 4th-10th characters) to$mySubstr($haystack)
. To do this, the closure-paramater binding function could acceptnull
s & on__invoke
replace thenull
s with the passed in params, respectively. You could also use a numbered binding mechanism & allow for chained binding, with something like:$mySubstr = BoundClosure::bind('substr')->bind(4,1)->bind(6,2)
to bind the number4
to the second paramater (0-based indexing) & bind6
to the third paramater. -
I've used both a
public __construct
er & astatic public bindParams(...)
... Having both of those isn't really necessary. Writingnew BoundClosure...
is a bit more clear & concise thanBoundClosure::bindParams...
. Furthermore, the real keystroke saving version would utilize a global function. I stick, very strongly, to OOP, so I won't use a global function in my own code, but it could look something like this.
function closureBind($callable,...$params){
$obj = new Class(){
public function __invoke(...$args){
$params = array_merge($this->params,$args);
return call_user_func_array($this->callable,$params);
}
};
$obj->callable = $callable;
$obj->params = $params;
return $obj;
}
The Test
I'm testing with the OOP class, NOT the global function & I'll be using the bind to implode a 2-dimensional array without writing a function.
$data = [
'animal' => [
'dog','cat','lizard'
],
'vehicle' => [
'car','truck','boat',
],
];
//Since $data's elements are 1-dimensional arrays, mapping 'implode' will concatenate their values.
//I couldn't normally map 'implode', because it requires the 'glue' to be passed as the first paramater
$implode = new BoundClosure('implode','-');
$data = array_map($implode,$data);
$str = implode('-',$data);
var_dump($str);