How can I best drop the top of an object hierarchy?
For instance, let’s say, during runtime, I build up this simple object hierarchy.
So, to get the ObjectA, I’d need
$ObjectZ->ObjectA
ObjectZ is injected into another object, but I want to only call the child objects within the class like $ObjectA->someProperty or $ObjectB->someMethod() I don’t want $ObjectZ, as it is like only a container for the actual collection of objects I want.
Is there a way to drop the parent object and only access the child objects?
trait FooTrait{
public function shared($value){
return $value;
}
}
Class Foo{
use FooTrait {
shared as template;
}
public function yourMethod($value){
$shared = $this->template();
return $value.' '.$shared;
}
}
Reading what Tom said… here is the next question. Can objects be injected dynamically? I can determine which objects are needed, but they can differ depending on what is happening within the application.
Yes. Generally this logic will be in a different class to the class having the objects injected so you can do this:
if ($x) {
$foo = new Foo(new ObjectX);
}
else {
$foo = new Foo(new AlternativeX);
}
if you need the logic in the same class you can use a factory:
class Foo {
public function __construct(Closure $xFactory) {
$this->xFactory = $xFactory;
}
public function doSomething($bar) {
if ($bar) $x = $this->xFactory('somearg');
else $x = $this->xFactory('someotherarg');
}
}
However, be wary of this:
class Foo {
public function __construct(Closure $xFactory) {
$this->xFactory = $xFactory;
}
public function doSomething($bar) {
$x = $this->xFactory($bar);
}
}
Because this is better expressed as
class Foo {
public function doSomething(X $x) {
}
}
My DIC Dice can handle most of the complexities introduced here for you
$dice = new \Dice\Dice;
$rule = new \Dice\Rule;
$rule->substitutions['ObjectX'] = new \Dice\Instance(function() {
if ($something) return new ObjectX;
else return new AlternativeX;
});
$dice->addRule('Foo', $rule);
$foo = $dice->create('Foo');
and then when Foo is constructed, the closure will be run to determine which dependency to inject
Tom, it should be possible to have a dynamic rule, right? For instance, instead of ObjectX or AlternativeX being called, I could use variables, which hold the object names and create them “on the fly”? That would be so cool.
How does the variable $something get into the rule? I mean, how does the value get passed into Dice? If I unsderstand how that works, then I think I understand how I can use Dice to solve the problem.
Maybe your problem can be solved with introspection and reflection? There is an article about it in the site point: http://www.sitepoint.com/introspection-and-reflection-in-php/ . I am not specialist in that field so I can’t help with it but to me this sounded like it could be related to your problem yes? no? Waiting for Tom’s answer =)
EDIT:
I am not even sure if this is close to what you meant in the original post, but made an quick example to test out the reflection which I think could be related to your problem. So we have a container that has objects which type “we dont know” and then we wanna call some methods on them and find out the object classes.
class Container {
private $objectArray = array();
public function __construct($objArray) {
$this->objectArray = $objArray;
}
public function getObjects() {
return $this->objectArray;
}
}
class Class1 {
public function someMethod1($className) {
echo 'someMethod1 was called from class ' . $className . '.<br/>';
}
public function someAnotherMethod1($className) {
echo 'someAnotherMethod1 was called from ' . $className . '.<br/>';
}
}
class Class2 {
public function someMethod2($className) {
echo 'someMethod2 was called from class ' . $className . '.<br/>';
}
}
class Class3 {
public function someMethod3($className) {
echo 'someMethod3 was called from class ' . $className . '.<br/>';
}
}
// Initialize container with some objects
$objects[] = new Class1;
$objects[] = new Class2;
$objects[] = new Class3;
$obj = new Container($objects);
// Get container's objects, find out their class names and methods then call the methods with parameter.
foreach ($obj->getObjects() as $o) {
$reflection = new ReflectionClass($o);
$methods = $reflection->getMethods();
foreach ($methods as $reflectionMethod) {
$reflectionMethod->invoke($o, get_class($o));
}
}
The above example will print
someMethod1 was called from class Class1.
someAnotherMethod1 was called from class Class1.
someMethod2 was called from class Class2.
someMethod3 was called from class Class3.
However, your example is only helping with the question of handling unknown classes within the class. The issue isn’t the fact they are unknown, but actually injecting the unknowns, to then be known or usable in the class the objects are injected into. Let me explain.
Say you have a templating system. The objects you want to inject for producing the dynamic output can be any set of objects. You have, however, only one entry point into your templating module (and no, we aren’t using a controller for this That is the whole challenge!) . You can have a container ObjectX, but then you would have something like $ObjectX->ObjectA->method1() or $ObjectX->ObjectB->property2 or ObjectX.ObjectA.method1()to bind the data in the templates. What I am looking for is not to contain the needed objects in ObjectX, but rather be able to dynamically inject ObjectA, ObjectB or any other objects created at run time and inject only them into the templating system.
$foo = new Foo;
$rule->substitutions['ObjectX'] = new \Dice\Instance(function() use ($foo) {
if ($foo->bar) return new ObjectX;
else return new AlternativeX;
});
Of if you want to get even smarter:
$rule->substitutions['ObjectX'] = new \Dice\Instance(function($dice) {
$foo = $dice->create('Foo');
if ($foo->bar) return $dice->create('ObjectX');
else return $dice->create('AlternativeX');
});
Interesting. That solution seemed a bit cludgy to me. And from a testing standpoint, I’d need to introduce some sort of DI system anyway, so I am looking at Tom’s Dice currently, just to wrap my head around introducing a DIC to my system.
If you don’t want a full DIC solution you could always write your own simplistic function that does it (it’s probably not worth using a container for a single point of use)
$foo = new Foo;
$di = function() use($foo) {
if ($foo->bar) $args = [new ObjectX, new ObjectY];
else $args = [new AlternativeX, new AlternativeY];
return new Injectable(...$args);
};
use $injected = $di() in place of $injected = new Injectable and you’re good to go.
True, but the exercise is to learn more than anything else.
And, to my dismay, I am not getting my head wrapped around this from a theoretical standpoint. Maybe because the use case is not really explained properly. So here goes.
Let’s say I have a Templater class for my templating sub-system. And let’s say, I have a set of classes, ClassA, ClassB and ClassC, which create objects to be injected into Templater for one request, but in another request ClassD, ClassE and ClassF need to be injected into Templater (or even other objects for other requests). The building of the data objects is completely dynamic, but completely dependent on the request and the subsequent data needed for the proper building of the template output.
In other words, the request defines the core object for the returned page. In my example above that core object would be Objects A or D, but the extraneous templates needed to build the page, which need the other objects, (B, C, E and F) call on those objects, because they are defined in the templates.
Instead of injecting the objects, would it make more sense to just have a factory to build the objects dynamically within Templater? Hmm…this is where the words “A half knowledge is a dangerous thing” comes to mind. I think that is the right quote. LOL!