【问题标题】:PHP[OOP] - How to call class constructor manually?PHP[OOP] - 如何手动调用类构造函数?
【发布时间】:2010-12-26 08:07:13
【问题描述】:

请看下面的代码:

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 行手动调用它

有可能吗?如果那怎么办?有什么想法吗?

【问题讨论】:

  • 注意,如果构造函数的参数是通过引用传递的,那么call_user_func_array会失败!!

标签: php oop class constructor


【解决方案1】:

在构造对象时无法阻止调用构造函数(代码中的第 9 行)。如果您希望将 __construct() 方法中发生的某些功能推迟到构建之后,您应该将其移至另一个方法。该方法的一个好名字可能是init()

为什么不这样做呢?

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

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

编辑:我只是想到了一种可以防止构造函数被调用的骇人听闻的方法(有点)。你可以继承 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');

如果您正在处理一些由于某种原因无法更改并且需要解决编写不佳的构造函数的一些烦人行为的代码,这可能是有意义的。

【讨论】:

    【解决方案2】:

    请注意,如果构造函数(__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);
    

    【讨论】:

      【解决方案3】:

      我不知道使用 eval() 方法是否存在一些安全问题,但你可以让自己成为这样的函数:

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

      现在应该用正确的参数定义 $ob,我还没有测试过,也许代码有错误,但你明白了....

      【讨论】:

        【解决方案4】:

        如果不严格要求将实例化与初始化分开,还有其他两种可能性:第一种是静态工厂方法。

        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 或更高版本,则可以使用 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);
        }
        

        如果出于某种原因您需要在逻辑上将初始化与实例化分开,Asaph 描述的初始化方法非常有用,但如果支持上述用例是一种特殊情况,而不是常规要求,那么要求用户可能会很不方便在两个单独的步骤中实例化和初始化您的对象。

        工厂方法很好,因为它为您提供了一个调用方法来获取初始化实例。但是,该对象在同一个操作中初始化和实例化,因此如果您需要将两者分开,它将不起作用。

        最后,如果这种初始化机制不常用,我推荐使用 lambda,并且您不希望将几乎不会使用的初始化或工厂方法弄乱您的类定义。

        【讨论】:

          【解决方案5】:

          在 PHP 中,您可以在不调用构造函数的情况下创建对象。但这不能通过使用 new 而是通过反序列化对象实例来实现。

          然后可以手动调用构造函数。

          通常不需要。另见: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】:

            先构造你的对象,然后传递参数,你可以尝试这种方式:

            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 不支持方法重载。这种策略适用于 Java 等语言。
            • 是的,你是对的。我删除了构造函数重载.....最近做了太多java
            • 好的,编辑得很好,但是您的 $ob-&gt;init($params); 看起来与您对接受 3 个参数而不是单个数组的 init() 函数的定义不一致。顺便说一句:没有太多的 Java。
            猜你喜欢
            • 1970-01-01
            • 2023-03-21
            • 2016-01-29
            • 2012-05-09
            • 1970-01-01
            • 2023-03-31
            • 2011-05-22
            • 1970-01-01
            • 2014-10-12
            相关资源
            最近更新 更多