On 10/12/2025 14:25, Tim Düsterhus wrote:
You missed db-transaction/application/1b-raii-with-scope-block.php.
Let me begin with some more obvious fixes I found in this new iteration:
- In db-transaction/application/1c-raii-with-scope-declaration.php I
am noticing that the comment mentions “extra indent” which is not a
case for the 1b version either, due to the “single statement” variant
being used.
- file-handle/application/3a-ioc-current-closure.php is missing a
closing parens and semicolon in line 20.
- file-handle/application/3b-ioc-auto-capture.php is missing the same.
- Both also applies to the unguarded version and the file-object version.
- file-handle-unguarded/application/1b-raii-with-scope-block.php is
missing a closing parens in line 11.
- file-object/application/1a-raii-no-helpers.php is unsetting a
`$fileWrapper` variable, this should probably be `$fh`.
- file-object/application/2-context-manager.php has broken indentation.
For little fixes like this, it would probably be most efficient if you
raise a PR, or mail me a patch, rather then me hunting around for each one.
FWIW: Using VS Code to view the examples with syntax highlighting
worked surprisingly well despite the new keywords. It allowed me to
easily spot these typos.
Interesting. I'm using PhpStorm, and it gets very confused by most of them.
Less obvious issues with the locked-pdf example:
- locked-pdf/application/0-linear-code.php: In this case you are
closing the lock before writing into the same output file, which makes
locking useless.
The idea was to lock the file while processing the data, then use the
existing code (which knows nothing about locking) to write to it. You're
right that there's a race condition between unlock and save, but it
seems harsh to call it "useless".
- If you would make the changes, this would effectively become
equivalent to the Transaction example or the file-object example just
with the extra `flock()` call and some extra business-specific logic.
This feels like the Monty Python "what have the Romans ever done for
us?" sketch: "if you take away all the things that make it different
from the other examples, it's the same as the other examples".
Ultimately, they're all just "try { setup(); act(); } finally {
cleanup(); }", but I was trying to write code that was different enough
to play with different implications of each syntax.
I think this example should be adjusted to make use of “external
locking”, i.e. using a dedicated reusable single-purpose lock object
that is independent of the resource in question, so that it is
sufficiently dissimilar from the transaction example (though I guess
it would then be equivalent to the wikimedia-at-ease example).
That would be a completely different scenario, which wouldn't illustrate
what I was intending. It might be interesting to add though; feel free
to contribute it.
From what I see, the locked-pdf example can be removed entirely, since
it does not bring anything new to the table. Did I miss something?
What I was trying to illustrate with that scenario was something where
you want to add logic next to the setup of some object, and logic next
to the tear down of that same object, and encapsulate the whole thing in
some way.
Coming up with realistic but concise examples is tricky.
With regard to
file-handle-unguarded/application/1b-raii-with-scope-block.php, I am
not happy with either of the examples, since the style is bad in
different ways. Based on the “Example showing the combination of let
and if():” from the RFC and the generally accepted “happy path first”
when having an if with an else block, I would personally write it like
this:
let ($fh = fopen('file.txt', 'w') if ($fh !== false) {
try {
foreach ($someThing as $value) {
fwrite($fh, serialize($value));
}
} catch (\Exception $e) {
log('Failed processing the file in some way.');
}
} else {
log('Failed to open file.');
}
Here the “else” block is behaving quite similarly to a “catch” block
in that it does the error handling.
I agree, in this case that nesting does read quite well (although see my
thoughts in the other thread about the trailing "if").
I tried a few different versions, but found it quite hard to have an
intuitive grasp of which statements to combine.
In particular, the implications of "if() let()" vs "let() if()", and how
exactly the "else" block would behave, didn't come naturally. Maybe they
would if I was using it regularly, but it perhaps demonstrates the
"strangeness" I was talking about in the other thread.
It's perhaps also because I've so often been told that the non-block
forms of if(), while(), etc should be avoided, so my instinct is to
start with explicit braces everywhere.
Personally I find it pretty unintuitive that `break;` would target the
`using()` block for the context manager. It feels pretty arbitrary,
why is it possible to break out of `using()`, but not out of `if ()`
or `try` or `catch ()`.
I guess it comes back to the idea that a Context Manager is like an
Iterator that only yields once, so using() is like a loop that goes
round once. But I agree it might not be immediately obvious.
If for some reason, you would like to break out of `let()`, there are
some options that rely on `let()` being designed to compose well with
existing functionality:
Using a do-while(false) loop. This is a pattern that is somewhat known
from C as a “restricted” form of goto.
Not being a seasoned C coder, this always looks weird to me. I have to
read it a couple of times to realise a) that it's not really a loop, and
b) that the false means "do it once", not "do it never".
And of course a regular goto also works.
As far as I can remember, I've used "goto" exactly once in twenty years
of PHP coding. And if I found that code now, I'd probably spot a way to
make it read more naturally without.
If I really wanted to avoid the nesting, I'd probably look for some code
I could break out into a helper function, and use "return" to abort that
early.
The docblock in locked-pdf/application/2-context-manager.php is
incorrectly copy and pasted.
Well spotted. Fixed.
I'm not sure what you changed, but it's still referring to
“Transaction”. I'm also now noticing that the same is true for
locked-pdf/application/1a-raii-no-helpers.php.
Apparently, I misread which file you were talking about, and fixed a
different copy-paste error:
https://gitlab.com/imsop/raii-vs-cm/-/commit/f3e1591c8da07f14399fe4e22710e3ad092ce743
Yes, but if this is desired I would prefer not implement this as an
explicit “try let” special case, but rather by allowing `try` to be
followed by any statement (which includes block statements). This
would then automatically compose with `let()`, just like `let()`
composes with `if()` and would improve predictability of the language
overall. It is not entirely trivial to implement, since the “dangling
else” ambiguity (https://en.wikipedia.org/wiki/Dangling_else) would
then exist as a “dangling catch” ambiguity, but it should be possible.
Yes, it would certainly be "purer" that way. I bet coding standards
would immediately forbid its use with anything other than let blocks though.
--
Rowan Tommins
[IMSoP]