Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129641 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 A693E1A00BC for ; Wed, 17 Dec 2025 19:09:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1765998581; bh=Bzoi5NdWeIRsQvoW7aEUYiQ5ml5f7EeKIKVCcmz/3yA=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=ORvGGLzv5OkrSHTT6R/X/iG+APMAmlc01dkWH654y/wMF1owVfkXMz8tuJ6zgp2qX cRoaJCBOeBwQgiSbakCUi3P52CvKRcMrzh35HqvSDy5H1KSm+kO4nY6MY9J6yjcIAg CiGyhdNRrfhnQFXlZZJSvSsBD0nT7ZCuZfwf0puAaSXTxnSmskSp3veQlaRVyFYJ9N 7C/sahvRmbdkQ8zS0mbIBB8BI5k8JVi42eCn9/aV3OnUvWyJUt6Rfo/4otZM6tG+Na QT0soHGYegXnO2zi3V5w30Lxp7UOuLsMPiH/XB7g2jO0vRxMA00OqalZj8DUE8N9yG u/y46N+c0cBHQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 1107C180341 for ; Wed, 17 Dec 2025 19:09:40 +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.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from chrono.xqk7.com (chrono.xqk7.com [176.9.45.72]) (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 ; Wed, 17 Dec 2025 19:09:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bastelstu.be; s=mail20171119; t=1765998573; bh=BYs6WPCfUGJBofSXE1pmFHxt+loaCqPemkq+mop86HE=; h=MIME-Version:Date:From:To:Cc:Subject:In-Reply-To:References: Message-ID:Content-Type:from:to:cc:subject:message-id; b=XV0kqZ+GCkZGQCEASXAEYBX7MnJ1Ry/mEILwfLnJTlPEP4Ugk28Nc+9T0D3Zj3Sa0 Vl6McYb4/Xr6hTKAeQvKorN1tOvAjxSX+qvKlPorBaIewm1R0VPWm9YsLhg6kBpUqF ObEzWkcjgHvAt/0s8yq40cw07Lxd7DLPnm7Y1kgOwEaGjMSDHCfIpH2Czkfpp3733k aNS/1ftuoK0puzmrS2C87DG/a9fZyUqIArR4HrWeCeXza40WN2vsR2gvbl0TqiXHPu BDcgefPPsn6Uz7iKSxZuC538qmv0WtLCBGk8XSTFT8EqXtSDJHsravgWL8ibRibbhS wMU/8Ow4MsSTA== Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 Date: Wed, 17 Dec 2025 20:09:32 +0100 To: "Rowan Tommins [IMSoP]" Cc: internals@lists.php.net Subject: Re: [PHP-DEV] [RFC][Discussion] use construct (Block Scoping) In-Reply-To: <21f12343-b212-456f-93b3-079810d3d76d@rwec.co.uk> References: <1F3473C7-5D83-48D3-964E-A63D6F44D21E@rwec.co.uk> <4998b4c6-0474-4f0c-b63a-9909b8acfa96@bastelstu.be> <018421f64342a0d960589b4c8eea5cc5@bastelstu.be> <84b9dc16-3eb3-4283-b015-3af29fc0e55d@rwec.co.uk> <590fa655-d170-43f2-984c-d0a5ff6c30e4@bastelstu.be> <7c623161-cde3-4fc0-944c-ddfc2785c845@rwec.co.uk> <21f12343-b212-456f-93b3-079810d3d76d@rwec.co.uk> Message-ID: <7da06511e359f73410032a03757dfbe4@bastelstu.be> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit From: tim@bastelstu.be (=?UTF-8?Q?Tim_D=C3=BCsterhus?=) Hi Am 2025-12-11 23:21, schrieb Rowan Tommins [IMSoP]: >> That sentence you quoted was specifically in the context of the >> initial paragraph of that section, contrasting PHP - where block >> scoping is expected to be used comparatively sparingly - against >> languages where variable declarations are a more “bread and butter” >> part of the development process, because formally / explicitly >> declaring variables is a necessity for one reason or another. > > > I don't think that changes anything I said in my previous reply: as > soon as you declare a variable half-way through a block, there is an > ambiguity about its range of visibility. Having more variable > declarations makes that *more* likely to come up, not *less*, so I'm > not sure why you think it "avoids" the problem. The difference I'm seeing is that for languages where variable declarations (and block scoping) are a core part of the language, the scoping rules are “moulding” (if that word makes sense here) how code in that language is written and how folks reason about the code. This is different for a language where block scoping is added after-the-fact and remains an optional part of the language. > There's also an assumption that if PHP added block scoping, it would > only rarely be used. We have no way to know, but I'm not sure that's > true. I can easily imagine code styles adding a rule that all local > variables be declared at an appropriate level. I can also imagine new > users coming from other languages - particularly JS - adding "let" out > of habit, even if seasoned PHP coders wouldn't. From my experience, a majority of functions in modern code bases are reasonably short and single-purpose where intermediate variables are meant to live for the remainder of the function scope. And of course with additions such as the pipe operator, the number of temporaries will likely also go down further. From my own PHP code, I would guess block scoping to be useful for less than 10% of functions. For the ones where it would be useful, it would be very useful, though, since those are the functions that are on the more complex end of things. >> I feel that the C99 requirements and syntax would still have more >> ambiguity compared to the proposed `let()` syntax in cases like this: >> >>     { >>         let $foo = bar($baz); // What is $baz referring to? >> Particularly if it is a by-reference out parameter. >> >>         let $baz = 1; >>     } > > Probably the simplest solution is to re-use our existing definition of > "constant expression". In fact, we already have variable declarations > using that rule: > > function foo() { >     static $a = 1; // OK >     static $b = $a; // Fatal error: Constant expression contains > invalid operations > } Morgan already correctly noted that `static` supports arbitrary expressions nowadays. I would like to add that supporting arbitrary expressions within the initializer is also something we expect from block scoping to avoid boilerplate, since most if we don't store a dynamically computed value in a variable, we might as well use a constant or hardcode the value.ö >> As an example, is a goto jump label a statement? >> >>     { >>         let $foo = 1; >>  label: >>         let $bar = $foo++; >>         goto label; >>     } > > PHP already limits where "goto" can jump to; I don't know how that's > implemented, but I don't think we need to get into philosophical > definitions to say "you can't jump into the middle of a declaration > list". Another, perhaps better, example that is not handled well by any C-derived language that we are aware of is block scoping in combination with `switch()`: switch ($var) { let $tmp; case "foo": let $tmp2; break; case "bar": case "baz": let $tmp2; let $tmp3; break; } Which of the `$tmp`s is placed at the “start of a block”? What is the end of the block for each of them? Is it legal for `$tmp2` to be declared in two locations? > Or, we could just bite the bullet and answer the "which way does it > resolve" question, as loads of other languages have already done. Other languages have other ecosystems and other user expectations. PHP has extensive “scope introspection” functionality by means of `extract()`, `compact()`, `get_defined_vars()` and variable variables. Folks are used to being able to access arbitrary variables (it's just a Warning, not an Error to access undefined variables) and there's also constructs like `isset()` that can act on plain old local-scope variables. Adding semantics like the “temporal dead zone” from JavaScript that you suggested in the other thread would mean that we would need to have entirely new semantics and interactions with various existing language features that folks already know, adding to the complexity of the language. The RFC, as currently proposed, avoids all that by preserving all the existing semantics about “variable existence” and just adding the “backup and restore old value” semantics that are known from other languages and reasonably intuitive to understand even when not intimately familiar with block scoping. >>     let ($user = $repository->find(1)) if ($user !== null) { } > > > Skimming down a piece of code, I can spot where code is being run > conditionally without reading the condition itself: For me this works, because the `let()` is preparing me that “this code is doing user processing” and the `if()` is just an “implementation detail” / “means to an end” of that. By the block scoping semantics I know that when I read the closing brace, the user processing is finished. The function is a

, the user processing is a

and the `if()` is a

if that analogy makes sense. If I just want to get an overview over the function, I only care about the

headings. > Maybe it's also because I've dabbled in Perl, which has post-fix > conditions, so a very similar line would have a very different meaning: I understand that some languages have postfix conditions, but being able to place an `if()` after another control structure is not a new thing. The same would apply to: foreach ($users as $user) if ($user->isAdmin()) { echo "User is admin"; } which is already valid PHP. > In terms of making it less of a special case, some languages have a "," > operator which lets you glue any two expressions together and get the > right-hand result. > > In Perl, you can write this: > > ``` > my $a = 'outer', $b = 'whatever'; > if ( my $a='inner', $b == 'whatever' ) { >     say $a; // 'inner' > } > say $a; // 'outer' > ``` > > This gives the desired scope for $a, but the if statement is still just > accepting a single expression. The comma would leave ambiguity in cases like `if (let $repository = $container->getRepository(), $user = $repository->find(1))`. Are both $repository and $user block-scoped or only $repository of them? Assignments are valid expressions in a condition. That's probably why C++ uses the `;` as a delimiter there. > JavaScript has the same operator, but apparently doesn't allow "let" in > an expression, so you can write: > > if ( a="inner", b=="whatever" ) { } > > but can't use it to declare a local version of "a". > > > I haven't thought through exactly how to apply that to PHP, but it > might give us an option for "both and": a concise and reusable syntax > for the if use case, and a separate syntax for cases like the closure > example I gave earlier: https://externals.io/message/129059#129075 Adding “inline” support for other control structures certainly is something that can be done as future scope. But we believe the “top of the block” semantics are important for block scoping to work well in PHP due to its unique semantics and 30y history. Best regards Tim Düsterhus PS: With that both Seifeddine and I are going to be enjoying our end-of-the-year vacations and are expected to be back on the list next year.