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

  1. 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 accept nulls & on __invoke replace the nulls 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 number 4 to the second paramater (0-based indexing) & bind 6 to the third paramater.

  2. I've used both a public __constructer & a static public bindParams(...)... Having both of those isn't really necessary. Writing new BoundClosure... is a bit more clear & concise than BoundClosure::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

See the full 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);