Today I came across an interesting challenge in PHP : “How to instantiate a class with dynamic parameters ?"
Well, let’s try to explain. I need to instantiate a class with parameters provided by a yaml config file, something like this :
class: MyClass
params: [ "value 1", { key1: "value key 1", key2: "value key 2" ]
so in raw php this will be done by :
class myClass
{
public $arg1;
public $arg2;
public function __construct($arg1, $arg2)
{
$this->arg1 = $arg1;
$this->arg2 = $arg2;
}
}
$class = 'myClass';
$params = = array(
'value 1',
array('key1' => 'value key 1', 'key2' => 'value key 2')
);
$instance = new $class($params[0], $params[1]);
Ok now the problem is : we don’t know the number of parameters provided by the config file.
Let’s try to find a solution
Solution 1 : The bad
function create_instance($class, $params) {
$inline = array();
foreach($params as $index => $param)
{
$params[$index] = var_export($param, true);
}
$code = 'return new '.$class.'('.implode(', ', $params).');';
$function = create_function('', $code);
return $function();
}
$instance = create_instance($class, $params);
This solution uses the create_function to instantiate the class with the correct parameters. This solution works fine with the current configuration file but will failed with object instance parameter. You will get a nice php error : ‘Call to undefined method myClass::__set_state() in index.php : runtime-created '
This error is due to var_export which try to call an non existant __set_state method to the object. This method is cleary a bad solution.
Solution 2 : The Ugly
Ok, we can say that the limit of parameters is 5. So we can do someting like this :
function create_instance($class, $params) {
case(count($params)) {
case 0: return new $class;
case 1: return new $class($param[0]);
case 2: return new $class($param[0], $param[1]);
case 3: return new $class($param[0], $param[1], $param[2]);
// and so on ...
}
}
$instance = create_instance($class, $params);
This solution will work fine but it’s cleary an ugly assumption and an ugly code.
Solution 3 : The Good
The call_user_function_array()
can be our friend, but there is no way to have a valid callback function with the string ‘new $class’.
My friend Ally finds the solution by using the ReflexionClass :
function create_instance($class, $params) {
return call_user_func_array(
array(new ReflectionClass($class), 'newInstance'),
$params
);
}
After some look up to the ReflectionClass documentation, I find a more natural way to instantiate the class
function create_instance($class, $params) {
$reflection_class = new ReflectionClass($class);
return $reflection_class->newInstanceArgs($params);
}