Level-2 / Dice

Dice - a lightweight Dependency Injection Container for PHP

Home Page:https://r.je/dice

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Shared chain call

pattisahusiwa opened this issue · comments

I've rules like this.

{
  "Factory": {
    "shared": true
  },
  "$object": {
    "instanceOf": "Factory",
    "call": [
      [
        "getObject",
        [],
        "Dice::CHAIN_CALL"
      ]
    ]
  }
}

Calling $dice->create('$object') or $dice->create('Factory') multiple times will return the same object as expected.

The Factory also needs to access the same instance as defined in $object. However, in the code below $objectA and $objectB point to different instances.

$objectA = $dice->create('$object');

$factory = $dice->create('Factory');
$objectB = $factory->getObject();

Can this feature be added to Dice? So, both $objectA and $objectB point to the same instance.

Since $object is considered a different instance of Factory then it will not be the same. If they were shared then you would never be able to define a different version of the object, for example if you had PDO and $otherPDO.

Honestly, I've rules like this

{
  "$objectA": {
    "instanceOf": "Factory",
    "call": [
      [
        "getObjectA",
        [],
        "Dice::CHAIN_CALL"
      ]
    ]
  },
  "$objectB": {
    "instanceOf": "Factory",
    "call": [
      [
        "getObjectB",
        [],
        "Dice::CHAIN_CALL"
      ]
    ]
  }
}

Both rules require the same instance, so the rule below is added. However, current implementation doesn't allow this use case.

  "Factory": {
    "shared": true
  }

If they were shared then you would never be able to define a different version of the object, for example if you had PDO and $otherPDO.

We can use something like this

{
  "$otherPDO": {
    "instanceOf": "PDO",
    "shared": false
  }
}

We can use something like this

I just remembered that there is the inherit: false syntax to prevent the issue I was describing.

I think the main issue here is that $objectA and $objectB are not actually instances of Factory but instead something created by it. In my experience the config {"Dice::INSTANCE" : ["Factory", "getObjectA"]} would return the same objectA instance every time it is retrieved. This issue is that the instanceOf and call method of getting objectA is different in the way it processes it.

Suppose I've

$factory = new Factory();

$classA = new ClassA($factory->getObjectA());
$classB = new ClassB($factory->getObjectB());

$classC = new ClassC($classA, $classB);

I need to create $classC directly without create $factory, $classA, and $classB manually.

$classC =  $dice->create('ClassC');

Can dice be used for this use case?

I don't think this is currently possible with json rules, but you can do it with closures:

$dice->addRules([
	'classC' => [
		'substitutions' => [
			'ClassA' => [\Dice\Dice::INSTANCE => function() use ($factory) {
			            return $factory->getObjectA();
			}],
			'ClassB' => [\Dice\Dice::INSTANCE => function() use ($factory) {
			            return $factory->getObjectB();
			}]
		]
	]
]);

Factory and DIC are different approaches to the same problem. With DIC you generally don't need factories as the same can usually be achieved in the container and all the factory is really doing is providing functionality already provided by the container:

{
	"ClassA": {
		//rules for objectA instantiation (currently in your factory)
	},
	"ClassB": {
		//rules for objectB instantiation (currently in your factory)
	}
}
class ClassC {
	public function __construct(ClassA $objectA, ClassB $objectB) {

	}

}


$dice->create('ClassC');

We can use something like this

I just remembered that there is the inherit: false syntax to prevent the issue I was describing.

I think the main issue here is that $objectA and $objectB are not actually instances of Factory but instead something created by it. In my experience the config {"Dice::INSTANCE" : ["Factory", "getObjectA"]} would return the same objectA instance every time it is retrieved. This issue is that the instanceOf and call method of getting objectA is different in the way it processes it.

The main issue is objectA is initialized inside factory and is passed into objectB by the factory itself. So, when objectA is changed, objectB has the state too.

This use case is similar to Aura/Router. Map, Matcher, and Generator can be retrieved from RouterContainer. However, Matcher and Generator point to the same Map that reside inside the container.

@pattisahusiwa If you know the class you are passing it into then it is possible currently.
@TRPB Using Dice::INSTANCE will allow passing the same instance here because Dice::INSTANCE calls create then calls the methods on it when the instanceOf and call method does not create the same factory first.

Example code that I believe should work here (because I have had this same issue before)

{
    "Factory": {
        "shared": true
    },
    "ClassC" : {
        "constructParams" : [
            {"Dice::INSTANCE" : ["Factory", "getClassA"]},
            {"Dice::INSTANCE" : ["Factory", "getClassB"]},
        ]
    }
}

@TRPB @solleer thank you. It works.