php parent




在PHP中執行多個構造函數的最佳方法 (12)

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


Kris的解決方案非常好,但我發現工廠和流暢風格的組合更好:

<?php

class Student
{

    protected $firstName;
    protected $lastName;
    // etc.

    /**
     * Constructor
     */
    public function __construct() {
        // allocate your stuff
    }

    /**
     * Static constructor / factory
     */
    public static function create() {
        $instance = new self();
        return $instance;
    }

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

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

你可以做這樣的事情:

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

你總是可以給構造函數添加一個額外的參數,叫做模式,然後在它上面執行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被設置為“something”,所以類中的錯誤標誌被設置並且控制返回到主程序,以便決定下一步該做什麼(在這個例子中,我剛剛告訴它用一個錯誤消息“無效模式” - 但也可以將其循環,直到找到有效數據為止)。


對於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;
    }
}

從版本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");

這是一個非常詳細的方法,但它可以非常方便。


我可能會做這樣的事情:

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

或者,如果我有一個數據庫行的數組:

$student = Student::withRow( $row );

從技術上講,你並沒有構建多個構造函數,只是靜態輔助方法,但是你可以這樣避免構造函數中的很多意大利麵條代碼。


按數據類型調用構造函數:

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

} 

正如其他評論所述,由於php不支持重載,通常避免使用構造函數中的“類型檢查技巧”,並且工廠模式被用於intead

即。

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

正如在這裡已經展示的那樣,在PHP中聲明multiple構造函數有很多方法,但是沒有一個方法是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.');

來源: 使用和理解多個構造函數的最簡單方法:

希望這可以幫助。 :)


讓我在這裡添加我的沙粒

我個人喜歡添加一個構造函數作為靜態函數來返回類的實例(對象)。 以下代碼是一個示例:

 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('[email protected]_email.com');

我從以下代碼獲取:

http://alfonsojimenez.com/post/30377422731/multiple-constructors-in-php


這個問題已經以非常聰明的方式回答了要求,但我想知道為什麼不退後一步問我們為什麼需要一個帶有兩個構造函數的類的基本問題? 如果我的類需要兩個構造函數,那麼設計我的類的方式可能需要更多的考慮才能提出更清晰,更易於測試的設計。

我們試圖混淆如何用實際的類邏輯實例化一個類。

如果一個Student對象處於有效狀態,那麼它是從一個數據庫的行構建還是從一個Web表單或一個cli請求構造而來?

現在回答這個問題,如果我們不添加從db行創建對象的邏輯,那麼我們如何從db數據創建一個對象,我們可以簡單地添加另一個類,將其稱為StudentMapper if你對數據映射器模式很熟悉,在某些情況下你可以使用StudentRepository,如果沒有任何東西符合你的需求,你可以讓一個StudentFactory來處理各種對象構建任務。

底線是當我們處理域對象時,讓持久層保持在我們的頭腦之外。







multiple-constructors