381

您不能在 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 中执行此操作的最佳方法是什么?

4

25 回答 25

527

我可能会做这样的事情:

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

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

于 2009-11-09T14:33:09.460 回答
93

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);
?>
于 2012-09-21T07:39:52.103 回答
44

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.  
    }
}
于 2009-11-09T08:48:01.190 回答
26
public function __construct() {
    $parameters = func_get_args();
    ...
}

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

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

编辑,

我可以补充

func_num_args()

将为您提供函数的参数数量。

于 2009-11-09T08:47:58.427 回答
24

正如这里已经展示的那样,multiple在 PHP 中有许多声明构造函数的方法,但没有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.');

来源: 使用和理解多个构造函数的最简单方法:

希望这可以帮助。:)

于 2015-01-24T06:52:28.700 回答
16

你可以这样做:

public function __construct($param)
{
    if(is_int($param)) {
         $this->id = $param;
    } elseif(is_object($param)) {
     // do something else
    }
 }
于 2009-11-09T08:47:43.770 回答
16

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

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

于 2013-03-01T08:45:29.437 回答
9

这是一种优雅的方法。在给定参数数量的情况下,创建将启用多个构造函数的特征。您只需将参数数量添加到函数名称“__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
于 2019-05-12T20:22:39.083 回答
7

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

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));
    }
}
于 2009-11-09T10:03:24.493 回答
4

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

IE。

$myObj = MyClass::factory('fromInteger', $params);
$myObj = MyClass::factory('fromRow', $params);
于 2009-11-09T10:09:27.080 回答
4

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

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;            
   }
}
于 2010-06-23T03:30:40.450 回答
4

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

像往常一样设置你的班级,使用你喜欢的任何变量。

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] => [myVar2:protected] => 123 )

因为我们已经$group在我们的 中初始化为 null __construct(...),所以也不向构造函数传递任何东西也是有效的,就像这样......

$sample_variable = new MyClass();

print_r($sample_variable);

现在输出完全符合预期:

MyClass Object ( [myVar1:protected] => [myVar2:protected] => )

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

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

于 2018-08-21T20:26:39.380 回答
3

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

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

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

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

底线是在我们处理域对象时将持久层放在我们的脑海中。

于 2017-10-16T06:21:15.737 回答
2

让我在这里添加我的沙粒

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

 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

于 2014-07-24T17:49:38.810 回答
2

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

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;
}
于 2019-08-05T22:22:27.430 回答
2

这是我的看法(为 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

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

于 2020-03-20T08:41:56.097 回答
2

我在创建具有不同签名的多个构造函数时遇到了同样的问题,但不幸的是,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
于 2021-09-09T09:16:49.647 回答
1

对于 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;
    }
}
于 2016-05-13T10:06:02.843 回答
1

更现代的方法:您将单独的类混合为一个,实体和数据水合。因此,对于您的情况,您应该有 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 这样的对象。

于 2021-01-25T19:30:11.760 回答
0

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

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

} 
于 2015-02-26T13:35:21.747 回答
0

为了回应 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>");  
}
于 2015-04-01T21:44:06.250 回答
0

您总是可以向构造函数添加一个额外的参数,称为 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 除外,因为它设置为“某事”,因此设置了类中的错误标志,并将控制权返回给主程序以决定下一步要做什么(在示例中,我只是告诉它退出错误消息“无效模式” - 但或者您可以将其循环回来,直到找到有效数据)。

于 2015-12-29T06:27:30.170 回答
0

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

我的构造函数:

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,您在其中定义Nargs

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

它会调用doSomethingM, , 你定义Margs 的地方;

于 2016-08-02T17:16:26.423 回答
0

Kris 的回答很棒,但正如 Buttle Butku评论的那样,new 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 文档中找到了一个有用的示例。

于 2021-09-10T08:24:39.873 回答
0

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

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

于 2021-11-16T18:08:58.847 回答