Re: Re: [PHP DEV] [Discussion] Native terminal helpers for PHP CLI

From: Date: Fri, 18 Sep 2026 07:46:05 +0000
Subject: Re: Re: [PHP DEV] [Discussion] Native terminal helpers for PHP CLI
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-132532@lists.php.net to get a copy of this message
Hi it seems your replies don't contain proper in-reply-to or references headers, which breaks the threading. As an example in the archives at https://news-web.php.net/php.internals/132530 no other emails from the discussion are shown and https://news-web.php.net/php.internals/132528 only shows your email and my reply. On externals.io new threads are created for every email. Can you check the configuration of your email client and make sure to always use real “Reply” instead of sending a fresh email? On 2026-09-18 04:19, Pratik Bhujel wrote:
To answer your question regarding the domain problem: The primary issue is that PHP CLI currently lacks native primitives for non-canonical raw terminal mode and single keypress reading. Modern interactive CLI tools in userland (such as Laravel Prompts, Symfony Console, and interactive tools) want to deliver rich experiences like searchable selection menus, autocomplete, multi-choice checkboxes, spinners, and hidden password prompts.
Yes, I roughly understand the problem that is being solved. It's just something I never had to deal with, so I can't comment on whether the solution is complete and ergonomic.
2. Instance-based Terminal handle: Classes are no longer static wrappers. The Io\Terminal\Terminal class encapsulates a stream descriptor, created via named constructors stdin(), stdout(), stderr(), or by wrapping custom stream resources.
For consistency with the recently introduced APIs, the named constructors should start with a prefix that clearly indicates that the method is a constructor. As of now we have the following: - Dom\HTMLDocument::createEmpty(), ::createFromFile(), ::createFromString() - Time\Duration::fromSeconds(), ::fromMilliseconds(), …, ::fromIso8601DurationString() - Uri\Rfc3986\Uri::parse() (though this one also has a real constructor). For the Terminal I wonder why we need to have stdin, stdout, and stderr separately. For me logically a “Terminal” is a combination of all three and I would expect them to be “synchronized” if that makes sense?
3. Automatic RAII cleanup: Both Terminal instances and ModeToken objects track active raw mode and automatically restore terminal state upon destruction. If an uncaught exception terminates script execution, the destructor restores canonical mode so the developer shell is never left broken.
That makes sense to me.
5. Free-standing procedural functions: Io\Terminal provides procedural functions like is_tty(), read_key(), and get_size() for functional script usage.
I've taken another look at these and have some point that stood out to me: - Do we need all three of get_size(), get_width(), get_height()? The latter two seem redundant. - Should related methods have a common prefix for discoverability? So mode_enable_raw() and mode_restore() instead of enable_raw_mode() and restore_mode()? - Many (all?) of the functions also exists as methods on the Terminal class. This redundant procedural + object-oriented API design is something we no longer do. Decide on one of the two, depending on what is more useful. I expect the OO variant to be the right choice due to the RAII support. Best regards Tim Düsterhus

« previous php.internals (#132532) next »