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


当前回答

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

ie.

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

其他回答

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

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

我创建这个方法是为了让它不仅在构造函数中使用,而且在方法中使用:

我的构造函数:

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

or

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

等等。

使用时:

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

它将调用__constructN,在那里你定义了N个参数

然后 $myObject -> doSomething($arg1, $arg2,..), argM美元)

它会调用doSomethingM,在这里你定义了M个参数;

为了回应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>");  
}

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

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

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