You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
bug #11422 [DependencyInjection] Self-referenced 'service_container' service breaks garbage collection (sun)
This PR was submitted for the master branch but it was merged into the 2.3 branch instead (closes#11422).
Discussion
----------
[DependencyInjection] Self-referenced 'service_container' service breaks garbage collection
| Q | A
| ------------- | ---
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Needs merge to | 2.4
| Fixed tickets | —
| License | MIT
| Doc PR | —
#### Problem
1. `Container::__construct()` sets the `service_container` service ID to a self-reference of `$this`.
1. This breaks PHP garbage collection (as well as `clone`).
#### Background
* As explained in [Reference Counting Basics](http://php.net/manual/en/features.gc.refcounting-basics.php), PHP is not able to destruct and garbage collect variables that contain a reference to itself, because the `refcount` is always > 0.
#### Example Use-Case
A precompiled container that is cloned for multiple tests (because `::compile()` can be slow).
```php
$precompiled = new ContainerBuilder();
// ...
$precompiled->compile();
foreach ($precompiled->getServiceIds() as $id) {
if ($precompiled->initialized($id)) {
$precompiled->set($id, null);
}
}
$precompiled_clean = clone $precompiled;
$precompiled = NULL;
// ...
foreach ($tests as $test) {
$container = clone $precompiled_clean;
// Required without this PR:
$container->set('service_container', $container);
// ...
}
```
* **Actual result [master]:**
All references to the original `$precompiled` container are not destructed and cleaned up, because `$precompiled` still references itself, which can cause terribly hard to debug defects due to stale reference pointers in memory (especially with `ContainerAware` services, or things like e.g. PDO connections), as well as high memory consumption, etc.
* **Expected result [PR]:**
No references to `$precompiled` are left; everything is shut down, cleaned up, and garbage-collected upon `$precompiled = NULL;`
#### Proposed solution
1. Hard-code a special behavior for the service ID `service_container`.
1. Remove the self-reference.
1. For now (until next major release), ensure that all `Container` methods work identically to before — instead of throwing exceptions when e.g. trying to set `service_container` to something that isn't `$this`… (which is technically possible right now, but the operation doesn't remotely make sense)
#### API changes
Per (3) above, this PR is backwards-compatible. However, for the sake of full disclosure, users may experience the following super-micro changes compared to HEAD:
1. The following method no longer exists in a dumped Container, because it is no longer a registered service:
```php
protected function getServiceContainerService()
{
throw new RuntimeException('You have requested a synthetic service ("service_container"). The DIC does not know how to construct this service.');
}
```
But given the exception, you weren't able to call it anyway.
1. The order/position of `service_container` in the array returned by `Container::getServiceIds()` may be different.
But that array has no particular order to begin with.
(Only the component's own unit tests are expecting an identical result at times, which is why I added the hard-coded ID via `$id[]` last, instead of initializing it first with `$id` already.)
1. `$container->set('service_container', $not_the_container);` no longer works.
Commits
-------
440322e Fixed self-reference in 'service_container' service breaks garbage collection (and clone).
0 commit comments