We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

[Feature/bug] Clear DI shared services already instantiated is actually impossible.

Hello, i'm opening a new topic because this feat could solve also other problem than my actual issues.

I haven't found a way to reset DI shared services already instantiated.

Debuggin my app i discovered that Phalcon use the DI to store also services not initially registered. For example it seems the Response service in added to DI after an MVC action return. That response service is set as a Shared service and there is no way to remove or reset it. I tried everything

  • add a New response object as single or shared
  • remove and then re-add a new instance

The only solution is to create a new DI (not desirable)... It could be a cool feature to be able to reset/clear instantiated shared services. The first thing it could help is Unit testing (i'm actually involved with)

https://docs.phalcon.io/en/latest/api/Phalcon_DI.html

How about reset() to reset the container or for a particular service remove($service) to unset the service and replace it with whatever you want?

I haven't tried those myself, just throwing ideas here.

Hi Nikolas, actually reset() empty the container so you have to re-add all services like if you create a new DI object (not very desirable)... remove() remove the service but don't delete the cached shared services... it mean if you make for example

$di->set("myservice", new MyService(), true);
$di->get("myservice")->setField1("FooBar");
$di->remove("myservice");
$di->set("myservice", new MyService(), true);
echo $di->get("myservice")->getField1(); //this print FooBar

I wrote this code just here, not really tested if it work, but i've verified this via XDEBUG with the Response object of an MVC app... This is why the title of topic is Feature/Bug... it may be a bug because one expect that "remove()" effectively delete the service and the shared instance too...

I was able to replicate this bug, here's my github gist: https://gist.github.com/mikegioia/8546422

You can see that tests 2 and 3 erroneously return the original value "something". I don't think the ->remove() or second ->setShared() affect the service at all. Only until I do a hard reset on the DI does it allow me to overwrite that service.

While I don't think this affects the majority of use cases, what it does impact is unit tests for anyone using Phalcon\DI as a service locator who doesn't want to reset the DI object for every test.

I went through the di.c file in cphalcon github and noticed that this might be the cause of the problem: https://github.com/phalcon/cphalcon/blob/master/ext/di.c#L448

In the getShared() method of the DI object, line 448 checks if the service name exists in array "shared_instances". However, in remove() on line 196, the service is only being removed the "services" array. I haven't written C in a while and I certainly am a newb with the cphalcon codebase, but my best guess is that the shared services are staying in shared_instances and not being cleared from that array properly when remove() is called.

I did notice that after I call DI::getDefault()->remove( 'service' ), a subsequent call to DI::getDefault()->getService( 'service' )->getDefinition() throws an exception saying the service wasn't found in the dependency injection container.

Sorry for spamming this thread, but I re-ran my gist using ->set() and ->get() instead of ->setShared() and ->getShared() and all of them worked correctly. The DI container was properly removing the services; I think this bug only affects shared services.

Did exactly the same investigation yesterday night and arrived to the same conclusions... i planned to write about it this morning but i had some hitch... Happy to have confirmations on my investigation. And no, you are absolutely not spamming this thread... on the contrary your posts are verfy helpful!



51.3k

The only way ATM:

                /**
                 * @var \Phalcon\Di\Service
                 */
                $service = $this->di->getService('myService');
                $this->di->remove($service->getName());
                $this->di->set($service->getName(), $service->getDefinition(), $service->isShared());

                // Init myService again.
                $this->di->get('myService');