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
Custom Order Status Background Button Color in Woocommerce 3.3 Admin Order List
Notice: Unknown: Skipping Numeric Key 1 in Unknown on Line 0
How to Use PHP to Dynamically Publish an Ical File to Be Read by Google Calendar
How to Store an Array in a File to Access as an Array Later with PHP
Casperjs Passing Data Back to PHP
Getting Data with Utf-8 Charset from Mssql Server Using PHP Freetds Extension
Track When User Hits Back Button on the Browser
MySQL Select Query Within a Serialized Array
Why Do Changes to Some PHP Files Take So Long to Show on the Live Site
Using Square Brackets in Hidden HTML Input Fields
Programmatically Access Currency Exchange Rates
PHP Sessions with Disabled Cookies, Does It Work
Custom Laravel Validation Messages
How to Call the Constructor with Call_User_Func_Array in PHP