Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127263 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 886E31A00BC for ; Thu, 1 May 2025 13:56:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1746107651; bh=BoS2HKXmc8ADNkyaNm1k74jq/DVfK6/2pob+7vSgTao=; h=Date:From:To:In-Reply-To:References:Subject:From; b=BPi3Yesy2zbAAdjObGiWsJ3W4rpWNVJnv1ipYd9IBt0x1mZ0v6wMLoq+VGZpB3nfP Rd0nPsAUFhBok+Q6oaYpeU+QnlVzGF+UBipaV9Vp3xsZTzCU0+e3bazVHnXi37kqK7 PGmZ6FQnVZl/9KYpysdnq6a1rP/PwIgdHfyiGAfV+4Kx+38ge4grJ6E1IytVQOXkSk ZJKATDUdRSzpwZj5kLN8TuZTwXgXxpVfDs5EN00qliGlp0CLzmBnM1n0dd54EXmkdS iLqBhjN9ru/lQZyvEzDtM/Cbg56T+eFRwBvngrF6SCh2n9QECl1jDmObAejp1uvOMS rCkYeMSKDO6Wg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 6C73E18007C for ; Thu, 1 May 2025 13:54:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_05,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fhigh-b7-smtp.messagingengine.com (fhigh-b7-smtp.messagingengine.com [202.12.124.158]) (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 ; Thu, 1 May 2025 13:54:10 +0000 (UTC) Received: from phl-compute-10.internal (phl-compute-10.phl.internal [10.202.2.50]) by mailfhigh.stl.internal (Postfix) with ESMTP id 9D60325401EB for ; Thu, 1 May 2025 09:56:25 -0400 (EDT) Received: from phl-imap-06 ([10.202.2.83]) by phl-compute-10.internal (MEProxy); Thu, 01 May 2025 09:56:25 -0400 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=1746107785; x=1746194185; bh=kLpfZ3iwc98RdvaZVLBXh pe5MEvuiOH+Ud4E5NCrM2M=; b=cr7YxroXogjSET5X0xML6irLlQkf97nA8a4AM /8os1rBcZ6D0NAhjcc1J8XWFNaHWgqwPJwtDYfKIgWoLQxX0v6Hgc6qc0fGtUMRD lf7adxDeyi5Gev6SB8JDF9txEaj0vqFvALmqm9audkR5GtTjjIQJFdGDSLTuMcBC TJFuz+RYkfm/eR0C7JmE6J2a6T5p/lNdFEAW7H2Z/DJsGKBA9Hwij/uIQ3XbIkln tg03Rby8KcJkXY0tUJpanp7PcJFHoWLEDj2tWs21fAbC1V6IWMxGf6EaSZhO4hxj N00OyTyRY6RrfJqtD0sw3dJWV0Xp5Aznb2DenNtgALc0kFeaA== 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=1746107785; x=1746194185; bh=k LpfZ3iwc98RdvaZVLBXhpe5MEvuiOH+Ud4E5NCrM2M=; b=G+5rqCl325BrgDdUr aIMlX6AGW1kvWyOLnXWRMvVNr4AMOtKrmctb5UPMuKdE82t9Pb4xmeFb+06Zn8jU GTAm94u5Dwd8qsT69afFeaHLCT0Sr0FF9BUgcMv7uRpWf1LzaD/oB9ynAQjgKkfu GV/y0nE/JRYt5SJpwD9gzHOYkLbIrKpON7Rfr2SnuanLUfeDdDUR5QVARkoN5hb1 SWknKbhDwoUkQ0ejK4CaN9k85x6ykAAk+agWbx11qNBG5K5vT1kIaM0V78LXBsjv wE4rbzszlqZG1j0R0BLcIUCJPJwKVTzrshlnwsA3VqIvqixQXE2k7K7KtV/yvXBc 5gtgg== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvieeljeeiucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpih gvnhhtshculddquddttddmnecujfgurhepofggfffhvffkjghfufgtgfesthhqredtredt jeenucfhrhhomhepfdfnrghrrhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrh hfihgvlhguthgvtghhrdgtohhmqeenucggtffrrghtthgvrhhnpeffieeivdfhvdeguddt tdegteeiueegvefhteehfeeffeetudeitdehtdegjeeuieenucevlhhushhtvghrufhiii gvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehlrghrrhihsehgrghrfhhivghlught vggthhdrtghomhdpnhgspghrtghpthhtohepuddpmhhouggvpehsmhhtphhouhhtpdhrtg hpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 2E66129C0072; Thu, 1 May 2025 09:56:25 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: T7e0f24752af1ea38 Date: Thu, 01 May 2025 08:56:04 -0500 To: "php internals" Message-ID: <258b8500-af7d-4cd5-8b40-7a2f08d5feaf@app.fastmail.com> In-Reply-To: References: <39597a9c-6854-40c6-a529-32b2b178cb27@app.fastmail.com> <150fb4df-47a4-4ab3-8edb-13e446b122cc@app.fastmail.com> <8ba39427-bc2d-4c2a-90d8-648371e4f576@app.fastmail.com> Subject: Re: [PHP-DEV] Concept: Lightweight error channels Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: larry@garfieldtech.com ("Larry Garfield") On Thu, May 1, 2025, at 7:47 AM, Juris Evertovskis wrote: > On 2025-04-29 17:29, Matthew Weier O'Phinney wrote: > >> =20 >> * Exceptions should not be used for normal application logic flow. If= the "error" is recoverable and/or expected, use a different mechanism s= o you can use standard conditional branching. >> =20 >> As such, there are a lot of situations where I may not want to use ex= ceptions. Two common ones: >> =20 >> * Input validation. In most cases, _invalid input is expected_, and a= condition you will handle in your code. Exceptions are a really poor me= chanism for this. >> * "Not found" conditions, such as not finding a matching row in a dat= abase or a cache. Again, this is expected, and something you should hand= le via conditionals. > I don't want to make this into a quarrel, please consider this to be a=20 > genuine question =E2=80=94 I'm trying to understand the viewpoint behi= nd the=20 > need for such "failed result" channel. > > I'm considering this scenario: An update request comes into a=20 > controller and passes a superficial validation of field types. The=20 > 'troller invokes an action which in turn invokes a service or whatever=20 > the chain is. Somewhere along the call stack once all the data is=20 > loaded we realize that the request was invalid all along, e.g. the=20 > status can't be changed to X because that's not applicable for objects=20 > of kind B that have previously been in status Z. > > In such situations I have found (according to my experience) the=20 > following solution to be a good, robust and maintainable pattern: > > Once I find the request was invalid, I throw a ValidationException. No=20 > matter how deep in the stack I am. No matter that the callers don't=20 > know I might've thrown that. The exception will be caught and handled=20 > by some boundary layer (controller, middleware, error handler or=20 > whatever), formatted properly and returned to the user in a=20 > request-appropriate form. > > I currently have no urge to return an indication of invalidity manuall= y=20 > and pass it up the call stack layer by layer. Should I want that? In m= y=20 > experience such patterns (requiring each layer to do an `if` for the=20 > possible issue and return up the stack instead of continuing the=20 > execution) get very clumsy for complex actions. Or have I misunderstoo= d=20 > the usecase that you had in mind? > > BR, > Juris The key distinction is here: > Somewhere along the call stack once all the data is=20 > loaded we realize that the request was invalid all along combined with: > No matter that the callers don't=20 > know I might've thrown that. Addressing the second part first, unchecked exceptions means I have no i= dea at all if an exception is going to get thrown 30 calls down the stac= k from me. Literally any line in my function that calls anything could = be the last. Is my code ready for that? Can it handle that? Or do I n= eed to put a try-finally inside every function just in case? Admittedly in a garbage collected language that concern is vastly reduce= d, to the point most people don't think about that concern. But that's = not because it's gone away entirely. Are you writing a file and need to= write a terminal line to it before closing the handle? Are you in the = middle of a DB transaction that isn't using a closure wrapper for auto-c= losing (which PDO natively does not)? =20 Technically, if you're writing this code: $pdo->beginTransaction(); foreach ($something as $val) { $write =3D transform($val); $pdo->query('write $write here'); } $pdo->commit(); That's unsafe because transform() might throw, and if it does, the commi= t() line is never reached. So you really have to put it in a try-catch-= finally. (Usually people push that off to a wrapping closure these days= , but that introduces extra friction and you have to know to do it.) Or similarly,=20 $fp =3D fopen('out.csv', 'w'); fwrite($fp, "Header here"); foreach ($input as $data) { $line =3D transform($data); fputcsv($fp, $line); } fwrite($fp, "Footer here"); fclose($fp); If transform() throws on the 4th entry, you now have an incomplete file = written to disk. And *literally any function you could conceive of* cou= ld do this to you. The contract of every function is implicitly "and I = might also unwind the call stack 30 levels at any time and crash the pro= gram, cool?" You are correct that unchecked exceptions let you throw from way down in= the stack if something goes wrong later. Which brings us back to the f= irst point: If that's a problem, it's a code smell that you should be va= lidating your data sooner. This does naturally lead to a different arch= itectural approach. Error return channels are an "in the small" feature= . They're intended to make the contract between one function and anothe= r more robust. One can build a system-wide error pattern out of them, b= ut they're fundamentally an in-the-small feature. So in the example you list, I would offer: 1. Do more than "superficial validation" at the higher level. Validate = field types *and* that a user is authorized to write this value (for exa= mple). 2. As discussed, we do need some trivially easy way to explicitly defer = an error value back to our caller. That should be easy enough that it's= not a burden to do, but still explicit enough that both the person read= ing and writing the code know that an error is being propagated. This s= till requires research to determine what would work best for PHP. Wouldn't the heavy use of error return channels create additional fricti= on in some places and cause us to shift how we write code? Yes, grassho= pper, that is the point. :-) The language should help us write robust, = error-proof code, and the affordances and frictions should naturally nud= ge us in that direction. Just as explicit typing makes certain patterns= less comfortable and therefore we move away from them, and are better f= or it. --Larry Garfield