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

2019-02-04 18:26发布

Please see the code bellow:

01. class Test {
02.     public function __construct($param1, $param2, $param3) {
03.         echo $param1.$param2.$param3;
04.     }
05. }
06. 
07. $params = array('p1','p2','p3');
08. 
09. $ob = new Test;
10. 
11. if(method_exists($ob,'__construct')) {
12.     call_user_func_array(array($ob,'__construct'),$params);
13. }

Now, the problem is the constructor is called in line 09

But i want to call it manually at line 11-13

Is it possible? If then how? Any idea please?

6条回答
祖国的老花朵
2楼-- · 2019-02-04 18:43

If separating instantiation from initialization isn't strictly a requirement, there are two other possibilities: first, a static factory method.

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

    public static function CreateTest($param1, $param2, $param3) {
        return new Test($param1, $param2, $param3);
    }
}

$params = array('p1','p2','p3');

if(method_exists($ob,'__construct')) {
    call_user_func_array(array($ob,'CreateTest'),$params);
}

Or, if you're using php 5.3.0 or higher, you can use a lambda:

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

$params = array('p1','p2','p3');

$func = function ($arg1, $arg2, $arg3) {
    return new Test($arg1, $arg2, $arg3);
}

if(method_exists($ob,'__construct')) {
    call_user_func_array($func, $params);
}

The initialization method described by Asaph is great if for some reason you have a need to logically separate initialization from instantiation, but if supporting your use case above is a special case, not a regular requirement, it can be inconvenient to require users to instantiate and initialize your object in two separate steps.

The factory method is nice because it gives you a method to call to get an initialized instance. The object is initialized and instantiated in the same operation, though, so if you have a need to separate the two it won't work.

And lastly, I recommend the lambda if this initialization mechanism is uncommonly used, and you don't want to clutter your class definition with initialization or factory methods that will hardly ever be used.

查看更多
▲ chillily
3楼-- · 2019-02-04 18:46

to construct your object first and then pass parameters your could try this way:

class Test {
    public function __CONSTRUCT($p1="Bundy", $p2="house", $p3=10) {
        $this->init($p1, $p2, $p3);
    }
    public function init($p1, $p2, $p3) {
        //setup your object here
    }
}

then it is possible to construct the object and call

$ob->init($p1, $p2, $p3);

later.

查看更多
Rolldiameter
4楼-- · 2019-02-04 18:58

I don't know if there are some security concerns by using the eval() method, but you could make yourself a function like this:

function CreateObj($className,$params)
{
    $strArgs = '$params['.implode('],$params[',array_keys($params)).']';
    eval('$ob = new '.$className.'('.$strArgs.');');
    return $ob
}

And now $ob should now be defined with its correct parameters, i haven't tested it and maybe there is a mistake in the code, but you get the idea....

查看更多
Bombasti
5楼-- · 2019-02-04 19:04

In PHP you can create objects w/o calling the constructor. But that does not work by using new but by un-serializing an object instance.

The constructor can then be called manually.

Normally this is not needed. See as well: Loading an object from PHP session, does it call constructor?

<?php

class Test
{
    public function __construct()
    {
        echo '__construct called.',"\n";
    }
}

function old($class)
{
    return unserialize
    (
        sprintf
        (
            'O:%d:"%s":0:{}', 
            strlen($class), 
            $class
        )
    );
}

$test = old('Test');
$test->__construct();
查看更多
贪生不怕死
6楼-- · 2019-02-04 19:05

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.

查看更多
祖国的老花朵
7楼-- · 2019-02-04 19:09

Note that if the constructor (__construct method) contains arguments passed by reference, then the function:

call_user_func_array

will fail with an error.

I suggest you to use Reflection class instead; here is how you can do so:

// assuming that class file is already included.

$refMethod = new ReflectionMethod('class_name_here',  '__construct');
$params = $refMethod->getParameters();

$re_args = array();

foreach($params as $key => $param)
{
    if ($param->isPassedByReference())
    {
        $re_args[$key] = &$args[$key];
    }
    else
    {
        $re_args[$key] = $args[$key];
    }
}

$refClass = new ReflectionClass('class_name_here');
$class_instance = $refClass->newInstanceArgs((array) $re_args);
查看更多
登录 后发表回答