This problem was found during testing, it is unlikely to cause problems in production environments.
The problem is quite complex and the code below has been simplified to illustrate the problem. Due to this simplification it might seem to not make sense (why not use ExistValidator if we're checking primary keys of another model); please refrain from making suggestions to change my code, I am aware of how to circumvent the problem.
A number of preconditions exist, here's what I used to reliably reproduce the issue:
class A extends ActiveRecord {
public function getB() {
return $this->hasMany(B::class, ['a_id' => 'id'])->indexBy('id');
}
public function rules() {
return [
[['most_important_a_id'], RangeValidator::class, 'range' => $this->getB()->each()
]
}
}
The test code looks like this:
public function testValidate()
{
$a = new A();
$a->save();
$b = new B();
$b->a_id = $a->id;
$b->save();
// Manually getting the validator so it is clear that the issue is not in the `save()` logic.
// Since there is only 1 validator is defined we know it is the `RangeValidator`.
$a->getValidators()[0]->validate($b->id);
// Unset our references to $a and $b
unset($a, $b);
gc_collect_cycles();
}
After each execution of the above test no references should exist to the PDO object other than the one in the database component.
There is a cycle:
RangeValidator::$range -> BatchQueryResult::$query -> ActiveQuery::$primaryModel -> A::$_validators -> RangeValidator
This cycle causes the BatchQueryResult to never get freed.
Since it stores a reference to DataReader which in turn has a reference to a PDOStatement the underlying PDO connection is never freed.
This cycle is not caught by the php garbage collector.
Theoretically the approach used by the PHP to garbage collect cycles should successfully clear this cycle.
$_validators public and tried manually unsetting it. This breaks the cycle and allows garbage collection to work.each() in ActiveQuery, have it clone the query and set $primaryModel to null. This breaks the cycle and allows garbage collection to work.If you have any questions or if I've not fully explained everything in a clear manner please let me know before drawing conclusions!
| Q | A
| ---------------- | ---
| Yii version | dev-master
| PHP version | 7.2.10
@SamMousa do you think we should create a workaround for it?
Since it has already been patched and the issue is most likely to occur during running of tests I'd say no.
If we can create a workaround that increases code quality I'd be in favor though
Most helpful comment
https://bugs.php.net/bug.php?id=76946