Background
In a project with a PHP 5.6 runtime, I need to work around some third-party code. This code will not be changed by the vendor, nor will it be removed from the codebase.
Specifically, the third-party code (let's call its namespace Theirs
) contains a class (\Theirs\BaseClass
) whose constructor instantiates another class (\Theirs\Detector
).
BaseClassTheirs.php:
<?php namespace Theirs;
class Detector {
public function __construct() {
print "Theirs\n";
}
}
class BaseClass {
public function __construct() {
$detector = new Detector();
}
}
I do not want BaseClass
to instantiate \Theirs\Detector
. Instead, I want BaseClass
to instantiate a different Detector
class, from a different namespace (Mine
) that is outside of the third-party's control.
In all other respects, though, I want BaseClass
to behave as it does in the third-party code, including if the vendor later adds additional functionality to \Theirs\BaseClass
. (I'll call this property "non-fragility" and the lack of it "fragility".) As such, it seems sensible for me to create my own \Mine\BaseClass
as a child of \Theirs\BaseClass
, inheriting everything from it.
If I take the fragile, non-DRY approach of copy-pasting \Theirs\BaseClass
's constructor into \Mine\BaseClass
, then \Mine\Detector
is instantiated, as I desired:
BaseClassMine.php:
<?php namespace Mine;
include "BaseClassTheirs.php";
class Detector {
public function __construct() {
print "Mine\n";
}
}
class BaseClass extends \Theirs\BaseClass {
public function __construct() {
$detector = new Detector();
}
}
\\ Prints "Mine"
$obj = new BaseClass();
However, if I change this into a DRY, non-fragile approach by removing the duplicated code so that \Mine\BaseClass
invokes exactly the same constructor, but as inherited from its parent rather than being copy-pasted, then \Theirs\Detector
gets invoked, which is not what I want:
BaseClassMine.php:
<?php namespace Mine;
include "BaseClassTheirs.php";
class Detector {
public function __construct() {
print "Mine\n";
}
}
use \Mine\Detector;
class BaseClass extends \Theirs\BaseClass {
}
\\ Prints "Theirs"
$obj = new BaseClass();
This happens regardless of whether the file contains a use \Mine\Detector;
line, as above.
Question
How can I get the best of both approaches?
I.e. how can I invoke \Theirs\Baseclass
's constructor from \Mine\Baseclass
's constructor in order to have it invoke \Mine\Detector
, as though \Theirs\Baseclass
's constructor had simply been copy-pasted into \Mine\Baseclass
's context, but without actually copy-pasting it and introducing the corresponding fragility?
For instance, is there a good way to use reflection or some other introspective technique to dynamically read the parent's constructor and to "paste" it at runtime into the child class?