Download the PHP package croustibat/php-pty without Composer

On this page you can find all versions of the php package croustibat/php-pty. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package php-pty

php-pty

tests

node-pty for PHP. Pseudo-terminals in pure PHP — no extension to compile, no companion C library, and no Node. Just ext-ffi and ext-pcntl. macOS and Linux.

The namespace is Croustibat\Pty\. The package requires PHP CLI with FFI, pcntl and posix; it has no runtime Composer dependencies.

Try it

From a checkout, install dependencies and run the quickstart:

It prints 30 120: the terminal dimensions reported by the child process. No API key, external service or additional application is needed.

Then open an interactive shell:

Inside it, run stty size, resize your terminal window and run stty size again. Type exit or press Ctrl-D at an empty shell prompt to leave. Ctrl-C is forwarded through the PTY to the child terminal's foreground process group. Your original terminal settings are restored when the relay finishes.

Pass a command as separate arguments after --:

Example What it demonstrates
quickstart.php Spawn with a known size, drain output and collect the exit code
interactive.php Keyboard relay, live resizing, bounded queues, partial writes and terminal restoration
multiple-sessions.php One stream_select() loop reading two independently finishing commands

The interactive relay requires a terminal on both STDIN and STDOUT and the system stty command. It returns the child's exit code. It handles external SIGINT, SIGTERM, SIGHUP and SIGQUIT with cleanup; SIGKILL cannot be intercepted. The multiple-session example labels each output line with fast or slow; ordering between processes is intentionally not guaranteed.

When installed with Composer in an application, the examples can also be run under vendor/croustibat/php-pty/examples/. Run them as standalone CLI scripts. They are reference implementations to adapt, not a public event-loop API.

Why

PHP can already reach a pty through proc_open() with ['pty'] descriptors — but it hands you no control over the window size. No ioctl, so no TIOCSWINSZ, so no SIGWINCH. Interactive TUIs render at whatever geometry they guess and never learn they were resized.

That single gap is why PHP projects that need to drive a terminal end up shipping a Node sidecar just for node-pty. This package closes it without leaving PHP.

Compared to node-pty

node-pty php-pty
Runtime Node PHP CLI
Install native module, needs a toolchain composer require, nothing compiled
Platforms macOS, Linux, Windows (ConPTY) macOS, Linux
Concurrency libuv event loop your own stream_select() loop
Scope pty + spawn pty + spawn

Windows is the honest gap: it has no pty. ConPTY is a different API and would be a different package.

What you get: a real pty, a real controlling terminal (login_tty, so job control and Ctrl-C work), a settable and resettable window size, and a non-blocking master stream you can drive from stream_select().

What you don't: an event loop, a scrollback buffer, a terminal emulator, or a session daemon. This is the primitive, not the product.

Requirements

Troubleshooting

Read this before you use it

pcntl_fork() duplicates the entire process. Every open PDO connection, Redis socket and file handle is inherited by the child. In a web request, an FPM worker or a queue job, that is a foot-gun, not a feature. This package targets long-running CLI processes — daemons, terminal multiplexers, agent runners — and nothing else. It refuses to load outside the CLI SAPI.

ECHO is on by default on the slave, as on any terminal. The line discipline echoes your writes straight back to the master before the child has read anything. If you are measuring round-trip latency, you are measuring the kernel, not the child. Send stty -echo or set the termios flags yourself.

Reading and writing

A PTY is a byte stream. read() is non-blocking: an empty string can mean there is no data yet. Wait for readiness with stream_select() and continue reading while the child is active. After it exits, drain any remaining output before closing the session. Waiting for exit before reading can block a child whose output is waiting to be consumed.

write() retries partial writes until its deadline and returns the number of bytes accepted. Keep substr($payload, $written) when the count is short. For full-duplex relays, interleave reads and writes and bound both queues, as the interactive example does. A writable stream can still accept only part of a buffer. write(timeout: 0) sends no bytes.

The terminal echoes input by default. Use stty -echo inside the child to turn echo off; use stty raw -echo for byte-oriented protocols. Changing the local terminal is a separate operation, and its settings must be restored.

The measured partial-write behaviour and the Darwin ARM64 variadic ioctl ABI regression are explained in Implementation notes.

API

Method
Pty::spawn(array $command, int $rows, int $cols, ?array $env, ?string $cwd) Returns a Session
Session::stream() Non-blocking master, for stream_select()
Session::write(string, float $timeout = 5.0): int Loops over partial writes, bounded
Session::read(int $length = 65536): string '' when nothing is pending
Session::resize(int $rows, int $cols) TIOCSWINSZ + SIGWINCH
Session::winSize(): array [rows, cols]
Session::ttyName(): string e.g. /dev/ttys018
Session::pid(), isRunning(), signal(), terminate(), kill()
Session::wait(?float $timeout = 10.0): int Exit code, 128 + signal when killed; -1 on timeout or unavailable status. null waits indefinitely
Session::close() Closes the master; the child gets SIGHUP

Rows and columns must be between 1 and 65535, both at spawn and resize. An invalid working directory or executable is rejected before allocating a terminal. Explicit relative executable paths are resolved against cwd when provided. If the working directory disappears before the child enters it, the child exits with code 127 without executing the command.

write() returns the number of bytes accepted before its deadline; on a short write, retain and retry the remaining suffix. Timeouts must be finite and non-negative. write(timeout: 0) returns immediately without writing; wait(timeout: 0) makes one non-blocking status check. A timed-out wait() does not stop the child. If another process manager has already reaped the child, wait() returns -1 and exitCode() remains null.

close() is idempotent and releases both the original master descriptor and the PHP stream's duplicate. Newly spawned children close their inherited copies of other php-pty sessions. Closing a session does not reap its process: call wait() afterwards to collect its status. A child can handle or ignore SIGHUP; use terminate() or kill() when explicit termination is needed.

Scope

Out of scope, deliberately:

Security

FFI, pcntl_fork() and command execution are all dangerous by design here. SECURITY.md spells out the threat model and how to report a vulnerability privately. Short version: never pass user-controlled input as the executable, and do not work around the CLI-only check.

Contributing and releases

See CONTRIBUTING.md for local checks and the CI matrix. Changes not yet included in a tag are listed under Unreleased in the CHANGELOG. Check that section when comparing main with an installed Composer version.

Credits

The FFI/pcntl approach was validated against jerch's advice in the Coolify discussion on terminal streaming, which recommended a single-process event loop over per-session processes.

Licence

MIT.


All versions of php-pty with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
ext-ffi Version *
ext-pcntl Version *
ext-posix Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package croustibat/php-pty contains the following files

Loading the files please wait ...