2009-02-06 - How to instantiate a PHP class with dynamic parameters

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);
  }

Comments

comments powered by Disqus