PHP[OOP] - как вызвать конструктор класса вручную?

пожалуйста, смотрите код ниже:

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. }

теперь проблема конструктор вызывается в строке 09

но я хочу назвать ее вручную в строке 11-13

это возможно? Если да, то как? Любой идея пожалуйста?

6 ответов


невозможно предотвратить вызов конструктора при построении объекта (строка 9 в коде). Если есть какая-то функциональность, которая происходит в вашем __construct() метод, который вы хотите отложить до окончания строительства, вы должны переместить его в другой метод. Хорошим названием для этого метода может быть init().

почему бы просто не сделать это?

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

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

EDIT: я просто подумал о хакерском способе, которым вы могли бы предотвратить вызов конструктора (вроде). Вы можете подкласс Test и переопределить конструктор пустым конструктором ничего не делать.

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');

Это может иметь смысл, если вы имеете дело с некоторым кодом, который вы не можете изменить по какой-то причине и должны работать вокруг некоторого раздражающего поведения плохо написанного конструктора.


обратите внимание, что если конструктор (__construct метод) содержит аргументы, переданные ссылка, затем функция:

call_user_func_array

завершается с ошибкой.

Я предлагаю вам вместо этого использовать класс отражения; вот как вы можете это сделать:

// 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);

Если отделение экземпляра от инициализации не является строго обязательным, есть две другие возможности: во-первых, статический заводской метод.

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);
}

или, если вы используете php 5.3.0 или выше, вы можете использовать лямбда:

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);
}

метод инициализации, описанный Asaph, отлично подходит, если по какой-то причине вам нужно логически отделить инициализацию от создания экземпляра, но если поддержка вашего варианта использования выше является частным случаем, а не обычным требование, может быть неудобно требовать от пользователей создания экземпляра и инициализации объекта в два отдельных шага.

заводской метод хорош, потому что он дает вам метод для вызова, чтобы получить инициализированный экземпляр. Однако объект инициализируется и создается в одной и той же операции, поэтому, если вам нужно разделить их, он не будет работать.

и, наконец, я рекомендую лямбда, если этот механизм инициализации используется необычно, и вы не хотите загромождать ваше определение класса с инициализацией или заводскими методами, которые вряд ли когда-либо будут использоваться.


Я не знаю, есть ли какие-то проблемы с безопасностью, используя метод eval (), но вы можете сделать себе такую функцию:

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

и теперь $ob теперь должен быть определен с его правильными параметрами, я не тестировал его, и, возможно, в коде есть ошибка, но вы понимаете идею....


чтобы сначала построить свой объект, а затем передать параметры, вы можете попробовать так:

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
    }
}

тогда можно построить объект и вызвать

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

позже.


в PHP вы можете создавать объекты без вызова конструктора. Но это не работает с помощью new, а путем отмены сериализации экземпляра объекта.

конструктор можно вызвать вручную.

обычно это не требуется. Смотрите также: загрузка объекта из сеанса PHP, вызывает ли он конструктор?

<?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();