Re: [RFC] Context Managers

From: Date: Sat, 08 Nov 2025 19:54:28 +0000
Subject: Re: [RFC] Context Managers
References: 1 2  Groups: php.internals 
Request: Send a blank email to internals+get-129152@lists.php.net to get a copy of this message
On 05/11/2025 22:38, Bob Weinand wrote:
The choice of adding the exception to the exitContext() is interesting, but also very opinionated: - It means, that the only way to abort, in non-exceptional cases, is to throw yourself an exception. And put a try/catch around the with() {} block. Or manually use enterContext() & exitContext() - with a fake "new Exception" essentially. - Maybe you want to hold a transaction, but just ensure that everything gets executed together (i.e. atomicity), but not care about whether everything actually went through (i.e. not force a rollback on exception). You'll now have to catch the exception, store it to a variable, use break and check for the exception after the with block. Or, yes, manually using enterContext() and exitContext().
The Context Manager is *given knowledge of* the exception, but it's not obliged to change its behaviour based on that knowledge. I don't think that makes the interface opinionated, it makes it extremely flexible. It means you *can* write this, which is impossible in a destructor: function exitContext(?Throwable $exception) {
    if ( $exception === null ) {
        $this->commit();
    } else {
        $this->rollback();
    }
} But you could also write any of these, which are exactly the same as they would be in __destruct(): // Rollback unless explicitly committed function exitContext(?Throwable $exception) {
    if ( ! $this->isCommitted ) {
        $this->rollback();
    }
} // Expect explicit commit or rollback, but roll back as a safety net function exitContext(?Throwable $exception) {
    if ( ! $this->isCommitted && ! $this->isRolledBack ) {
        $this->logger->warn('Transaction went out of scope without explicit rollback, rolling back now.');
        $this->rollback();
    }
} // User can choose at any time which action will be taken on destruct / exit function exitContext(?Throwable $exception) {
    if ( $this->shouldCommitOnExit ) {
        $this->commit();
    } else {
        $this->rollback();
    }
} You could also combine different approaches, using the exception as an extra signal only if the user hasn't chosen explicitly: function exitContext(?Throwable $exception) {
    if ($this->isCommitted || $this->isRolledBack) {
        return;
    }
    if ( $exception === null ) {
        $this->logger->debug('Implicit commit - consider calling commit() for clearer code.');
        $this->commit();
    } else {
        $this->logger->debug('Implicit rollback - consider calling rollback() for clearer code.');
        $this->rollback();
    }
} -- Rowan Tommins [IMSoP]

« previous php.internals (#129152) next »