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]

Reply via email to