I have two classes A
and B
, both inheriting from the same parent. In PHP, is there a way to make sure that class B
cannot be instantiated except from within class A
?
(Class B
is not a child of A
.)
I have two classes A
and B
, both inheriting from the same parent. In PHP, is there a way to make sure that class B
cannot be instantiated except from within class A
?
(Class B
is not a child of A
.)
Using debug_backtrace:
class Ancestor{}
class A extends Ancestor{
public function buildB()
{
return new B;
}
}
class B extends Ancestor{
public function __construct(){
$backtrace = debug_backtrace();
if( $backtrace[1]['class'] !== 'A' )
throw new Exception("Don't you dare!");
echo "Built successful!\n";
}
}
Try it:
//Everything ok this way:
$a = new A;
$a -> buildB();
// You will have an exception in any other case:
try{
$b = new B;
}catch(Exception $e){
echo $e -> getMessage();
}
EDIT: if you want to be sure to create B
just inside A
's code, you can do as well - just make buildB
private ^^.
I'm not thinking of this from a php perspective, but more from the oop side...think the only way you could accomplish it is if you made B's constructor private, then exposed a static method accepting a parameter of A and an out parameter of B, then that method could privately instantiate B and return it to A through the out parameter.
//Pseudocode, language-indifferent
class A{
var _B;
public B GetMeAnInstanceOfB(){
_B=B.CreateInstanceOfB(this);
}
//alternate
public B GetMeAnotherInstanceOfB(){
_B=new B(this);
}
}
class B{
private B();
//alternate
private B(A);
static B CreateInstanceOfB(A){
return new(b);
}
}
That's really crude and probably full of potholes, but there's a stab at it. Technically, subclasses of A could still get a B, so if you sealed the class (prevented subclasses), that would close that door.
Interesting question, to be sure...
EDIT: This mod really doesn't fix the problem, but maybe(?) it's better - I've created a public constructor for B that takes an A as a parameter, and the instantiation of B now takes place only in A. The only problem is that it persists with the same problem JDelage pointed out - if I instantiate A, I can build a B...
Yes.
How you can go about achieving it is, make the class B
's constructor accept one argument. And define a method for class A
that makes objects of B
.
Die or throw execption if $argument == null
|| !($argument instanceof A)
.
Example code:
class X {
public $i = 0;
public function getI() {
return $i;
}
public function setI($x) {
$i = $x;
}
}
class A extends X {
public function setI($x) {
$i = $x * 2;
}
public function makeB($var){
$b = new B($var);
}
}
class B extends X {
public function __construct($a) {
if (null == $a) {
echo "no arguments given!\r\n";
//exit;
}else if (!($a instanceof A)) {
echo "disallowed\r\n";
//exit;
}else{
echo "initialized b\r\n";
}
}
public function setI($x) {
$i = $x * 3;
}
}
$a = new A();
$a->makeB();
$a->makeB(new X());
$a->makeB(&$a);
Output:
Warning: Missing argument 1 for A::makeB(), called in file.php
no arguments given!
disallowed
initialized b
You can see a demo here.