Search code examples
phpclassphp4

Is there a call_user_func() equivalent to create a new class instance?


How can I create a class with a given array of arguments to be sent to the constructor? Something along the lines of:

class a {
    var $args = false;
    function a() {$this->args = func_get_args();}
}

$a = call_user_func_array('new a',array(1,2,3));
print_r($a->args);

Ideally this needs to work, without modification to the class, in both PHP4 and PHP5. Any ideas?


Solution

  • ReflectionClass:newInstance() (or newInstanceArgs()) let's you do that.

    e.g.

    class Foo {
      public function __construct() {  
        $p = func_get_args();
        echo 'Foo::__construct(', join(',', $p), ') invoked';
      }
    }
    
    $rc = new ReflectionClass('Foo');
    $foo = $rc->newInstanceArgs( array(1,2,3,4,5) );
    

    edit: without ReflectionClass and probably php4 compatible (sorry, no php4 at hand right now)

    class Foo {
      public function __construct() {  
        $p = func_get_args();
        echo 'Foo::__construct(', join(',', $p), ') invoked';
      }
    }
    
    $class = 'Foo';
    $rc = new $class(1,2,3,4);
    

    speed comparison: Since the speed of reflection has been mentioned here's a little (synthetic) test

    define('ITERATIONS', 100000);
    
    class Foo {
      protected $something;
      public function __construct() {
        $p = func_get_args();
        $this->something = 'Foo::__construct('.join(',', $p).')';
      }
    }
    
    $rcStatic=new ReflectionClass('Foo'); 
    $fns = array(
      'direct new'=>function() { $obj = new Foo(1,2,3,4); },
      'indirect new'=>function() { $class='Foo'; $obj = new $class(1,2,3,4); }, 
      'reflection'=>function() { $rc=new ReflectionClass('Foo'); $obj = $rc->newInstanceArgs( array(1,2,3,4) ); },
      'reflection cached'=>function() use ($rcStatic) { $obj = $rcStatic->newInstanceArgs( array(1,2,3,4) ); },
    );
    
    
    sleep(1);
    foreach($fns as $name=>$f) {
      $start = microtime(true);
      for($i=0; $i<ITERATIONS; $i++) {
        $f();
      }
      $end = microtime(true);
      echo $name, ': ', $end-$start, "\n";
      sleep(1);
    }
    

    which prints on my (not so fast) notebook

    direct new: 0.71329689025879
    indirect new: 0.75944685935974
    reflection: 1.3510940074921
    reflection cached: 1.0181720256805
    

    Isn't that bad, is it?