On 30/06/2022 11:29, Arnaud Le Blanc wrote:
I feel that the RAII pattern aka SBRM / Scope-Bound Resource Management is not
relevant in PHP context, and I don't believe that it's commonly used in PHP or
in garbage collected language.
I've used a simple version of the pattern effectively to implement
transactions: if the Transaction object goes out of scope without being
explicitly committed or rolled back, it assumes the program hit an
unexpected error condition and rolls back.
One way the lifetime of a value could be extended is via a reference cycle.
These are easy to introduce and difficult to prevent or observe (e.g. in a
test or in an assertion).
I would expect reference cycles to be pretty rare in most code,
particularly when you're dealing with a value with a short lifetime as
is involved in most RAII scenarios.
The worst-case release of the cycle can also be made predictable by
running gc_collect_cycles()
An other way would be by referencing the value
somewhere else. You can not guarantee that the lifetime of a value is
unaffected after passing it to a function.
Surely the only way to avoid that is with something like Rust's "borrow
checker"? Otherwise, any function that has a reference to something can
extend the lifetime of that reference by storing it inside some other
structure with a longer lifetime. Manually freeing the underlying
resource then just leads to a "use after free" error.
Another factor that makes RAII un-viable in PHP is that the order of the
destructor calls is unspecified. Currently, if multiple objects go out of
scope at the same time, they happen to be called in a FIFO order, which is not
what is needed when using the RAII pattern [0][1].
I can imagine this would be a problem for some advanced uses of the
pattern, but for a simple "acquire lock, release on scope exit" or
"start transaction, rollback on unexpected scope exit", it's generally
not relevant.
Other languages typically have other ways to explicitly manage the lifetime of
resources. Go has `defer()` [2]. Python has context managers / `with` [3], C#
has `using` [4]. `with` and `using` can be implemented in userland in PHP.
My understanding is that C#'s "using" is indeed about deterministic
destruction, but Pythons's "with" is a more powerful
inversion-of-control mechanism. I would actually really love to have
some version of Python's context managers in PHP, and think it would be
a better alternative to closures in a lot of cases.
For instance, a motivation cited in support of auto-capture is something
like this:
function doSomething($a, $b, $c) {
return $db->doInTransaction(fn() {
// use $a, $b, and $c
// roll back on exception, commit otherwise
return $theActualResult;
}
}
But this is actually quite a "heavy" implementation: we create a
Closure, capture values, enter a new stack frame, and have two return
statements, just to wrap the code in try...catch...finally boilerplate.
The equivalent with a context manager would look something like this:
function doSomething($a, $b, $c) {
with ( $db->startTransaction() as $transaction ) {
// use $a, $b, and $c
// roll back on exception, commit otherwise
return $theActualResult;
}
}
Here, the with statement doesn't create a new stack frame, it just
triggers a series of callbacks for the boilerplate at the start and end
of the block. No variables need to be captured, because they are all
still available, and "return" returns from the doSomething() function,
not the transaction wrapper.
The explanation of how Python's implementation works and why is an
interesting read: https://peps.python.org/pep-0343/
Regards,
--
Rowan Tommins
[IMSoP]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php