Re: [RFC] Context Managers, v2

From: Date: Fri, 06 Nov 2026 14:49:37 +0000
Subject: Re: [RFC] Context Managers, v2
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-133029@lists.php.net to get a copy of this message
On Fri, Nov 6, 2026, at 3:40 AM, David Maye wrote: > Hello, Please remember to bottom post, not top post. > I agree that as seems more coherent than > => in PHP. The only time I > saw => in PHP was with key => value. And in JS it was > with () => {} > functions. > > I have another question. This mechanism of Context Manager, Is it the > same as the Ruby do blocks? Example with Vagrant Ruby > configuration > file: > > ``` > Vagrant.configure("2") do |config| > config.vm.box = "bento/ubuntu-24.04" > config.vm.box_version = "202510.26.0" > end It's not quite the same. I don't have any Ruby experience, so going based on https://zetcode.com/ruby/do-keyword/, it can be used in a similar way but it's not the same mechanism. Ruby (per that page) creates a closure for a do block, and thus variables go out of scope at the end and get closed implicitly. A using block in this RFC creates no closure or scope. The body of the using expression executes in the same function context as the code around it. Also, the context manager generator itself allows more cleanup than just "unset()" on the context variable. That happens automatically, which can trigger some cleanup, but the context manager is able to do a lot more if it wants to. The model we are building from is Python, basically. > When you set the example as: > > ``` > using (file_for_write('file.txt') => $fp) { > foreach ($someThing as $value) { > fwrite($fp, serialize($value)); > } > }; > // At this point, we're guaranteed that $fp has closed, whether there > was an error or not. > ``` > > It means that the block inside using is aware of variables > outside of > it's own scope, right? Correct. There is no "own scope". As implemented currently, it's just materializing some generator read logic around the body of the using block. There is no new function scope created, so the code inside is "in" the function it's already in, as it always has been. > I like the idea. I have to admit that at first I was confused because I > thought that the first example in the RFC post was exactly the same as > the second example (Error catching included). You mean in the introduction? They do accomplish the same goal, just with less code in the context manager version. > I have another question: according to PHP documentation ("Freeing > resources" > > > https://www.php.net/manual/en/language.types.resource.php#:~:text=makes%20no%20sense.-,Freeing%20resources,-%C2%B6): > > "Thanks to the reference-counting system being part of the Zend Engine, > a resource with no more references to it is detected automatically, and > it is freed by the garbage collector. For this reason, it is rarely > necessary to free the memory manually." > > From what I understand by this text, fclose() is optional (but > recommended according to some Stack Overflow posts). In both cases > (using or not the using using), this acts exactly the same. The > only > difference is that you declare the variable inside the using instead of > the outside the try/catch block, right? Because even inside the > using > function you would need to invoke the fclose() function, right? PHP will automatically close() a resource that goes out of scope, just as it will garbage collect an object that goes out of scope. However, that is only true if that is the last reference to that resource/object. If the value had "escaped" (see the section in the RFC), then the file wouldn't be closed at that point. Also, even if PHP will cleanup as it goes, it's still good practice to close a file when you're done with it to minimize race conditions. A using block takes care of both of those for you. The context manager generator function is where the fclose() would live, so you don't have to remember to write it yourself every time. > Apart from easing the expressiveness of the language, I'm curious of > what would be other benefits. In the post you express that it provides > "a way to abstract out common control-flow and > variable-lifetime-management pattern". What are the ways this function > may bring such improvements? Are those improvement observed by language > like Python not already available in PHP by default? I read somewhere > that PHP tends to be more memory efficient than Python due to the > processing of the many magic methods and other issues. If so, Is the > context manager in Python focused on improving the > variable-lifetime-management due to Python internal issues, and are > these issues present in PHP? The examples section includes most of the example cases I could think of off hand, but I'm sure there are more. Basically, any time you have the use case of "I want to stick some setup logic at the start of this code, and some cleanup and error handling logic at the end", a context manager can abstract that away for you, resulting in reusable logic and in your business logic not getting confused with the boilerplate error handling. Think of opening a connection to some remote service and sending a message to it, but then there's like 10 error cases you need to handle. That's a great example to factor out into a using block, so you don't need to do all that error handling each time. For use in Python, I recommend this video, which lays out a "Pythonic" way of thinking, and in particular shows how context managers in Python can help vastly simplify code. https://www.youtube.com/watch?v=wf-BqAjZb8M (That's the video that really helped me wrap my head around context managers and made me want them in PHP.) As far as performance, there's a huge number of differences between PHP and Python that impact performance. The engine itself is less efficient, plus yes, Python relies a lot more heavily on magic callbacks that incur extra overhead (just as they do in PHP). You can't really pin it down to any one thing. Context Managers in Python in particular are mainly about managing flow of control and error handling, not performance, which is the same for this RFC. The way using is implemented here, that impact is minimal. It adds a generator invocation and read, and that's about it. So it's not 100% free, but the overhead is minimal, and comparable to if you were passing a closure to some ordinary function that did the same sort of wrapping. --Larry Garfield

« previous php.internals (#133029) next »