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.

Believe2014
  • 3,894
  • 2
  • 26
  • 46

3 Answers3

4

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.

axiac
  • 68,258
  • 9
  • 99
  • 134
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.

Evert
  • 93,428
  • 18
  • 118
  • 189
  • This is an interesting approach. Could you please write a sample code to demonstrate the factory method? I really appreciate your help. – Believe2014 Mar 25 '18 at 20:24
  • I think the second approach is the same one that @axiac proposed – MAZux Mar 25 '18 at 20:58
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;
        ....
    }
}
Cyperghost
  • 63
  • 1
  • 8
  • Thanks for providing an answer. However, this is a work-around using the magic `__clone` method. My question is to find a way to implement a constructor function. That's why I disclose the work-around using optional argument. – Believe2014 Mar 25 '18 at 20:21
  • Okay, no problem. Have you tried it with the func_num_args, instanceof functions to solve your problem? – Cyperghost Mar 25 '18 at 21:46