Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.4.1 - 2026-06-06

- Added Windows resize-event support: `ReadConsoleInputW()` `WINDOW_BUFFER_SIZE_EVENT` now returns `Terminal\Key::Resize`.
- Documented the default 25ms POSIX sequence timeout used by `Terminal\Terminal::readKey()`.
- Documented current key-input scope, including UTF-8 code point behavior, control-byte behavior, and function-key/modifier limitations.

## 0.4.0 - 2026-06-06

- Replaced raw-mode string handles with opaque `Terminal\ModeToken` objects.
Expand Down
72 changes: 40 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,28 @@ The first cut stays small on purpose. It exposes the pieces that are awkward to

Created and maintained by Pratik Bhujel.

Current release: `v0.4.0`.
Current release: `v0.4.1`.

`v0.4.0` keeps the `Terminal\Terminal` class and enum API from `v0.3.0`, replaces raw-mode string handles with `Terminal\ModeToken`, and hardens key reads, resize handling, ANSI detection, and terminal-size fallbacks. The older `v0.2.0` release used the first procedural `terminal_*()` API.
`v0.4.1` keeps the `Terminal\Terminal` class and enum API from `v0.3.0`, replaces raw-mode string handles with `Terminal\ModeToken`, and hardens key reads, resize handling, ANSI detection, and terminal-size fallbacks. The older `v0.2.0` release used the first procedural `terminal_*()` API.

## Why this exists

PHP already has useful pieces such as `stream_isatty()` and `sapi_windows_vt100_support()`, but there is still no small extension that exposes a shared terminal capability layer across Unix and Windows.

The main goal is native Windows parity for PHP CLI prompts and terminal apps. Users should not need WSL just to get arrow keys, raw mode, terminal size, and safe restore behavior that already work on macOS and Linux.

This also removes two common framework workarounds: spawning `stty`/`mode CON` helpers for terminal state, and bundling a Windows-only helper executable just to read hidden password input.

Older console-oriented extensions took different paths:

- `ncurses` and `termbox` wrap external terminal libraries
- `php-wcli` is Windows-only

This extension stays narrower:

- no ncurses dependency
- no framework coupling
- one user-facing API on both backends

## Current API

Expand Down Expand Up @@ -38,11 +57,19 @@ Enums:
`Terminal\Terminal::enableRawMode()` leaves terminal output processing intact, so normal prompt output such as `"\n"` keeps working while input is read one key at a time.
On POSIX, raw-mode switches use `TCSANOW` so mode changes are immediate; callers that type ahead should not assume pending input was drained first.
`Terminal\Terminal::readKey()` temporarily prepares standard input for key reads, restores the previous mode before returning, returns special keys as `Terminal\Key` cases, returns printable input as strings including UTF-8 input, and returns `false` when no key is available before the timeout. If standard input is already raw, `readKey()` preserves that state.
On POSIX, `$sequenceTimeout` controls how long `readKey()` waits for bytes that complete an escape or UTF-8 sequence after the first byte.
On POSIX, `SIGWINCH` during `readKey()` returns `Terminal\Key::Resize`.
On POSIX, `$sequenceTimeout` controls how long `readKey()` waits for bytes that complete an escape or UTF-8 sequence after the first byte. `null` uses the default 25ms sequence timeout.
POSIX `SIGWINCH` and Windows `WINDOW_BUFFER_SIZE_EVENT` during `readKey()` return `Terminal\Key::Resize`.
Printable Unicode input is returned as the next encoded code point from the terminal, not as a full grapheme cluster.
`Terminal\Terminal::readSecret()` reads a hidden line from standard input, restores the previous mode before returning, handles backspace and UTF-8 input, and returns `false` on timeout or abort.

Current key input scope:

- normalized keys: arrows, enter, backspace, escape, tab, home, end, delete, page up, page down, resize
- printable input: returned as a string containing the next encoded code point
- control bytes such as Ctrl+C: returned as single-byte strings by `readKey()`
- not normalized yet: F1-F12, modifier combinations, and full grapheme clusters
- unknown POSIX escape sequences: fall back to `Terminal\Key::Escape`

The earlier procedural API was removed while the project is still pre-1.0 so the extension can track the PHP core discussion more closely.

Example:
Expand Down Expand Up @@ -71,25 +98,6 @@ try {
}
```

## Why this exists

PHP already has useful pieces such as `stream_isatty()` and `sapi_windows_vt100_support()`, but there is still no small extension that exposes a shared terminal capability layer across Unix and Windows.

The main goal is native Windows parity for PHP CLI prompts and terminal apps. Users should not need WSL just to get arrow keys, raw mode, terminal size, and safe restore behavior that already work on macOS and Linux.

This also removes two common framework workarounds: spawning `stty`/`mode CON` helpers for terminal state, and bundling a Windows-only helper executable just to read hidden password input.

Older console-oriented extensions took different paths:

- `ncurses` and `termbox` wrap external terminal libraries
- `php-wcli` is Windows-only

This extension stays narrower:

- no ncurses dependency
- no framework coupling
- one user-facing API on both backends

## Enabling the extension

After you build and install it, enable it like any normal PHP extension:
Expand All @@ -106,11 +114,11 @@ extension=terminal
extension=php_terminal.dll
```

## Installing v0.4.0
## Installing v0.4.1

The `v0.4.0` release is available at:
The `v0.4.1` release is available at:

https://github.com/prateekbhujel/php-terminal/releases/tag/v0.4.0
https://github.com/prateekbhujel/php-terminal/releases/tag/v0.4.1

Windows builds are attached for PHP 8.2-8.5, x64, TS/NTS. These are native Windows builds for normal Windows PHP runtimes, not WSL. Pick the zip that matches your PHP version and thread-safety mode, copy `php_terminal.dll` into your PHP extension directory, and enable it with:

Expand All @@ -123,7 +131,7 @@ Build from source on Unix-like systems:
```sh
git clone https://github.com/prateekbhujel/php-terminal.git
cd php-terminal
git checkout v0.4.0
git checkout v0.4.1
phpize
./configure --enable-terminal
make
Expand All @@ -140,7 +148,7 @@ For installed builds, use your normal `extension=terminal` configuration instead

### Build current main from source

To test unreleased changes after `v0.4.0`:
To test unreleased changes after `v0.4.1`:

```sh
phpize
Expand Down Expand Up @@ -178,9 +186,9 @@ set PHP_BIN=C:\xampp\php\php.exe

Download the matching zip from the release page. For example:

- PHP 8.2, thread safety disabled: `php_terminal-v0.4.0-8.2-nts-vs16-x86_64.zip`
- PHP 8.2, thread safety enabled: `php_terminal-v0.4.0-8.2-ts-vs16-x86_64.zip`
- PHP 8.4, thread safety disabled: `php_terminal-v0.4.0-8.4-nts-vs17-x86_64.zip`
- PHP 8.2, thread safety disabled: `php_terminal-v0.4.1-8.2-nts-vs16-x86_64.zip`
- PHP 8.2, thread safety enabled: `php_terminal-v0.4.1-8.2-ts-vs16-x86_64.zip`
- PHP 8.4, thread safety disabled: `php_terminal-v0.4.1-8.4-nts-vs17-x86_64.zip`

Copy `php_terminal.dll` into that PHP installation's extension directory, for example:

Expand Down Expand Up @@ -316,7 +324,7 @@ Until Laravel Prompts has that adapter, existing Laravel Prompts releases will s

The bundled `examples/prompt.php` file is intentionally small so framework authors can see the shape without reading a full TUI library.

Future Laravel Prompts adapter work should target the `Terminal\Terminal` and enum API from `v0.4.0`.
Future Laravel Prompts adapter work should target the `Terminal\Terminal` and enum API from `v0.4.1`.

For release feedback, open a new issue with the OS, terminal, PHP version, extension version, what you tried, and the behavior you expected.

Expand Down
15 changes: 5 additions & 10 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,18 @@
</lead>
<date>2026-06-06</date>
<version>
<release>0.4.0</release>
<api>0.4.0</api>
<release>0.4.1</release>
<api>0.4.1</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<license uri="https://www.php.net/license/3_01.txt">PHP License</license>
<notes>
- Replaced raw-mode string handles with opaque Terminal\ModeToken objects.
- Added Terminal\Key::Resize for POSIX SIGWINCH events surfaced by Terminal\Terminal::readKey().
- Added sequence-timeout control for delayed escape and UTF-8 continuation bytes.
- Added terminal-size fallback to positive COLUMNS and LINES values.
- Hardened ANSI detection with non-empty NO_COLOR, COLORTERM truecolor/24bit, and TERM_PROGRAM.
- Hardened POSIX key-read timeout math with CLOCK_MONOTONIC when available.
- Hardened malformed CSI escape handling and raw-mode re-entry paths.
- Updated examples, docs, package metadata, and PHPT coverage.
- Added Windows resize-event support: ReadConsoleInputW WINDOW_BUFFER_SIZE_EVENT now returns Terminal\Key::Resize.
- Documented the default 25ms POSIX sequence timeout used by Terminal\Terminal::readKey().
- Documented current key-input scope, including UTF-8 code point behavior, control-byte behavior, and function-key/modifier limitations.
</notes>
<contents>
<dir name="/">
Expand Down
2 changes: 1 addition & 1 deletion php_terminal.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
extern zend_module_entry terminal_module_entry;
# define phpext_terminal_ptr &terminal_module_entry

# define PHP_TERMINAL_VERSION "0.4.0"
# define PHP_TERMINAL_VERSION "0.4.1"

# define TERMINAL_STREAM_STDIN 0
# define TERMINAL_STREAM_STDOUT 1
Expand Down
5 changes: 5 additions & 0 deletions terminal.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,11 @@ static zend_string *terminal_read_stdin_key(double timeout, bool timeout_is_null
break;
}

if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
result = terminal_key_string("resize");
break;
}

if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) {
zend_string *key = terminal_key_from_input_record(&record.Event.KeyEvent);

Expand Down