Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129163 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id 765191A00BC for ; Sun, 9 Nov 2025 15:02:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1762700546; bh=ykuTw9ctN3LDMfe+935Z+Sql4kFvAX4TGHjzwg7iShA=; h=Date:From:To:In-Reply-To:References:Subject:From; b=CJ5/pAEqfLpb/KIoHWK/JZdwG7xC8UozrUKjEyQYbiBxe0Fr6+4BhGKABIcUvCJjK kUiabGC/wnpixbW34wttmZ11B2LWo0QE4D0RwWJM1bHQveuSNiHAIxhKnrAD2lYXKQ hfnoFbZPr7pK9w94Xo/CpoHyTViamoZ9grkZvb5PbskmPFO/ZgLcufau0OtDTtlO2d ZjZZeNqwv79yL15CM5i6MseGp2YS7H5vGGhJ1KWV41A9P9WiodLcTNGFbWIXA1oJGC wJZwczw4HinF4X6L9YobzWHFFPKbf5WoXKEfV3i1p6Xnwe+QFkwjjhyrdmiEVBehTi geGeFDn7mh9mQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 20DD5180041 for ; Sun, 9 Nov 2025 15:02:25 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_40,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-b5-smtp.messagingengine.com (fhigh-b5-smtp.messagingengine.com [202.12.124.156]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 9 Nov 2025 15:02:24 +0000 (UTC) Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailfhigh.stl.internal (Postfix) with ESMTP id 4C9C47A00D2 for ; Sun, 9 Nov 2025 10:02:19 -0500 (EST) Received: from phl-imap-02 ([10.202.2.81]) by phl-compute-04.internal (MEProxy); Sun, 09 Nov 2025 10:02:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to; s=fm3; t=1762700539; x=1762786939; bh=yS00gr6zeFoBG5CbFmPZF 3u/aCbrxos5tqDSeFyQ5j0=; b=F6Qu0rLuYF2g2yylzY5EOtMHIhR4LQcbxJymY G5H+XMoYrK2fs3xn1jcQUS/+Nek9uTdkcAD+16fqTagzAUat+CRND7rieqG7buf9 XdJnpvwmkzM1M6OgbRps4sKCD95MY2A2Tv38mMVGLJeWA8smGhnBuNYe0kDzM1eG 26Q9PD/9UVhMggHQRxQPGiRNAWC40JYUCcPz5CCwCHESKJz7F7/+I7/TNMqMyltY VuFlyjmwMDwKA8iTgWwA9qJhPDYUNw/mDNd6n599ued2JFtrf6w1LXgITPgWjaHQ 5Bg6wIwJ+aYyON6a0BH9zWxQejWA+Uc7xFXHD7zRVTXZwLkHg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1762700539; x=1762786939; bh=y S00gr6zeFoBG5CbFmPZF3u/aCbrxos5tqDSeFyQ5j0=; b=WyiUkwhGtJNBkRS9S NabEniVE6Wy2PvWBEE3IPgkDpn1qhsXH1aTheEwQLbwjhn7CG0cQ9j00h3icGDAP 730eedjHbanyBlBwCLZqfbg3P5hiy6fKc6J8dr3Alpe8svM4lBRTYbZWES85pAQH TeQoUvQSBWfXynocyFUFi0Ke7vL5FYy3u6Vzavr+iQPPPHM1qC7JHWkMWMy6alXO HnfzaT5IKVff2LIevuvlD4Ly899CIcgN7+/FiI7aOL15luC9e6XPyy6rO1OItqwt zxjLp3SRiHvYp4RH7+84E16TRHR3qVjkfjjYCzpB60IEBmpOchuj2MwZgABGrYb+ jdaEw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduleehjeefucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvffkjghfufgtgfesthhqredtredtjeenucfhrhhomhepfdfnrghrrhih ucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmqe enucggtffrrghtthgvrhhnpeffieeivdfhvdeguddttdegteeiueegvefhteehfeeffeet udeitdehtdegjeeuieenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrih hlfhhrohhmpehlrghrrhihsehgrghrfhhivghlughtvggthhdrtghomhdpnhgspghrtghp thhtohepuddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepihhnthgvrhhnrghlsh eslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id B855B700063; Sun, 9 Nov 2025 10:02:18 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: ALvh3TDXSVwZ Date: Sun, 09 Nov 2025 09:01:58 -0600 To: "php internals" Message-ID: In-Reply-To: <864235f2-41ea-4ea5-ae45-9fdb6b6c5a6c@rwec.co.uk> References: <864235f2-41ea-4ea5-ae45-9fdb6b6c5a6c@rwec.co.uk> Subject: Re: [PHP-DEV] [RFC] Context Managers Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: larry@garfieldtech.com ("Larry Garfield") On Sat, Nov 8, 2025, at 1:54 PM, Rowan Tommins [IMSoP] wrote: > On 05/11/2025 22:38, Bob Weinand wrote: >> The choice of adding the exception to the exitContext() is=20 >> interesting, but also very opinionated: >> >> - It means, that the only way to abort, in non-exceptional cases, is=20 >> to throw yourself an exception. And put a try/catch around the with()=20 >> {} block. Or manually use enterContext() & exitContext() - with a fak= e=20 >> "new Exception" essentially. >> - Maybe you want to hold a transaction, but just ensure that=20 >> everything gets executed together (i.e. atomicity), but not care abou= t=20 >> whether everything actually went through (i.e. not force a rollback o= n=20 >> exception). You'll now have to catch the exception, store it to a=20 >> variable, use break and check for the exception after the with block.=20 >> Or, yes, manually using enterContext() and exitContext().=20 > > > The Context Manager is *given knowledge of* the exception, but it's no= t=20 > obliged to change its behaviour based on that knowledge. I don't think=20 > 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) { > =C2=A0 =C2=A0 if ( $exception =3D=3D=3D null ) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->commit(); > =C2=A0 =C2=A0 } else { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->rollback(); > =C2=A0 =C2=A0 } > } > > > But you could also write any of these, which are exactly the same as=20 > they would be in __destruct(): > > // Rollback unless explicitly committed > function exitContext(?Throwable $exception) { > =C2=A0 =C2=A0 if ( ! $this->isCommitted ) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->rollback(); > =C2=A0 =C2=A0 } > } > > // Expect explicit commit or rollback, but roll back as a safety net > function exitContext(?Throwable $exception) { > =C2=A0 =C2=A0 if ( ! $this->isCommitted && ! $this->isRolledBack ) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->logger->warn('Transaction went out= of scope without=20 > explicit rollback, rolling back now.'); > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->rollback(); > =C2=A0 =C2=A0 } > } > > // User can choose at any time which action will be taken on destruct = / exit > function exitContext(?Throwable $exception) { > =C2=A0 =C2=A0 if ( $this->shouldCommitOnExit ) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->commit(); > =C2=A0 =C2=A0 } else { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->rollback(); > =C2=A0 =C2=A0 } > } > > > You could also combine different approaches, using the exception as an=20 > extra signal only if the user hasn't chosen explicitly: > > function exitContext(?Throwable $exception) { > =C2=A0 =C2=A0 if ($this->isCommitted || $this->isRolledBack) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 return; > =C2=A0 =C2=A0 } > =C2=A0 =C2=A0 if ( $exception =3D=3D=3D null ) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->logger->debug('Implicit commit - c= onsider calling=20 > commit() for clearer code.'); > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->commit(); > =C2=A0 =C2=A0 } else { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->logger->debug('Implicit rollback -= consider calling=20 > rollback() for clearer code.'); > =C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->rollback(); > =C2=A0 =C2=A0 } > } > > > --=20 > Rowan Tommins > [IMSoP] Though one point to note here, $this->isCommitted on the context MANAGER= is not the same as isCommitted on the context VARIABLE. So in the abov= e examples you would have to either return $this from enterContext() (wh= ich is fine), or save a reference to the context variable in the manager= and then check $this->txn->isCommitted (which is also fine). Which you choose is mostly an implementation detail, and it's fine eithe= r way; I just want to emphasize that a lot of the flexibility of context= managers comes from the separation of the context manager from the vari= able. A context manager is not just an auto-unsetter, though that is part of w= hat it does. It is more properly a way to abstract out and package up s= etup/teardown lifecycle management, which can differentiate between a su= ccess or failure case. (At least as much as PHP itself is able to right= now.) That has a wide variety of use cases, only a few of which a simp= le destructor could handle. =20 --Larry Garfield