Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Implement resettable containers #15185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ CHANGELOG
* allowed specifying a directory to recursively load all configuration files it contains
* deprecated the concept of scopes
* added `Definition::setShared()` and `Definition::isShared()`
* added ResettableContainerInterface to be able to reset the container to release memory on shutdown

2.7.0
-----
Expand Down
15 changes: 14 additions & 1 deletion src/Symfony/Component/DependencyInjection/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
Expand Down Expand Up @@ -60,7 +61,7 @@
*
* @api
*/
class Container implements IntrospectableContainerInterface
class Container implements IntrospectableContainerInterface, ResettableContainerInterface
{
/**
* @var ParameterBagInterface
Expand Down Expand Up @@ -375,6 +376,18 @@ public function initialized($id)
return isset($this->services[$id]) || array_key_exists($id, $this->services);
}

/**
* {@inheritdoc}
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely document the side effects of calling this method, not only the intended effects (memory) but also the side effects (a shared service will be re-created if get() is called).

public function reset()
{
if (!empty($this->scopedServices)) {
throw new LogicException('Resetting the container is not allowed when a scope is active.');
}

$this->services = array();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about aliases and some other properties? I know that this does not help with your goal, but this implementation looks like a hack to me. Or you should call it clearSharedServices() or something to really show the intent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$this->aliases and $this->methodMap are part of the container definition, not of the runtime usage. they are just maps of strings and so does not impact the GC of containers. This is similar for scopes and scopeChildren.
And scopedServices and scopeStack will be empty here as I enforce being outside of any active scope.

Clearing it currently sets the container in the same state than just after instantiating it: if you continue to call get on it, it will just reinstantiate services (this would be equivalent to entering in a new container scope actually, which also make sense for the condition on active scopes).
Resetting all internal properties would make the code likely to break badly if someone tries to continue to use the object after clearing it.

However, the name of the method could indeed be changed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do understand that, I think the problem is more about the method name. reset() could be better?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I even thought about it. I will update it (and rename the interface too)

}

/**
* Gets all service ids.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection;

/**
* ResettableContainerInterface defines additional resetting functionality
* for containers, allowing to release shared services when the container is
* not needed anymore.
*
* @author Christophe Coevoet <[email protected]>
*/
interface ResettableContainerInterface extends ContainerInterface
{
/**
* Resets shared services from the container.
*
* The container is not intended to be used again after being reset in a normal workflow. This method is
* meant as a way to release references for ref-counting.
* A subsequent call to ContainerInterface::get will recreate a new instance of the shared service.
*/
public function reset();
}
43 changes: 43 additions & 0 deletions src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,49 @@ public function testInitialized()
$this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized');
}

public function testReset()
{
$c = new Container();
$c->set('bar', new \stdClass());

$c->reset();

$this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE));
}

/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage Resetting the container is not allowed when a scope is active.
* @group legacy
*/
public function testCannotResetInActiveScope()
{
$c = new Container();
$c->addScope(new Scope('foo'));
$c->set('bar', new \stdClass());

$c->enterScope('foo');

$c->reset();
}

/**
* @group legacy
*/
public function testResetAfterLeavingScope()
{
$c = new Container();
$c->addScope(new Scope('foo'));
$c->set('bar', new \stdClass());

$c->enterScope('foo');
$c->leaveScope('foo');

$c->reset();

$this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE));
}

/**
* @group legacy
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/HttpKernel/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\ResettableContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
Expand Down Expand Up @@ -180,6 +181,10 @@ public function shutdown()
$bundle->setContainer(null);
}

if ($this->container instanceof ResettableContainerInterface) {
$this->container->reset();
}

$this->container = null;
}

Expand Down