How to implement a copy constructor in PHP?

时间:2018-03-25 19:36:45

标签: php oop psr-2

I have a class Account which has a default constructor function:

class Account {

    AccountType $type;
    AccountLabel[] $labels;
    AccountAttribute[] $attributes;

    // Initializes a new account and assigns labels to the new account.
    public function __construct(
        AccountType $type,
        AccountLabel[] $labels,
        AccountAttribute[] $attributes)
    {
        $this->type = $type;
        $this->labels = $labels;
        $this->attributes = $attributes;
    }

    // Other parts of the class are omitted here.
}

I have a requirement to implement a copy constructor for this class so that a new account can be constructed by copying data from another account.

In other OOP languages, this could be done by creating an overload for the default constructor function to take in another instance of the account class for copying. However, PHP doesn't allow having two functions with the same name regardless of the arguments being different, including the __construct() function.

I cannot make the $labels argument as optional because it is actually required for creating a new account. Making it optional just to add a new argument could lead to many false positive test results. So, this implementation should be the last resort:

class Account {

    AccountType $type;
    AccountLabel[] $labels;
    AccountAttribute[] $attributes;

    // Initializes a new account and assigns labels to the new account;
    // Or, copy from another account.
    public function __construct(
        AccountType $type,
        AccountLabel[] $labels,
        AccountAttribute[] $attributes,
        Account $that)
    {
        if ($that === null) {
            $this->type = $type;
            $this->labels = $labels;
            $this->attributes = $attributes;
        } else
        {
            // Copy from another account.
            $this->type = $that->type;
            $this->labels = $that->labels;
            $this->attributes = $that->attributes;
        }
    }

    // Other parts of the class are omitted here.
}

I am also aware of the magic __clone callback function. However, I am looking for ways to implement a copy constructor, not a work-around.

3 个答案:

答案 0 :(得分:3)

PHP doesn't support methods overloading and one cannot create more than one constructor for a class.

A common way to achieve what you need is to implement a so-called "named constructor" which is just a static factory method:

class Account {

    AccountType $type;
    AccountLabel[] $labels;
    AccountAttribute[] $attributes;

    // The regular constructor
    public function __construct(
        AccountType $type,
        AccountLabel[] $labels,
        AccountAttribute[] $attributes,
    {
        $this->type = $type;
        $this->labels = $labels;
        $this->attributes = $attributes;
    }

    // A "named constructor" that works similar to a copy constructor 
    public static copyFrom(Account $account)
    {
        // Do not re-implement the constructor
        return new self($account->type, $account->labels, $account->attributes);
    }

    // Other parts of the class are omitted here.
}

Read this article for more examples.

答案 1 :(得分:1)

You have 2 options:

  1. Make 1 constructor and inspect the arguments to figure out what the intent was.
  2. Create a factory method. You could do this as a static method in the same class, or outside of the class.

I'd argue that #2 is better, because you still get the benefit of typing + you can make very intentionally phrased method names.

PHP does not support function overloading.

答案 2 :(得分:1)

Why the __clone function don´t work for you?

You can clone an object like this, the default __clone function copy all variables to the new instance.

$a = new Account(....);
$b = clone $a;

But if you don´t wont that all variable copied, than you can overwrite the __clone function in the class

class Account {
    ....
    public function __clone() {
        $this->type = null;
        ....
    }
}