10
--- A.php ----
require_once 'B.php';

class A
{
    public function __constructor($x){...}

    public function foo()
    {
        $b = B::getInstance();
        ...
    }
}

--- B.php ----
require_once 'A.php';

class B extends A
{
    protected static $_instance = null;

    protected function __construct(){}

    public static function getInstance()
    {....}
}

PHP just stops interpreting the code as soon as it reaches line

protected function __construct(){}

and outputs everything before and nothing that would have been sent to the browser afterwards.

And as soon as I just take that line out, by changing it to

// protected function __construct(){}

everything works fine!?

I don't get that.

Any ideas?

David Tang
  • 92,262
  • 30
  • 167
  • 149
Raffael
  • 19,547
  • 15
  • 82
  • 160
  • 1
    Do you have php opening tags in the files, e.g. do they start with ` – Gordon Jan 17 '11 at 10:37
  • 1
    The __construct() method will need to be public as it is accessed (publically) when an instance of the class is created. – Matt Lowden Jan 17 '11 at 10:38
  • Your pseudo-code doesn't have any obvious issue so the problems is most likely in your real code. If PHP stops running and doesn't display any error message, you probably need to enable full error reporting. Feel free to ask for details if you don't know how. – Álvaro González Jan 17 '11 at 10:41
  • 1
    Typo? In class `A` the ctor is called `__constructor`, not `__construct`. – Linus Kleen Jan 17 '11 at 10:42
  • hmmm ... @middaparka & @Matt Lowden oh yes you very well can define a constructor as protected. have a look for "singleton pattern" on php.net , for example. @Gordon and if I wouldn't have opening tags in my files, the commenting wouldn't affect the parsing, right?! I guess it's b/c I can't overwrite the access to the constructor in a child class, cause changing the constructor in A to protected will solve the problem as well. Actually I want to implement B as singleton but also have it extend A, so I can access protected functions and variables. – Raffael Jan 17 '11 at 12:52

3 Answers3

12

I've just created a simple test-file to confirm whether this happens on my machine too, and I think I've found the answer. Take the following code:

<?php
error_reporting( E_ALL | E_STRICT );
class Foo {
    public function __construct( ) {
    }
}

class Bar extends Foo {
    protected function __construct( ) {
    }
}

When trying to execute that code, I get a fatal error: "PHP Fatal error: Access level to Bar::__construct() must be public (as in class Foo) in /home/berry/foo.php on line 12." That means you can't change the access level in a child class, if the parent has already defined the access level, which actually makes a lot of sense: PHP wouldn't know which constructor to call, I guess.

As a side note: by looking at your code, B extends A, and A uses B. Why exactly is that so, it seems like a strange construction to me? I'm guessing you actually want is composition, not inheritance.

Community
  • 1
  • 1
Berry Langerak
  • 18,561
  • 4
  • 45
  • 58
  • hey Berry, thanks for your answer. As I wrote in my answer I need to access protected elements in A and need B to be a singleton. But it is somewhat strange in deed, b/c of the "refactoring-situation" I am facing. I'm not sure if chose the best option but it should be the most straightforward for now, I think. – Raffael Jan 17 '11 at 10:48
  • Hi. This design seems to be odd. You need a class that needs access to the protected methods of an (unrelated?) other class, which uses the other one? Perhaps you'd benefit from explaining the situation, see if there's anything good we can say about it :) – Berry Langerak Jan 17 '11 at 11:14
  • I think it's debatable how much sense it makes for PHP to behave this way (see https://bugs.php.net/bug.php?id=61970). But the official reason given for this is the Liskov Substitution Principle, which basically states that instances of derived classes should "fit" where a base class instance is expected. – Peter Dec 04 '12 at 20:28
8

You can define a constructor as protected or private. This code compiles runs just fine since OOP was rewritten for PHP/5:

<?php

class A{
    public function __construct(){
        echo 'Instance of A created' . PHP_EOL;
    }
}

class B{
    protected function __construct(){
        echo 'Instance of B created' . PHP_EOL;
    }
}

class C{
    private function __construct(){
        echo 'Instance of C created' . PHP_EOL;
    }
}

Of course, private constructors prevent you from creating an instance with the new keyword, but PHP will trigger a fatal error (it won't just stop running):

<?php

new A; // OK
new B; // Fatal error: Call to protected B::__construct() from invalid context
new C; // Fatal error: Call to private C::__construct() from invalid context
Álvaro González
  • 142,137
  • 41
  • 261
  • 360
1

You can create custom static initiators:

<?php

class FancyClass {
    public static function init() {
        new self();
    }

    protected function __construct() {
        echo 'Instance created!';
    }
}

# This will not work.
new FancyClass();

# But this will work.
FancyClass::init();
ecabuk
  • 1,641
  • 1
  • 18
  • 20