A finally that returns discards the exception silently

A return inside finally overrides both the pending return value and any in-flight exception, with no diagnostic.

function f(): string
{
    try {
        throw new RuntimeException('boom');
    } finally {
        return 'ok';        // the exception is GONE
    }
}

f();   // 'ok'. no exception anywhere.

// the same applies to `break`, `continue` and `goto`
// jumping out of a finally block.

This is almost always a mistake and there is no legitimate use that could not be written another way, which is why every static analyser flags it and PHP still permits it. The version that reaches production is subtler: a finally containing a cleanup call that itself throws, which replaces the original exception with a less informative one from the cleanup path. Wrapping the cleanup in its own try is the defence.