381

一意の引数シグネチャを持つ 2 つの __construct 関数を PHP クラスに配置することはできません。私はこれをしたい:

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 = 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 に答える
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); }
}

各コンストラクターに 1 つずつ、合計 2 つのクラスができてしまいますが、これは少し非生産的です。ある程度の正気を保つために、ファクトリーを投入します。

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 に答える
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)ただし、コンストラクターはきれいに保たれます。

一方、ポリモーフィズムを利用したい場合は、次のように2つのクラスを作成できます。

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はオーバーロードをサポートしていないため、通常、コンストラクターでの「型チェックのトリック」は回避され、ファクトリパターンが代わりに使用されます。

すなわち。

$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 に答える
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

署名が異なる複数のコンストラクターを作成する際に同じ問題に直面していましたが、残念ながら、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 の場合、パラメーターの型も比較します。パラメーターの数は同じで型が異なる 2 つのコンストラクターを使用できます。

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 に答える
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

このメソッドを作成して、コンストラクターだけでなくメソッドでも使用できるようにしました。

私のコンストラクタ:

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を定義した場所で呼び出されますN

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

argsdoSomethingMを定義した場所で ,を呼び出します。M

于 2016-08-02T17:16:26.423 に答える