Hello!
```php
function addStudentLessons(DatabaseTransaction $transaction) {
try {
$transaction->execute(...);
} catch(\Exception $e) {
Logger::log($e);
throw $e;
}
}
// Application Code:
function do_work(DatabasePool $pool): void {
using (
$connection = $pool->getConnection(),
) {
using ($transaction = $connection->beingTransaction()) {
$transaction->execute('...');
sleep(10); // more work.
$transaction->execute('...');
}
sleep(10); // more work
}
sleep(10); // more work
} // <== broken!
```
Logger::log($e); <== reason!
In this example, the `Logger` service holds the exception `$e`,
which completely breaks the code because the transaction will no
longer complete correctly, and it’s unclear when the resources will be
released.
This is even more true for stateful applications, where the Logger
processes stored exceptions later rather than immediately.
Note that I didn’t even use circular references. I’m sure that 90% of
PHP developers who see this code won’t even understand what the
problem is.
And in practice, it will work for about 50% of them and fail for the other 50%.
At the same time, as a programmer, I didn’t do anything particularly
wrong or make any obvious mistake in this code. It’s just that the
logging service holds the exception object for a while.
RC-managed objects were designed to create code where the destruction
time of an object cannot be determined statically (only at runtime).
Automatic memory management is not a primary feature of RC objects,
since it can be implemented without reference counting.
However, the code in the example pursues the opposite goals: it must
guarantee the exact moment a function is called. In other words, the
RAII concept is not suitable here.
And this situation is typical for PHP stateful applications, where
resource control is not managed through RAII.
---
Best Regards, Ed