7

I got two classes, "A" and "B". In the application logic no one is allowed to create an object of class "B", except for class "A". But, since I dont want to have the two classes in the same file I cant restrict it with the "private" properity.

Is it possible to create this kind of restriction? If someone other then "A" tries to create an object of class "B", you say piss off!?

Emil
  • 71
  • 2

5 Answers5

7

This is as hacky as it get's and you should not use it. I only post it, because I like hacky things ;) Furthermore this will throw an error if E_STRICT error reporting is enabled:

class B
{
    private function __construct() {}

    public function getInstance() {
        if (!isset($this) || !$this instanceof A) {
            throw new LogicException('Construction of B from a class other than A is not permitted.');
        }

        return new self;
    }
}

class A
{
    function someMethod() {
        $b = B::getInstance(); // note that I'm calling the non-static method statically!
    }
}

The reason why this works is a "feature" which may be seen in the second example of this manual page.

NikiC
  • 100,734
  • 37
  • 191
  • 225
  • Bear in mind that while this works, it does cause PHP to spit an `E_STRICT`. – BoltClock Oct 16 '10 at 09:42
  • if somebody **really** wants to use this hack, he better enable `error_reporting(-1)` and use `@$b = B::getInstance();` , but I am not sure if the `@` will suppress all warnings/errors generated in getInstance method – wadkar Oct 21 '11 at 09:06
5

You could inspect the backtrace:

class B
{
    public function __construct()
    {
        $chain = debug_backtrace();
        $caller = $chain[1]['class'];

        if ('A' != $caller) {
            throw new Exception('Illegal instantiation');
        }
    }
}
Nev Stokes
  • 9,051
  • 5
  • 42
  • 44
0

In the constructor of B, require that A be passed in. When you then want to get B from A, just create a B and pass in A. When new B is called, it will require A be passed in.

class A
{
    private $b;

    private function getB()
    {
        if (null === $this->b)
        {
            $this->b    = new B($this);
        }

        return $this->b;
    }
}

class B
{
    public function __construct(A $a)
    {

    }
}
MANCHUCK
  • 2,424
  • 1
  • 16
  • 22
  • Actually thinking about it that is correct. Let me test out what happens if A follows a singleton pattern. – MANCHUCK Oct 15 '10 at 15:46
  • You could also clone an existing instance of B. And since the class will get instantiated by those who write the code anyway, there is very little point in restricting access this way. Class A and B will also be a pain to test this way, since A has a hardcoded dependency and B always requires an A Mock. Making B a Singleton will throw you into hell completely. IMO the value you get for restricting access this is not worth the penalty. – Gordon Oct 15 '10 at 15:59
  • 1
    Maybe a better solution would be to use PHPcs. Creating your own sniffer to detect weather B was called from A. Every time you want to deploy the code, just run the sniffer and see if it catches it – MANCHUCK Oct 15 '10 at 16:15
0

Maybe you want to use something like this:

class A
{
        protected function __construct ()
        {
        }
}

class B extends A
{
        public function __construct ()
        {
                $a = new A();
        }
}

$b = new B();
matthiasz
  • 21
  • 1
-1

Use get_called_class to find out which class tries to instantiate an object:

class B
{
        public function __construct ()
        {
                if(get_called_class() != 'A') {
                    //booboo
                }
        }
}
NikiC
  • 100,734
  • 37
  • 191
  • 225
rthrwht
  • 21
  • 1