finally runs even when you return from try

The obvious reading of finally is “runs after the try block finishes”. The useful reading is stronger: it runs on the way out no matter how you leave, including a return inside try and an exception nothing catches.

function withLock(callable $work)
{
    $this->lock->acquire();

    try {
        return $work();          // finally still runs
    } finally {
        $this->lock->release();
    }
}

Note that there is no catch here at all, and that is deliberate — the lock must be released whether the work succeeded or threw, but this function has no business deciding what to do about the exception. It is the closest PHP gets to a scope guard.