21

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?

Charles
  • 50,943
  • 13
  • 104
  • 142
Steve H
  • 946
  • 9
  • 22
  • 2
    Why do you want to do it like this? What problem are you trying to solve? – Skilldrick Dec 18 '09 at 16:11
  • @Skilldrick: in my case, trying to implement a simple dependency injection container. – Frank Schwieterman Mar 28 '10 at 20:58
  • 1
    Possible duplicate of [How to call the constructor with call\_user\_func\_array in PHP](http://stackoverflow.com/questions/2409237/how-to-call-the-constructor-with-call-user-func-array-in-php) – Maks3w Nov 13 '15 at 20:16

2 Answers2

25

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?

VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • Reflection are supported in PHP5 only, but this is the way I would do it. – Percutio Dec 18 '09 at 16:13
  • I would recommend against using any reflection in production code. It's very slow. – Lucas Oman Dec 18 '09 at 16:16
  • Shame it's PHP5 only, but thanks. Will probably use this with a fallback to an eval statement for PHP4. Speed isn't too much of an issue for my uses. – Steve H Dec 18 '09 at 17:52
  • Before using eval() you really should test the new $class(1,2,3) thing with php4 first. I'm quite certain it will work. And the factory is a great, basic pattern - definitely worth the time to read it up ;-) – VolkerK Dec 18 '09 at 18:23
  • 2
    Are there any downsides to using the `new $class($args)` method? – Shawn Feb 01 '13 at 18:26
10

Have a look at the Factory Method pattern and check out this example

From Wikipedia:

The factory method pattern is an object-oriented design pattern. Like other creational patterns, it deals with the problem of creating objects (products) without specifying the exact class of object that will be created.

If you don't want to use a dedicated Factory for this, you could still wrap Volker's code into a function, e.g.

/**
 * Creates a new object instance
 *
 * This method creates a new object instance from from the passed $className
 * and $arguments. The second param $arguments is optional.
 *
 * @param  String $className class to instantiate
 * @param  Array  $arguments arguments required by $className's constructor
 * @return Mixed  instance of $className
 */
function createInstance($className, array $arguments = array())
{
    if(class_exists($className)) {
        return call_user_func_array(array(
            new ReflectionClass($className), 'newInstance'), 
            $arguments);
    }
    return false;
}
Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559