1

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?

Related but not identical questions

  • Assuming `$detector` is in fact a property, what is that property's visibility? – Félix Adriyel Gagnon-Grenier Jul 28 '18 at 20:50
  • @FélixGagnon-Grenier, thanks for your question. In my example, `$detector` is not a property, nor does it have a visibility modifier. It is simply being used to detect whether the constructor is being executed from the parent's scope (i.e. early binding, IIUC) or the child's scope (i.e. late binding, IIUC). –  Jul 29 '18 at 02:37
  • `"Specifically, the third-party code (let's call its namespace Theirs) contains a class (\Theirs\BaseClass) whose constructor instantiates another class (\Theirs\Detector)".` I would imagine that this other class is not simply instantiated for the sake of it. How is it used? Is it assigned to a property, or is it only used in the constructor? – Félix Adriyel Gagnon-Grenier Jul 29 '18 at 02:42
  • @FélixGagnon-Grenier, , in the actual codebase, the child classes I am interested in are quite diverse. They instantiate various other classes for various purposes. But in all cases, the problem I have described above in simplified form, is present: *the need to be able to instantiate them from the child's scope rather than the parent's (in order to use the correct namespaces in each case), without introducing fragility into the codebase by copy-pasting code from the parent into the child.* –  Jul 29 '18 at 03:17

0 Answers0