How to Call the Constructor with Call_User_Func_Array in PHP

How to call the constructor with call_user_func_array in PHP

You can use reflection like:

$reflect  = new ReflectionClass($class);
$instance = $reflect->newInstanceArgs($args);

As of PHP 5.6.0, the ... operator can also be used for this purpose.

$instance = new $class(...$args);

if(version_compare(PHP_VERSION, '5.6.0', '>=')){
$instance = new $class(...$args);
} else {
$reflect = new ReflectionClass($class);
$instance = $reflect->newInstanceArgs($args);
}

call_user_func_array passing arguments to a constructor

You can't call the constructor of $class like this:

call_user_func_array (new $class, $args);

That's no valid callback as first parameter. Let's pick this apart:

call_user_func_array (new $class, $args);

Is the same as

$obj = new $class;
call_user_func_array ($obj, $args);

As you can see, the constructor of $class has been already called before call_user_func_array comes into action. As it has no parameters, you see this error message:

Missing argument 1 for Product::__construct()

Next to that, $obj is of type object. A valid callback must be either a string or an array (or exceptionally a very special object: Closure, but that's out of discussion here, I only name it for completeness).

As $obj is an object and not a valid callback, so you see the PHP error message:

Object of class Product could not be converted to string.

PHP tries to convert the object to string, which it does not allow.

So as you can see, you can't easily create a callback for a constructor, as the object yet not exists. Perhaps that's why you were not able to look it up in the manual easily.

Constructors need some special dealing here: If you need to pass variable arguments to a class constructor of a not-yet initialize object, you can use the ReflectionClass to do this:

  $ref = new ReflectionClass($class);
$new = $ref->newInstanceArgs($args);

See ReflectionClass::newInstanceArgs

Use php callable to call constructor

If you really have to use call_user_func, this might work, though it's not clear why you would want to do this:

$reflection = new ReflectionClass("Foo");
$instance = $reflection->newInstanceWithoutConstructor();
call_user_func(array($instance, '__construct'));

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

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?

extended class constructor not fired when child class is called using call_user_func_array

I'll take a stab in the dark here, and say that you're actually doing this:

call_user_func_array(array('AuthController', 'login'), $data);

In other words, you're not passing an instance to call_user_func, you're just passing the string 'AuthController'. That means your method will get called statically. If you had error reporting and/or strict error reporting enabled, you should see a notice warning you about calling non-static methods statically.

The problem is (probably) that you're never actually instantiating your class, so no constructor is ever run. call_user_func won't instantiate it for you. You'll need to do that yourself:

call_user_func_array(array(new $controller, $route['action']), $data);
// ^^^

Calling non-static method with call_user_func_array in PHP

Use the notation [$objectHandle, "methodName"] to dynamically call a non-static method:

call_user_func_array([$this,$method], $arguments);

Live demo

call_user_func(array($this, $method), $par) from parent's constructor?

It knows about $this. The only error in your code is that you use reserved keyword parent

class Ancestor
{

public function __construct($method) {
call_user_func(array($this, $method), 1);
}

}

class Child extends Ancestor
{

public function __construct($method) {
parent::__construct($method);
}

protected function call_me_on_construct($par) {
echo $par;
}

}

$c = new child("call_me_on_construct");

Determine arguments for call_user_func_array

$arguments is a array that wil explode to the parameters of the function. If you call the do_Something_Else function the array must either be empty or the first element must be null or a instance of AnotherObject

In all other situations you get a E_RECOVERABLE_ERROR error.

To find out what argument needs to be passed you can use the Reflectionclass

Sample, needs some work to adjust tou your needs:

  protected function Build( $type, $parameters = array( ) )
{
if ( $type instanceof \Closure )
return call_user_func_array( $type, $parameters );

$reflector = new \ReflectionClass( $type );

if ( !$reflector->isInstantiable() )
throw new \Exception( "Resolution target [$type] is not instantiable." );

$constructor = $reflector->getConstructor();

if ( is_null( $constructor ) )
return new $type;

if( count( $parameters ))
$dependencies = $parameters;
else
$dependencies = $this->Dependencies( $constructor->getParameters() );

return $reflector->newInstanceArgs( $dependencies );
}

protected static function Dependencies( $parameters )
{
$dependencies = array( );

foreach ( $parameters as $parameter ) {
$dependency = $parameter->getClass();

if ( is_null( $dependency ) ) {
throw new \Exception( "Unresolvable dependency resolving [$parameter]." );
}

$dependencies[] = $this->Resolve( $dependency->name );
}

return ( array ) $dependencies;
}

PHP[OOP] - How to call class constructor manually?

It is not possible to prevent the constructor from being called when the object is constructed (line 9 in your code). If there is some functionality that happens in your __construct() method that you wish to postpone until after construction, you should move it to another method. A good name for that method might be init().

Why not just do this?

class Test {
public function __construct($param1, $param2, $param3) {
echo $param1.$param2.$param3;
}
}

$ob = new Test('p1', 'p2', 'p3');

EDIT: I just thought of a hacky way you could prevent a constructor from being called (sort of). You could subclass Test and override the constructor with an empty, do-nothing constructor.

class SubTest extends Test {
public function __construct() {
// don't call parent::__construct()
}

public function init($param1, $param2, $param3) {
parent::__construct($param1, $param2, $param3);
}
}

$ob = new SubTest();
$ob->init('p1', 'p2', 'p3');

This is might make sense if you're dealing with some code that you cannot change for some reason and need to work around some annoying behavior of a poorly written constructor.



Related Topics



Leave a reply



Submit