The return expression is evaluated before finally runs, and the value is not handed to the caller until afterwards — so a finally that returns wins, silently.
function f(): int
{
try {
throw new RuntimeException('lost');
} finally {
return 2; // the exception is gone. no trace, no log.
}
}
// what finally is for:
try {
$lock->acquire();
return $this->work();
} finally {
$lock->release();
}
A return inside finally discards both the original return value and any exception in flight, which turns a crash into a silently wrong result. Several static analysers flag it and most coding standards forbid it, for exactly this reason. Keeping the block to cleanup only — release, close, unlock — is what makes it predictable, and anything else in there is worth a second look.