你不能在一个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没有提供直接的方法来这样做。然而,我发现了一个技巧来克服它。希望也适用于你们所有人。

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

其他回答

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

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

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

} 

这里有一种优雅的方法。创建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

让我在这里加一粒沙子

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

 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

对于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;
    }
}