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


当前回答

从5.4版开始,PHP支持trait。这并不是你真正想要的,但基于特征的简单方法是:

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

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

其他回答

你可以这样做:

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

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

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

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!

从5.4版开始,PHP支持trait。这并不是你真正想要的,但基于特征的简单方法是:

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

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

嗯,很惊讶我还没有看到这个答案,假设我要参加竞选。

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',
    .
    .
    .
]);

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

abstract class ActionOpt extends SplEnum{
    const cancelable    =   0;
    const target        =   1
    const type          =   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);

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