Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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?

like image 451
Steve H Avatar asked Dec 18 '09 16:12

Steve H


People also ask

How do I create a new instance of a class in PHP?

new ¶ To create an instance of a class, the new keyword must be used. An object will always be created unless the object has a constructor defined that throws an exception on error. Classes should be defined before instantiation (and in some cases this is a requirement).

What is Call_user_func?

The call_user_func() is an inbuilt function in PHP which is used to call the callback given by the first parameter and passes the remaining parameters as argument. It is used to call the user-defined functions.


2 Answers

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?

like image 50
VolkerK Avatar answered Sep 21 '22 14:09

VolkerK


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; } 
like image 35
Gordon Avatar answered Sep 18 '22 14:09

Gordon