1

When I run this, it complains that

Too few arguments to function Base::__construct(), 0 passed in [FileName].php on line 18 and exactly 1 expected

class Base {
    protected $var;
    public function __construct($var){
        $this->var = $var;
    }
}

class ChildA extends Base {
    public function handle(){
        echo $this->var;
    }
}

$base = new Base(10);

(new ChildA())->handle();

But as you can see, I have passed: $base = new Base(10);

Edit: As mentioned in answers and comments, the problem was not in here:

$base = new Base(10);

...but here:

(new ChildA())->handle();

What am I doing wrong? Also, what I am trying to achieve is that, there are many ChildX classes who need to extend Base and all these child classes must have the $var property. So, Instead of duplicating the $var in each child classes, I wanted to reduce typing effort by putting it in a parent class. But apparently, my plan didn't work out.

tereško
  • 58,060
  • 25
  • 98
  • 150
Tanmay
  • 3,009
  • 9
  • 53
  • 83
  • 4
    `But as you can see, I have passed: $base = new Base(10);` It is complaining about this line: `(new ChildA())->handle();`. – tkausl Sep 18 '18 at 11:25
  • 1
    That's not how inheritance works. `new ChildA` has nothing to do with `new Base`. They're independent objects and the constructor is run individually for each. – deceze Sep 18 '18 at 11:25
  • *"to reduce typing effort"* – Tip: this is usually a bad motivator and should never inform any sort of class design. If you want to type less, use a loop to repeat the same action several times or otherwise restructure your logic. – deceze Sep 18 '18 at 11:32

4 Answers4

2

Your problem is actually this line:

(new ChildA())->handle();

Do this instead:

$child = new ChildA(10);
$child->handle();

You don't need to instantiate the base class separately.

One other thing. When both parent and child classes have a constructor, you can put parent::__construct($whatever); in the child's constructor to run the parent code also.

delboy1978uk
  • 12,118
  • 2
  • 21
  • 39
  • 1
    OP wants to set `$var = 10` to all classes that extends Base – Justinas Sep 18 '18 at 11:27
  • That's what's happening in this code. – delboy1978uk Sep 18 '18 at 11:28
  • ChildA does not have `__construct` defined. So it will need no arguments in its construct statement ? – Madhur Bhaiya Sep 18 '18 at 11:28
  • 2
    the base constructor runs when the child class doesn't have one. If they both have one, but need to run both, you can make a parent:: call from the child class – delboy1978uk Sep 18 '18 at 11:29
  • 2
    Madhur, no, it uses the parents constructor – delboy1978uk Sep 18 '18 at 11:29
  • 1
    @delboy1978uk thanks for clarifying! – Madhur Bhaiya Sep 18 '18 at 11:34
  • no problem, can you mark this as the answer please? :-) – delboy1978uk Sep 18 '18 at 11:35
  • You don't need to separate the line into two, just pass the argument. `(new ChildA(10))->handle();` will work fine. – IMSoP Sep 18 '18 at 11:44
  • @delboy1978uk Thanks for your answer. But maybe using static will be the better solution here. Your answer also gets the job done but it requires me to pass argument every time I instantiate a child class. `new ChildA(arg)`, `new ChildB(arg)` and so on... – Tanmay Sep 18 '18 at 11:45
  • 1
    @Eisenheim You have to pass an argument, because you defined the constructor as requiring an argument. I'm not sure what your original problem was, but I'm not convinced that static variables are the right solution – IMSoP Sep 18 '18 at 11:51
2

This is a simple concept of class instantiation, I think which you are getting it wrong.

When you did

$base = new Base(10);

you are creating an instance of the Base class only.

However, the following statement invokes another instance of the Child class. Note, the base variable and this child instance are two different entities, hence their behaviour will not impact each other.

(new ChildA())->handle();

So, currently base variable will have a value of 10, but the handle() function call cannot be instantiated since it requires an argument which is to be supplied to the new instance of the Base class.

You'll need to instantiate the child class, due to which the constructor of the inherited class will be called first and your variable will be set. Your handle() function can then successfully return the correct value.

$child = new Child(10); $child->handle(); //returns 10
Aseem Upadhyay
  • 4,279
  • 3
  • 16
  • 36
1

When you extend a class, all the methods on that class exist on the child class by default, including "magic" methods like __construct.

So when you write this:

class Base {
    protected $var;
    public function __construct($var){
        $this->var = $var;
    }
}

class ChildA extends Base {
    public function handle(){
        echo $this->var;
    }
}

Your child class actually looks something like this:

class ChildA extends Base {
    // Inherited property from Base class
    protected $var;

    // Inherited method from Base class
    public function __construct($var){
        $this->var = $var;
    }

    // New method in this class
    public function handle(){
        echo $this->var;
    }
}

This is a slight simplification, as we'll see below, but it explains what happens in your example:

$base = new Base(10);

$base is an instance of the Base class; it is completely separate from any other instance of that class, and from any instance of the ChildA class.

(new ChildA())->handle();

This attempts to run the __construct method in the ChildA class; the copy of that method inherited from Base requires an argument, but you didn't supply one, so you get an error. It is expecting you to call it like this:

(new ChildA(10))->handle();

If we define __construct directly in the child class, we can give it a different number of arguments:

class ChildB extends Base {
    public function __construct() {
        // Do nothing
    }
    public function handle(){
        echo $this->var;
    }
}
(new ChildB())->handle();

But now we have a different problem: the logic in the Base class isn't running, so $this->var is never set to anything.

This is where I over-simplified things above. In reality, the parent __construct method and child __construct method both exist, they just "hide" each other. You can access the parent copy with the syntax parent::method.

So you can make a child class, with a constructor which takes no arguments, and when that constructor is run, run the base constructor with a hard-coded value of 10:

class ChildC extends Base {
    public function __construct() {
        // Run the constructor in the Base class
        parent::__construct(10);
    }
    public function handle(){
        echo $this->var;
    }
}
(new ChildC())->handle();
IMSoP
  • 89,526
  • 13
  • 117
  • 169
  • Do you mean, for every `ChildX` I will have to create a constructor? – Tanmay Sep 18 '18 at 11:39
  • If you want it to be different from the one in `Base`, then you have to tell PHP how it is different. If you want it to be the same, then you let it be inherited. Your code will run fine if you write `(new ChildA(10))->handle();`, because then you are passing the argument that the constructor requires. – IMSoP Sep 18 '18 at 11:41
0

Your new ChildA() is calling constructor from Base and you are not passing any arguments here.

You make mistake here: when you do new Base(10) your $var is not passed to new instance of new ChildA()

In your case I would use static parameter instead of constructor value

class Base {
    protected static $var;

    public function __construct(){
    }

    public static function setVar($var) {
        self::$var = $var;
    }
}

class ChildA extends Base {
    public function handle(){
        echo self::$var;
    }
}

Base::setVar(10);

(new ChildA())->handle();
Justinas
  • 41,402
  • 5
  • 66
  • 96
  • I hate static as much as the next guy, but this is a solution to the apparent problem, so not sure why you'd get a drive by down vote. Have a +1. – bishop Sep 18 '18 at 11:33
  • @bishop Why hate static? If you need single value to be shared across all instances of class, then it's perfect, isn't it? – Justinas Sep 18 '18 at 11:35
  • Yes, I think I will accept this as an answer. With this approach, I don't have to worry about passing the value during every child class instantiation. – Tanmay Sep 18 '18 at 11:36
  • I've downvoted this answer, because I think introducing static variables just complicates the picture; I think it would be better to start by understanding how inheritance and constructors work, and thinking about what problem you're trying to solve. – IMSoP Sep 18 '18 at 11:47
  • @IMSoP So you are saying that my answer does not solve *set variable once for all instances* question? – Justinas Sep 18 '18 at 11:51
  • @Justinas In your example, `Base::$var` might as well be a global variable - it has no relationship to the child classes, and every child class shares not just the same variable, but *the same value of that variable*. It's not at all clear to me that that was the requirement, or that this is a useful coding pattern to learn. – IMSoP Sep 18 '18 at 11:55
  • @IMSoP I will start by saying that I am not an OOP expert. But I guess why you guys are getting mad at static var is because it shares *the same value of that variable* across all the child classes. However I think in my use case, it would be fine. Because there will always be a single child class in action. There will never be a situation like `new ChildA() and new ChildB()`, it will always be `new ChildA() or new ChildB() or new ChildC()`. So during the lifecycle of the execution one child class will not be affected by another. – Tanmay Sep 18 '18 at 12:07
  • But I am glad that many people shared their point of views which will help me understand OOP better. Upvoting everyone's answer to appreciate their effort. :) – Tanmay Sep 18 '18 at 12:10