Why can't I overload constructors in PHP?

73,075

Solution 1

You can't overload ANY method in PHP. If you want to be able to instantiate a PHP object while passing several different combinations of parameters, use the factory pattern with a private constructor.

For example:

public MyClass {
    private function __construct() {
    ...
    }

    public static function makeNewWithParameterA($paramA) {
        $obj = new MyClass(); 
        // other initialization
        return $obj;
    }

    public static function makeNewWithParametersBandC($paramB, $paramC) {
        $obj = new MyClass(); 
        // other initialization
        return $obj;
    }
}

$myObject = MyClass::makeNewWithParameterA("foo");
$anotherObject = MyClass::makeNewWithParametersBandC("bar", 3);

Solution 2

You can use variable arguments to produce the same effect. Without strong typing, it doesn't make much sense to add, given default arguments and all of the other "work arounds."

Solution 3

For completeness, I'll suggest Fluent Interfaces. The idea is that by adding return $this; to the end of your methods you can chain calls together. So instead of

$car1 = new Car('blue', 'RWD');
$car2 = new Car('Ford', '300hp');

(which simply wouldn't work), you can do:

$car = (new Car)
       ->setColor('blue')
       ->setMake('Ford')
       ->setDrive('FWD');

That way you can pick exactly which properties you want to set. In a lot of ways it's similar to passing in an array of options to your initial call:

$car = new Car(['make' => 'Ford', 'seats' => 5]);

Solution 4

True overloading is indeed unsupported in PHP. As @Pestilence mentioned, you can use variable arguments. Some people just use an Associative Array of various options to overcome this.

Solution 5

PHP Manual: Function Arguments, Default Values

I have overcome this simply by using default values for function parameters. In __constuct, list the required parameters first. List the optional parameters after that in the general form $param = null.

class User
{
    private $db;
    private $userInput;

    public function __construct(Database $db, array $userInput = null)
    {
        $this->db = $db;
        $this->userInput = $userInput;
    }
}

This can be instantiated as:

$user = new User($db)

or

$user = new User($db, $inputArray);

This is not a perfect solution, but I have made this work by separating parameters into absolutely mandatory parameters no matter when the object is constructed, and, as a group, optional parameters listed in order of importance.

It works.

Share:
73,075

Related videos on Youtube

tom
Author by

tom

Updated on July 08, 2022

Comments

  • tom
    tom almost 2 years

    I have abandoned all hope of ever being able to overload my constructors in PHP, so what I'd really like to know is why.

    Is there even a reason for it? Does it create inherently bad code? Is it widely accepted language design to not allow it, or are other languages nicer than PHP?

    • Anthony Rutledge
      Anthony Rutledge about 7 years
      One thing is certain. The lack of explicit constructor overloading, as in the Java or C++ sense, makes you think hard about what should truly be in the parameter list (given that many will overcome this problem using default values for function parameters). :-)
    • clockw0rk
      clockw0rk over 3 years
      isn't dependency injection kind of overloading constructors?
  • Sam Giles
    Sam Giles about 12 years
    +1 For clean-ness, this method states with clear intent what you are trying to achieve and for me is a lot cleaner.
  • dotancohen
    dotancohen about 11 years
    Actually, overloading means to run a different function depending on the number (and types, for strongly-typed languages) of arguments. This is different than having the same function behave differently. For instance, I am now editing a class created by someone who had heard of OOP but didn't know how to use it. I would like to create a constructor that my code will call, but I don't want to break her working code.
  • xorinzor
    xorinzor over 10 years
    people who will try this, don't bother. It won't work, __construct<1-3> do not extend the default constructor and are basically the same as any other custom function in a class right now
  • Dennis
    Dennis about 10 years
    doesn't this break Single Responsibility Principle? You are creating your class (one responsibility), and you are also doing various class-functions (your main class responsibility). And in real-world scenarious you may need to read some of the $parameters from outside sources i.e. Databases, which creates yet another responsibility.
  • Christian Bongiorno
    Christian Bongiorno almost 9 years
    I am new to PHP -- having done tons of Java: HOLY limitation batman! Your example doesn't even follow the factory pattern. en.wikipedia.org/wiki/Factory_method_pattern
  • Mirko
    Mirko over 7 years
    Well I have to say this is the way I like the most, very readable and flexible
  • Pedro Amaral Couto
    Pedro Amaral Couto about 7 years
    But it's not immutable and you can instantiate the class with an invalid state.
  • J.D. Sandifer
    J.D. Sandifer almost 7 years
    @Chrisian It's a static factory method. See Factory (object-oriented programming) for more general information about factories. They're really anything that creates an object instead of using new.
  • Xavi Montero
    Xavi Montero almost 6 years
    This breaks the idea behind ValueObjects that should never have setters.
  • Xavi Montero
    Xavi Montero almost 6 years
    As @J.D.Sandifer said, it's a factory. If anyone does not like "static factories" for clean code, you can always create a MyClassFactory object that has makeNewWithParameterA() and makeNewWithParametersBandC() without being static, so they are more suitable for "dependency injection" frameworks. But if you control the environment and don't want to create the "factory class" this is the cleanest way, as @SamGiles said. The solutions with same constructor and switch-cases to invoke private methods are bad code.
  • Mazzy
    Mazzy over 5 years
    Another problem is that you are not certain if all variables are set that are required to be set. Secondly no compile-time error is thrown, rather you have to rely on a runtime error that may or may not occur depending on the usage of the variables.
  • Mazzy
    Mazzy over 5 years
    So how does this work with type-hinting? RIght now it is only looking at the amount of variables
  • Hassan Faghihi
    Hassan Faghihi over 5 years
    @Mazzy since 2012 i didn't wrote any PHP code, so i'm afraid i should say, type hinting doesn't ring a bell in my head
  • Anthony Rutledge
    Anthony Rutledge about 4 years
    If one intends on using a class in with a dependency injector, is this a form of establishing initial state that can be used? Also, what happens when the object your are instantiating needs to pass values to the super class / parent class constructor?
  • Donald Duck
    Donald Duck over 2 years
    That's not always possible, for example if I want one overload to be public and the other overload to be private.
  • theking2
    theking2 about 2 years
    A very elegant solution. Probably not compiled but for constructors it would do the trick.