【问题标题】:Best way to do multiple constructors in PHP在 PHP 中执行多个构造函数的最佳方法
【发布时间】:2010-12-14 13:29:27
【问题描述】:

您不能在 PHP 类中放置两个具有唯一参数签名的 __construct 函数。我想这样做:

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($id){
       $this->id = $id;
      // other members are still uninitialized
   }

   public function __construct($row_from_database){
       $this->id = $row_from_database->id;
       $this->name = $row_from_database->name;
       // etc.
   }
}

在 PHP 中最好的方法是什么?

【问题讨论】:

  • 我也梦想命名构造函数和方法重载+1
  • 在我的例子中,我想要一个受保护的构造函数,它比公共的参数少一个 - 为了标准化它的工厂方法。我需要一个能够创建自身副本的类,并且工厂位于抽象类中,但具体类可能具有需要第二个参数的构造函数 - 抽象类不知道。
  • 不是真正有价值的东西,而是我前段时间偶然发现的东西:date_c.php 中的 DatePeriod 类有多个构造函数。但我不知道 PHP 在内部用它做了什么。

标签: php constructor multiple-constructors


【解决方案1】:

我可能会这样做:

<?php

class Student
{
    public function __construct() {
        // allocate your stuff
    }

    public static function withID( $id ) {
        $instance = new self();
        $instance->loadByID( $id );
        return $instance;
    }

    public static function withRow( array $row ) {
        $instance = new self();
        $instance->fill( $row );
        return $instance;
    }

    protected function loadByID( $id ) {
        // do query
        $row = my_awesome_db_access_stuff( $id );
        $this->fill( $row );
    }

    protected function fill( array $row ) {
        // fill all properties from array
    }
}

?>

如果我想要一个我知道 ID 的学生:

$student = Student::withID( $id );

或者如果我有一个 db 行的数组:

$student = Student::withRow( $row );

从技术上讲,您并没有构建多个构造函数,只是静态辅助方法,但是这样可以避免构造函数中出现大量意大利面条式代码。

【讨论】:

  • 看来你刚刚回答了我问 gpilotino 的问题。谢谢!很清楚。
  • @gpilotino,矫枉过正,因为你需要另一个类,(或方法),它基本上只包含一个开关/案例决策树,最后只是做我已经用两种方法做的事情.工厂在您无法轻松定义问题的确切约束的情况下更有用,例如创建表单元素。但是,这只是我的意见并记录在案;我不认为这是事实。
  • 我们不能也将__construct()设为私有,以防止有人偶尔分配“未初始化”实例吗?
  • @mlvljr:可以,但我建议将其设为受保护而不是私有。否则,如果你打算扩展你的课程,你很可能会遇到麻烦。
  • PHP 5.3 中关于你应该使用new static() 而不是new self() 的注释,因为new static() 在子类中会更有效地工作。
【解决方案2】:

Kris 的解决方案确实不错,但我更喜欢工厂风格和流利风格的混合:

<?php

class Student
{

    protected $firstName;
    protected $lastName;
    // etc.

    /**
     * Constructor
     */
    public function __construct() {
        // allocate your stuff
    }

    /**
     * Static constructor / factory
     */
    public static function create() {
        return new self();
    }

    /**
     * FirstName setter - fluent style
     */
    public function setFirstName($firstName) {
        $this->firstName = $firstName;
        return $this;
    }

    /**
     * LastName setter - fluent style
     */
    public function setLastName($lastName) {
        $this->lastName = $lastName;
        return $this;
    }

}

// create instance
$student= Student::create()->setFirstName("John")->setLastName("Doe");

// see result
var_dump($student);
?>

【讨论】:

  • +1;这种类型的解决方案可以产生非常好的代码。尽管我会在此解决方案中选择setLastName(或更确切地说是所有设置器)来返回$this,而不是在同一属性上有效地使用两个设置器。
  • 作为一个习惯于编译静态类型语言(如 C#)的人,这种处理方式非常适合我。
  • 提供静态创建方法与仅以相同方式使用构造函数有何不同? $student = new Student()-&gt;setFirstName("John")-&gt;setLastName("Doe");
  • 该代码存在一个重要问题:您无法确保实例有效(这就是存在构造函数的原因),通常不可变类更可取。
  • 这是针对此类问题看到的最干净的代码,可以通过在create() 方法中使用return new self() 来清洁
【解决方案3】:

PHP 是一种动态语言,因此您不能重载方法。您必须像这样检查参数的类型:

class Student 
{
   protected $id;
   protected $name;
   // etc.

   public function __construct($idOrRow){
    if(is_int($idOrRow))
    {
        $this->id = $idOrRow;
        // other members are still uninitialized
    }
    else if(is_array($idOrRow))
    {
       $this->id = $idOrRow->id;
       $this->name = $idOrRow->name;
       // etc.  
    }
}

【讨论】:

  • 所有这一切都是令人敬畏的意大利面条代码。但这确实可能是最简单的方法。
  • 如果您像使用静态类型语言一样创建构造函数,它将变成意大利面条代码。但你没有。无论如何,为该参数创建具有一个参数且没有类型(无类型 == 任何类型)的两个构造函数在任何语言中都不起作用(例如,在一个类中具有两个具有一个 Object 参数的 Java 构造函数,或者)。
  • 我的意思是你在同一范围内根据外部影响做不同的事情,这不是一个糟糕的解决方案(因为它会起作用),只是不是我会选择的那个。跨度>
  • “动态”语言并不排除函数/构造函数重载的可能性。它甚至不排除静态类型。即便如此,仍然有可能允许纯粹基于参数计数的重载。请不要以“动态”为借口。
  • 我喜欢这种为类用户简化代码的方式,它实现了 OP 想要的。如果您创建两个函数(如 Kris 的回答)并在构造函数中适当地调用这些函数,则它不会是意大利面条代码。检查参数的代码可能没有那么复杂。这当然假设有某种方法可以将参数彼此区分开来,就像在这种情况下一样。
【解决方案4】:
public function __construct() {
    $parameters = func_get_args();
    ...
}

$o = new MyClass('One', 'Two', 3);

现在 $paramters 将是一个包含值 'One'、'Two'、3 的数组。

编辑,

我可以补充一下

func_num_args()

会给你函数的参数数量。

【讨论】:

  • 这如何解决知道通过了什么的问题?我认为这会使问题复杂化,因为您不必检查参数的类型,而必须检查是否设置了 x 参数,然后检查它的类型。
  • 知道传递了什么类型并不能解决问题,但这是PHP中“多个构造函数”的方法。类型检查由 OP 来做。
  • 我想知道当一个新开发人员被添加到一个包含大量这样代码的项目中会发生什么
【解决方案5】:

正如这里已经展示的那样,在 PHP 中声明 multiple 构造函数的方法有很多,但它们都不是 correct 这样做的方式(因为 PHP 技术上不允许这样做)。 但这并不能阻止我们破解这个功能...... 这是另一个例子:

<?php

class myClass {
    public function __construct() {
        $get_arguments       = func_get_args();
        $number_of_arguments = func_num_args();

        if (method_exists($this, $method_name = '__construct'.$number_of_arguments)) {
            call_user_func_array(array($this, $method_name), $get_arguments);
        }
    }

    public function __construct1($argument1) {
        echo 'constructor with 1 parameter ' . $argument1 . "\n";
    }

    public function __construct2($argument1, $argument2) {
        echo 'constructor with 2 parameter ' . $argument1 . ' ' . $argument2 . "\n";
    }

    public function __construct3($argument1, $argument2, $argument3) {
        echo 'constructor with 3 parameter ' . $argument1 . ' ' . $argument2 . ' ' . $argument3 . "\n";
    }
}

$object1 = new myClass('BUET');
$object2 = new myClass('BUET', 'is');
$object3 = new myClass('BUET', 'is', 'Best.');

来源:The easiest way to use and understand multiple constructors:

希望这会有所帮助。 :)

【讨论】:

  • 这是最好的解决方案。如果将 PHP 5.6+ 与新的 ... 运算符一起使用会更加优雅。
  • 当然这不适用于 JannieT 的原始问题,因为她想要的构造函数是 __construct($id)__construct($row_from_database)。两者都有一个参数,可能是第一个为 int,第二个为 arrayobject。当然,数字的附加可以扩展为某种 C++ 风格的参数签名(即__construct_i($intArg)__construct_a($arrayArg))。
  • +1:我有点喜欢这样,但是在嵌套的ctors中使用类型信息进行了扩展并且没有双下划线前缀。感谢您的启发!
  • 您甚至可以在示例代码中添加反射,以对以 __construct 开头的类的每个函数的参数应用类型检查,并以这种方式匹配适当的构造函数
【解决方案6】:

你可以这样做:

public function __construct($param)
{
    if(is_int($param)) {
         $this->id = $param;
    } elseif(is_object($param)) {
     // do something else
    }
 }

【讨论】:

  • +1 是一个非常可行的解决方案。但是,对于要记住的课程,我将使用 @Kris' 方法。
【解决方案7】:

从 5.4 版开始,PHP 支持traits。这不是正是您正在寻找的,但基于简单特征的方法将是:

trait StudentTrait {
    protected $id;
    protected $name;

    final public function setId($id) {
        $this->id = $id;
        return $this;
    }

    final public function getId() { return $this->id; }

    final public function setName($name) {
        $this->name = $name; 
        return $this;
    }

    final public function getName() { return $this->name; }

}

class Student1 {
    use StudentTrait;

    final public function __construct($id) { $this->setId($id); }
}

class Student2 {
    use StudentTrait;

    final public function __construct($id, $name) { $this->setId($id)->setName($name); }
}

我们最终得到了两个类,一个用于每个构造函数,这有点适得其反。为了保持理智,我将投入一个工厂:

class StudentFactory {
    static public function getStudent($id, $name = null) {
        return 
            is_null($name)
                ? new Student1($id)
                : new Student2($id, $name)
    }
}

所以,这一切都归结为:

$student1 = StudentFactory::getStudent(1);
$student2 = StudentFactory::getStudent(1, "yannis");

这是一种非常冗长的方法,但它可以非常方便。

【讨论】:

    【解决方案8】:

    这是一种优雅的方法。在给定参数数量的情况下,创建将启用多个构造函数的特征。您只需将参数数量添加到函数名称“__construct”。所以一个参数将是“__construct1”,两个“__construct2”......等等。

    trait constructable
    {
        public function __construct() 
        { 
            $a = func_get_args(); 
            $i = func_num_args(); 
            if (method_exists($this,$f='__construct'.$i)) { 
                call_user_func_array([$this,$f],$a); 
            } 
        } 
    }
    
    class a{
        use constructable;
    
        public $result;
    
        public function __construct1($a){
            $this->result = $a;
        }
    
        public function __construct2($a, $b){
            $this->result =  $a + $b;
        }
    }
    
    echo (new a(1))->result;    // 1
    echo (new a(1,2))->result;  // 3
    

    【讨论】:

    • 非常聪明、优雅且可重复使用。 :拍手:
    • 解析错误:语法错误,第 8 行出现意外 ','
    • 这是一个很棒的方法:)
    【解决方案9】:

    另一种选择是像这样在构造函数中使用默认参数

    class Student {
    
        private $id;
        private $name;
        //...
    
        public function __construct($id, $row=array()) {
            $this->id = $id;
            foreach($row as $key => $value) $this->$key = $value;
        }
    }
    

    这意味着你需要用这样的一行来实例化:$student = new Student($row['id'], $row),但要让你的构造函数保持整洁。

    另一方面,如果你想利用多态性,那么你可以像这样创建两个类:

    class Student {
    
        public function __construct($row) {
             foreach($row as $key => $value) $this->$key = $value;
        }
    }
    
    class EmptyStudent extends Student {
    
        public function __construct($id) {
            parent::__construct(array('id' => $id));
        }
    }
    

    【讨论】:

    • 现在您有两个名称不同但功能相同的类,只是构造函数上的签名不同,这对我来说听起来是个很糟糕的主意。
    • 对我来说听起来像是经典的多态性,也称为面向对象编程。
    • 创建多个类来提供不同的构造函数确实是个坏主意。 extends 其他类应该扩展的类,这意味着它们应该添加功能,这就是 OOP 的重点,而不是这个。
    【解决方案10】:

    正如其他cmets所说,由于php不支持重载,通常会避免构造函数中的“类型检查技巧”,而是使用工厂模式

    即。

    $myObj = MyClass::factory('fromInteger', $params);
    $myObj = MyClass::factory('fromRow', $params);
    

    【讨论】:

    • 看起来很整洁。我对工厂不熟悉。在您的示例中,$myObj 是否属于 MyClass 类型?两个静态函数返回 $myObj 的构造实例会是什么样子?
    • 我会像 Kris 那样使用单独的方法来防止一种大型工厂方法。
    • 确实,@Kris 解决方案是最好的。
    • 这看起来很像 C++ 标签
    【解决方案11】:

    您可以执行以下非常简单且非常干净的操作:

    public function __construct()    
    {
       $arguments = func_get_args(); 
    
       switch(sizeof(func_get_args()))      
       {
        case 0: //No arguments
            break; 
        case 1: //One argument
            $this->do_something($arguments[0]); 
            break;              
        case 2:  //Two arguments
            $this->do_something_else($arguments[0], $arguments[1]); 
            break;            
       }
    }
    

    【讨论】:

    • 为什么将func_get_args 分配给一个变量并在下一行再次调用它?如果您在决定需要基于fund_num_args 之后只调用func_get_args 也会更好。
    • 恕我直言,这与干净的解决方案相反
    【解决方案12】:

    我知道我在这里参加聚会已经很晚了,但我想出了一个相当灵活的模式,它应该允许一些非常有趣和通用的实现。

    像往常一样设置您的课程,使用您喜欢的任何变量。

    class MyClass{
        protected $myVar1;
        protected $myVar2;
    
        public function __construct($obj = null){
            if($obj){
                foreach (((object)$obj) as $key => $value) {
                    if(isset($value) && in_array($key, array_keys(get_object_vars($this)))){
                        $this->$key = $value;
                    }
                }
            }
        }
    }
    

    当您创建对象时,只需传递一个关联数组,该数组的键与您的变量名称相同,就像这样...

    $sample_variable = new MyClass([
        'myVar2'=>123, 
        'i_dont_want_this_one'=> 'This won\'t make it into the class'
        ]);
    
    print_r($sample_variable);
    

    此实例化后的print_r($sample_variable); 产生以下结果:

    MyClass Object ( [myVar1:protected] =&gt; [myVar2:protected] =&gt; 123 )

    因为我们已经在__construct(...) 中将$group 初始化为null,所以也可以不向构造函数传递任何内容,就像这样......

    $sample_variable = new MyClass();
    
    print_r($sample_variable);
    

    现在输出完全符合预期:

    MyClass Object ( [myVar1:protected] =&gt; [myVar2:protected] =&gt; )

    我写这个的原因是我可以直接将json_decode(...) 的输出传递给我的构造函数,而不必太担心它。

    这是在 PHP 7.1 中执行的。享受吧!

    【讨论】:

    • 您可以做一些很酷的事情,例如当在数组中输入意外值时抛出异常。 gist that I wrote up 上有一个这样的例子
    【解决方案13】:

    这个问题已经用非常聪明的方法来满足要求,但我想知道为什么不退后一步,问一个基本问题,为什么我们需要一个有两个构造函数的类? 如果我的类需要两个构造函数,那么我设计类的方式可能需要更多考虑来提出更清洁和更可测试的设计。

    我们正在尝试将如何实例化一个类与实际的类逻辑混为一谈。

    如果 Student 对象处于有效状态,那么它是从 DB 行还是从 Web 表单或 cli 请求中构造的数据是否重要?

    现在回答这里可能出现的问题,如果我们不添加从 db 行创建对象的逻辑,那么我们如何从 db 数据创建对象,我们可以简单地添加另一个类,调用如果您对数据映射器模式感到满意,则可以使用 StudentMapper,在某些情况下您可以使用 StudentRepository,如果没有适合您的需求,您可以创建 StudentFactory 来处理各种对象构造任务。

    底线是在我们处理域对象时将持久层排除在我们的脑海之外。

    【讨论】:

      【解决方案14】:

      让我在这里添加我的沙粒

      我个人喜欢将构造函数添加为返回类(对象)实例的静态函数。以下代码为示例:

       class Person
       {
           private $name;
           private $email;
      
           public static function withName($name)
           {
               $person = new Person();
               $person->name = $name;
      
               return $person;
           }
      
           public static function withEmail($email)
           {
               $person = new Person();
               $person->email = $email;
      
               return $person;
           }
       }
      

      请注意,现在您可以像这样创建 Person 类的实例:

      $person1 = Person::withName('Example');
      $person2 = Person::withEmail('yo@mi_email.com');
      

      我的代码来自:

      http://alfonsojimenez.com/post/30377422731/multiple-constructors-in-php

      【讨论】:

        【解决方案15】:

        嗯,很惊讶我还没有看到这个答案,假设我会把我的帽子扔进戒指。

        class Action {
            const cancelable    =   0;
            const target        =   1
            const type          =   2;
        
            public $cancelable;
            public $target;
            public $type;
        
        
            __construct( $opt = [] ){
        
                $this->cancelable   = isset($opt[cancelable]) ? $opt[cancelable] : true;
                $this->target       = isset($opt[target]) ?     $opt[target] : NULL;
                $this->type         = isset($opt[type]) ?       $opt[type] : 'action';
        
            }
        }
        
        
        $myAction = new Action( [
            Action::cancelable => false,
            Action::type => 'spin',
            .
            .
            .
        ]);
        

        您可以选择将选项分离到它们自己的类中,例如扩展 SplEnum。

        abstract class ActionOpt extends SplEnum{
            const cancelable    =   0;
            const target        =   1
            const type          =   2;
        }
        

        【讨论】:

        • 我在必须解决以下问题时也想到了这一点。我的类应该得到一个构造函数,可以在没有参数或定义数量的参数(在本例中为 3)的情况下调用该构造函数。使用数组很容易使用空和计数进行检查并采取适当的措施。如果为空,则终止函数,因为没有要分配的内容,或者如果参数的数量或其值不合适,则会引发适当的异常。使用 www.DeepL.com/Translator 翻译(免费版)
        【解决方案16】:

        这是我的看法(为 php 5.6 构建)。

        它将查看构造函数参数类型(数组、类名、无描述)并比较给定的参数。构造函数必须最后给出最不具体的。举例:

        // demo class
        class X {
            public $X;
        
            public function __construct($x) {
                $this->X = $x;
            }
        
            public function __toString() {
                return 'X'.$this->X;
            }
        }
        
        // demo class
        class Y {
            public $Y;
        
            public function __construct($y) {
                $this->Y = $y;
            }
            public function __toString() {
                return 'Y'.$this->Y;
            }
        }
        
        // here be magic
        abstract class MultipleConstructors {
            function __construct() {
                $__get_arguments       = func_get_args();
                $__number_of_arguments = func_num_args();
        
                $__reflect = new ReflectionClass($this);
                foreach($__reflect->getMethods() as $__reflectmethod) {
                    $__method_name = $__reflectmethod->getName();
                    if (substr($__method_name, 0, strlen('__construct')) === '__construct') {
                        $__parms = $__reflectmethod->getParameters();
                        if (count($__parms) == $__number_of_arguments) {
                            $__argsFit = true;
                            foreach ($__parms as $__argPos => $__param) {
                                $__paramClass= $__param->getClass();
                                $__argVar = func_get_arg($__argPos);
                                $__argVarType = gettype($__argVar);
                                $__paramIsArray = $__param->isArray() == true;
                                $__argVarIsArray = $__argVarType == 'array';
                                // parameter is array and argument isn't, or the other way around.
                                if (($__paramIsArray && !$__argVarIsArray) ||
                                    (!$__paramIsArray && $__argVarIsArray)) {
                                    $__argsFit = false;
                                    continue;
                                }
                                // class check
                                if ((!is_null($__paramClass) && $__argVarType != 'object') ||
                                    (is_null($__paramClass) && $__argVarType == 'object')){
                                    $__argsFit = false;
                                    continue;
                                }
                                if (!is_null($__paramClass) && $__argVarType == 'object') {
                                    // class type check
                                    $__paramClassName = "N/A";
                                    if ($__paramClass)
                                        $__paramClassName = $__paramClass->getName();
                                    if ($__paramClassName != get_class($__argVar)) {
                                        $__argsFit = false;
                                    }
                                }
                            }
                            if ($__argsFit) {
                                call_user_func_array(array($this, $__method_name), $__get_arguments);
                                return;
                            }
                        }
                    }
                }
                throw new Exception("No matching constructors");
            }
        }
        
        // how to use multiple constructors
        class A extends MultipleConstructors {
            public $value;
        
            function __constructB(array $hey) {
                $this->value = 'Array#'.count($hey).'<br/>';
            }
            function __construct1(X $first) {
                $this->value = $first .'<br/>';
            }
        
            function __construct2(Y $second) {
                $this->value = $second .'<br/>';
            }
            function __constructA($hey) {
                $this->value = $hey.'<br/>';
            }
        
            function __toString() {
                return $this->value;
            }
        }
        
        $x = new X("foo");
        $y = new Y("bar");
        
        $aa = new A(array("one", "two", "three"));
        echo $aa;
        
        $ar = new A("baz");
        echo $ar;
        
        $ax = new A($x);
        echo $ax;
        
        $ay = new A($y);
        echo $ay;
        

        结果:

        Array#3
        baz
        Xfoo
        Ybar
        

        如果没有找到构造函数,则可以将其删除并允许“空”构造函数,而不是终止异常。或者任何你喜欢的。

        【讨论】:

          【解决方案17】:

          我在创建具有不同签名的多个构造函数时遇到了同样的问题,但不幸的是,PHP 没有提供这样做的直接方法。但是,我找到了一个技巧来克服这个问题。希望也适用于你们所有人。

              <?PHP
          
              class Animal
              {
          
                public function __construct()
                {
                  $arguments = func_get_args();
                  $numberOfArguments = func_num_args();
          
                  if (method_exists($this, $function = '__construct'.$numberOfArguments)) {
                      call_user_func_array(array($this, $function), $arguments);
                  }
              }
             
              public function __construct1($a1)
              {
                  echo('__construct with 1 param called: '.$a1.PHP_EOL);
              }
             
              public function __construct2($a1, $a2)
              {
                  echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
              }
             
              public function __construct3($a1, $a2, $a3)
              {
                  echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
              }
          }
          
          $o = new Animal('sheep');
          $o = new Animal('sheep','cat');
          $o = new Animal('sheep','cat','dog');
          
          // __construct with 1 param called: sheep
          // __construct with 2 params called: sheep,cat
          // __construct with 3 params called: sheep,cat,dog
          

          【讨论】:

            【解决方案18】:

            对于php7,我也比较参数类型,你可以有两个参数数量相同但类型不同的构造函数。

            trait GenericConstructorOverloadTrait
            {
                /**
                 * @var array Constructors metadata
                 */
                private static $constructorsCache;
                /**
                 * Generic constructor
                 * GenericConstructorOverloadTrait constructor.
                 */
                public function __construct()
                {
                    $params = func_get_args();
                    $numParams = func_num_args();
            
                    $finish = false;
            
                    if(!self::$constructorsCache){
                        $class = new \ReflectionClass($this);
                        $constructors =  array_filter($class->getMethods(),
                            function (\ReflectionMethod $method) {
                            return preg_match("/\_\_construct[0-9]+/",$method->getName());
                        });
                        self::$constructorsCache = $constructors;
                    }
                    else{
                        $constructors = self::$constructorsCache;
                    }
                    foreach($constructors as $constructor){
                        $reflectionParams = $constructor->getParameters();
                        if(count($reflectionParams) != $numParams){
                            continue;
                        }
                        $matched = true;
                        for($i=0; $i< $numParams; $i++){
                            if($reflectionParams[$i]->hasType()){
                                $type = $reflectionParams[$i]->getType()->__toString();
                            }
                            if(
                                !(
                                    !$reflectionParams[$i]->hasType() ||
                                    ($reflectionParams[$i]->hasType() &&
                                        is_object($params[$i]) &&
                                        $params[$i] instanceof $type) ||
                                    ($reflectionParams[$i]->hasType() &&
                                        $reflectionParams[$i]->getType()->__toString() ==
                                        gettype($params[$i]))
                                )
                            ) {
                                $matched = false;
                                break;
                            }
            
                        }
            
                        if($matched){
                            call_user_func_array(array($this,$constructor->getName()),
                                $params);
                            $finish = true;
                            break;
                        }
                    }
            
                    unset($constructor);
            
                    if(!$finish){
                        throw new \InvalidArgumentException("Cannot match construct by params");
                    }
                }
            
            }
            

            使用它:

            class MultiConstructorClass{
            
                use GenericConstructorOverloadTrait;
            
                private $param1;
            
                private $param2;
            
                private $param3;
            
                public function __construct1($param1, array $param2)
                {
                    $this->param1 = $param1;
                    $this->param2 = $param2;
                }
            
                public function __construct2($param1, array $param2, \DateTime $param3)
                {
                    $this->__construct1($param1, $param2);
                    $this->param3 = $param3;
                }
            
                /**
                 * @return \DateTime
                 */
                public function getParam3()
                {
                    return $this->param3;
                }
            
                /**
                 * @return array
                 */
                public function getParam2()
                {
                    return $this->param2;
                }
            
                /**
                 * @return mixed
                 */
                public function getParam1()
                {
                    return $this->param1;
                }
            }
            

            【讨论】:

            • 你能展示你如何使用两种不同的构造方法新建你的MultiConstructorClass 的两个实例吗?谢谢。
            • 我以为我的回答很巧妙,但这显然更好。
            【解决方案19】:

            更现代的方法: 您正在将单独的类混合为一个实体和数据水合。 因此,对于您的情况,您应该有 2 个类:

            class Student 
            {
               protected $id;
               protected $name;
               // etc.
            }
            class StudentHydrator
            {
               public function hydrate(Student $student, array $data){
                  $student->setId($data['id']);
                  if(isset($data['name')){
                    $student->setName($data['name']);
                  }
                  // etc. Can be replaced with foreach
                  return $student;
               }
            }
            
            //usage
            $hydrator = new StudentHydrator();
            $student = $hydrator->hydrate(new Student(), ['id'=>4]);
            $student2 = $hydrator->hydrate(new Student(), $rowFromDB);
            

            另外请注意,您应该使用已经提供自动实体水合的学说或其他 ORM。 你应该使用依赖注入来跳过手动创建像 StudentHydrator 这样的对象。

            【讨论】:

              【解决方案20】:

              为了回应 Kris 的最佳答案(顺便说一句,它帮助我设计了自己的课程),这里有一个修改版本,供那些可能觉得它有用的人使用。包括从任何列中选择和从数组中转储对象数据的方法。干杯!

              public function __construct() {
                  $this -> id = 0;
                  //...
              }
              
              public static function Exists($id) {
                  if (!$id) return false;
                  $id = (int)$id;
                  if ($id <= 0) return false;
                  $mysqli = Mysql::Connect();
                  if (mysqli_num_rows(mysqli_query($mysqli, "SELECT id FROM users WHERE id = " . $id)) == 1) return true;
                  return false;
              }
              
              public static function FromId($id) {
                  $u = new self();
                  if (!$u -> FillFromColumn("id", $id)) return false;
                  return $u;
              }
              
              public static function FromColumn($column, $value) {
                  $u = new self();
                  if (!$u -> FillFromColumn($column, $value)) return false;
                  return $u;
              }
              
              public static function FromArray($row = array()) {
                  if (!is_array($row) || $row == array()) return false;
                  $u = new self();
                  $u -> FillFromArray($row);
                  return $u;
              }
              
              protected function FillFromColumn($column, $value) {
                  $mysqli = Mysql::Connect();
                  //Assuming we're only allowed to specified EXISTENT columns
                  $result = mysqli_query($mysqli, "SELECT * FROM users WHERE " . $column . " = '" . $value . "'");
                  $count = mysqli_num_rows($result);
                  if ($count == 0) return false;
                  $row = mysqli_fetch_assoc($result);
                  $this -> FillFromArray($row);
              }
              
              protected function FillFromArray(array $row) {
                  foreach($row as $i => $v) {
                      if (isset($this -> $i)) {
                          $this -> $i = $v;
                      }
                  }
              }
              
              public function ToArray() {
                  $m = array();
                  foreach ($this as $i => $v) {
                      $m[$i] = $v;    
                  }
                  return $m;
              }
              
              public function Dump() {
                  print_r("<PRE>");
                  print_r($this -> ToArray());
                  print_r("</PRE>");  
              }
              

              【讨论】:

                【解决方案21】:

                按数据类型调用构造函数:

                class A 
                { 
                    function __construct($argument)
                    { 
                       $type = gettype($argument);
                
                       if($type == 'unknown type')
                       {
                            // type unknown
                       }
                
                       $this->{'__construct_'.$type}($argument);
                    } 
                
                    function __construct_boolean($argument) 
                    { 
                        // do something
                    }
                    function __construct_integer($argument) 
                    { 
                        // do something
                    }
                    function __construct_double($argument) 
                    { 
                        // do something
                    }
                    function __construct_string($argument) 
                    { 
                        // do something
                    }
                    function __construct_array($argument) 
                    { 
                        // do something
                    }
                    function __construct_object($argument) 
                    { 
                        // do something
                    }
                    function __construct_resource($argument) 
                    { 
                        // do something
                    }
                
                    // other functions
                
                } 
                

                【讨论】:

                【解决方案22】:

                您总是可以在构造函数中添加一个额外的参数,称为 mode 之类的东西,然后对其执行 switch 语句...

                class myClass 
                {
                    var $error ;
                    function __construct ( $data, $mode )
                    {
                        $this->error = false
                        switch ( $mode )
                        {
                            'id' : processId ( $data ) ; break ;
                            'row' : processRow ( $data ); break ;
                            default : $this->error = true ; break ;
                         }
                     }
                
                     function processId ( $data ) { /* code */ }
                     function processRow ( $data ) { /* code */ }
                }
                
                $a = new myClass ( $data, 'id' ) ;
                $b = new myClass ( $data, 'row' ) ;
                $c = new myClass ( $data, 'something' ) ;
                
                if ( $a->error )
                   exit ( 'invalid mode' ) ;
                if ( $b->error )
                   exit ('invalid mode' ) ;
                if ( $c->error )
                   exit ('invalid mode' ) ;
                

                也可以随时使用该方法,如果您想添加更多功能,您可以在 switch 语句中添加另一个案例,您还可以检查以确保有人发送了正确的内容 - 在上面的示例中,所有除了 C 之外的数据都可以,因为它设置为“某事”,因此设置了类中的错误标志,并将控制返回给主程序以决定下一步该做什么(在示例中,我只是告诉它退出并显示错误消息“无效模式” - 但您也可以循环返回直到找到有效数据)。

                【讨论】:

                  【解决方案23】:

                  我创建了这个方法,不仅可以在构造函数中使用,还可以在方法中使用:

                  我的构造函数:

                  function __construct() {
                      $paramsNumber=func_num_args();
                      if($paramsNumber==0){
                          //do something
                      }else{
                          $this->overload('__construct',func_get_args());
                      }
                  }
                  

                  我的 doSomething 方法:

                  public function doSomething() {
                      $paramsNumber=func_num_args();
                      if($paramsNumber==0){
                          //do something
                      }else{
                          $this->overload('doSomething',func_get_args());
                      }
                  }
                  

                  两者都可以使用这个简单的方法:

                  public function overloadMethod($methodName,$params){
                      $paramsNumber=sizeof($params);
                      //methodName1(), methodName2()...
                      $methodNameNumber =$methodName.$paramsNumber;
                      if (method_exists($this,$methodNameNumber)) {
                          call_user_func_array(array($this,$methodNameNumber),$params);
                      }
                  }
                  

                  所以你可以声明

                  __construct1($arg1), __construct2($arg1,$arg2)...
                  

                  methodName1($arg1), methodName2($arg1,$arg2)...
                  

                  等等:)

                  而使用时:

                  $myObject =  new MyClass($arg1, $arg2,..., $argN);
                  

                  它将调用__constructN,您在其中定义了N args

                  那么 $myObject -> doSomething($arg1, $arg2,..., $argM)

                  它将调用 doSomethingM, ,您在其中定义了 M args;

                  【讨论】:

                    【解决方案24】:

                    Kris's answer 很棒,但由于 Buttle Butku commentednew static() 在 PHP 5.3+ 中会更受欢迎。

                    所以我会这样做(根据 Kris 的回答修改):

                    <?php
                    
                    class Student
                    {
                        public function __construct() {
                            // allocate your stuff
                        }
                    
                        public static function withID( $id ) {
                            $instance = new static();
                            $instance->loadByID( $id );
                            return $instance;
                        }
                    
                        public static function withRow( array $row ) {
                            $instance = new static();
                            $instance->fill( $row );
                            return $instance;
                        }
                    
                        protected function loadByID( $id ) {
                            // do query
                            $row = my_awesome_db_access_stuff( $id );
                            $this->fill( $row );
                        }
                    
                        protected function fill( array $row ) {
                            // fill all properties from array
                        }
                    }
                    
                    ?>
                    

                    用法:

                    <?php
                    
                    $student1 = Student::withID($id);
                    $student2 = Student::withRow($row);
                    
                    ?>
                    

                    我还在 php.net OOP 文档中找到了an useful example

                    【讨论】:

                      【解决方案25】:

                      从 PHP 8 开始,我们可以使用命名参数:

                      class Student {
                      
                        protected int $id;
                        protected string $name;
                      
                        public function __construct(int $id = null, string $name = null, array $row_from_database = null) {
                          if ($id !== null && $name !== null && $row_from_database === null) {
                            $this->id = $id;
                            $this->name = $name;
                          } elseif ($id === null && $name === null
                              && $row_from_database !== null
                              && array_keys($row_from_database) === [ 'id', 'name' ]
                              && is_int($row_from_database['id'])
                              && is_string($row_from_database['name'])) {
                            $this->id = $row_from_database['id'];
                            $this->name = $row_from_database['name'];
                          } else {
                            throw new InvalidArgumentException('Invalid arguments');
                          }
                        }
                      
                      }
                      
                      $student1 = new Student(id: 3, name: 'abc');
                      $student2 = new Student(row_from_database: [ 'id' => 4, 'name' => 'def' ]);
                      

                      通过适当的检查,可以排除无效的参数组合,以便创建的实例在构造函数末尾是有效的(但只会在运行时检测到错误)。

                      【讨论】:

                        猜你喜欢
                        • 2010-10-09
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2015-07-28
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多