你不能在一个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中最好的方法是什么?


当前回答

这个问题已经用非常聪明的方法回答了,但我想知道为什么不退一步,问一个基本的问题,为什么我们需要一个有两个构造函数的类? 如果我的类需要两个构造函数,那么我设计类的方式可能需要更多的考虑,以提出一个更干净、更可测试的设计。

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

如果一个学生对象是在一个有效的状态,那么它是否从一个DB或从一个web表单或cli请求的数据行构造?

现在回答这个问题,可能出现的,如果我们不添加从db行创建一个对象的逻辑,那么我们如何创建一个对象从数据库数据,我们可以简单地添加另一个类,称之为StudentMapper如果你熟悉数据映射模式,在某些情况下,您可以使用StudentRepository,如果没有适合你的需要你可以StudentFactory处理所有类型的对象建设任务。

当我们在处理领域对象时,底线是不要考虑持久化层。

其他回答

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

这个问题已经用非常聪明的方法回答了,但我想知道为什么不退一步,问一个基本的问题,为什么我们需要一个有两个构造函数的类? 如果我的类需要两个构造函数,那么我设计类的方式可能需要更多的考虑,以提出一个更干净、更可测试的设计。

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

如果一个学生对象是在一个有效的状态,那么它是否从一个DB或从一个web表单或cli请求的数据行构造?

现在回答这个问题,可能出现的,如果我们不添加从db行创建一个对象的逻辑,那么我们如何创建一个对象从数据库数据,我们可以简单地添加另一个类,称之为StudentMapper如果你熟悉数据映射模式,在某些情况下,您可以使用StudentRepository,如果没有适合你的需要你可以StudentFactory处理所有类型的对象建设任务。

当我们在处理领域对象时,底线是不要考虑持久化层。

我知道我在这方面非常晚,但我提出了一个相当灵活的模式,应该允许一些真正有趣和通用的实现。

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

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; } } } } } When you make your object just pass an associative array with the keys of the array the same as the names of your vars, like so... $sample_variable = new MyClass([ 'myVar2'=>123, 'i_dont_want_this_one'=> 'This won\'t make it into the class' ]); print_r($sample_variable); The print_r($sample_variable); after this instantiation yields the following: MyClass Object ( [myVar1:protected] => [myVar2:protected] => 123 ) Because we've initialize $group to null in our __construct(...), it is also valid to pass nothing whatsoever into the constructor as well, like so... $sample_variable = new MyClass(); print_r($sample_variable); Now the output is exactly as expected: MyClass Object ( [myVar1:protected] => [myVar2:protected] => ) The reason I wrote this was so that I could directly pass the output of json_decode(...) to my constructor, and not worry about it too much. This was executed in PHP 7.1. Enjoy!

这里有一种优雅的方法。创建trait,在给定参数数量的情况下启用多个构造函数。你只需将参数的数量添加到函数名“__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

Kris的回答很好,但正如Buttle Butku评论的那样,新的static()将在PHP 5.3+中被首选。

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

<?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文档中找到了一个有用的例子。