PHPackages                             awehttam/zmodem-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. awehttam/zmodem-php

ActiveLibrary

awehttam/zmodem-php
===================

Pure-PHP ZMODEM (sz/rz) file transfer protocol implementation for telnet/SSH-style byte streams.

v1.0.0(yesterday)00MITPHPPHP &gt;=8.2CI passing

Since Jul 27Pushed yesterday1 watchersCompare

[ Source](https://github.com/awehttam/zmodem-php)[ Packagist](https://packagist.org/packages/awehttam/zmodem-php)[ RSS](/packages/awehttam-zmodem-php/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (2)Used By (0)

zmodem-php
==========

[](#zmodem-php)

A pure-PHP implementation of the ZMODEM (`sz`/`rz`) file transfer protocol, extracted from [BinktermPHP](https://github.com/awehttam/binkterm-php)'s telnet/SSH terminal server.

Implements both the sender (`sz` — server sends a file to the client) and receiver (`rz` — server receives a file from the client) sides of the protocol, as documented in `ZMODEM.DOC` by Chuck Forsberg (1988), against any PHP stream resource — telnet sockets, SSH channel streams, etc.

Installation
------------

[](#installation)

```
composer require awehttam/zmodem-php
```

Usage
-----

[](#usage)

```
use Zmodem\ZmodemTransfer;

// Send a file to the connected client ($conn is a PHP stream resource,
// e.g. from stream_socket_accept() or an SSH channel stream).
ZmodemTransfer::send($conn, '/path/to/file.zip', 'file.zip');

// Receive a file uploaded by the client into a destination directory.
$savedPath = ZmodemTransfer::receive($conn, '/path/to/uploads');
```

Pass `$escapeTelnetIac = true` as the trailing argument on plain Telnet connections so that `0xFF` bytes in binary file data are doubled per the Telnet IAC escaping rule. Omit it (or pass `false`) for SSH or raw socket connections.

```
ZmodemTransfer::send($conn, $path, $name, escapeTelnetIac: true);
ZmodemTransfer::receive($conn, $destDir, escapeTelnetIac: true);
```

CLI Tools
---------

[](#cli-tools)

`sz.php` and `rz.php` in the repo root are standalone command-line tools modeled on the classic Unix `sz`/`rz` utilities. They communicate over stdin/stdout, so they're meant to be run with stdin/stdout connected to the remote ZMODEM peer — e.g. over a serial line, a telnet/SSH session, or from a terminal emulator with ZMODEM autodetect (in which case the emulator intercepts the ZMODEM stream and drives its own file dialog).

Internally they wrap STDIN (read) and STDOUT (write) into a single bidirectional resource via `Zmodem\StdioDuplexStream`(`src/StdioDuplexStream.php`), since `send()`/`receive()` operate on one `$conn` used for both directions.

```
# Send a file
php sz.php [--iac]

# Receive a file into a directory (default: current directory)
php rz.php [--iac] [dest-dir]
```

Pass `--iac` only on plain-telnet links (see the IAC escaping note above).

Configuration
-------------

[](#configuration)

Configuration is read via `Zmodem\Config::env()`, which checks process environment variables by default:

VariableDefaultPurpose`ZMODEM_DEBUG``false`Verbose frame-level protocol logging`ZMODEM_LOG_FILE`*(auto)*Log file path (defaults to `zdebug.log` in the project root)To inject configuration values programmatically instead of via environment variables (e.g. from a host application's own config store):

```
use Zmodem\Config;

Config::set('ZMODEM_DEBUG', 'true');
Config::set('ZMODEM_LOG_FILE', '/var/log/myapp/zmodem.log');
```

Development
-----------

[](#development)

### Project layout

[](#project-layout)

```
src/
  ZmodemTransfer.php     Protocol implementation (send/receive, framing, CRC, I/O)
  Config.php             Environment/config resolver (getenv() + runtime overrides)
  StdioDuplexStream.php  stdin/stdout → single bidirectional stream adapter
sz.php                   CLI sender built on ZmodemTransfer::send()
rz.php                   CLI receiver built on ZmodemTransfer::receive()
zdebug.log               Default log file (debug/info logging), created on demand

```

There's no build step — this is a plain PSR-4 library (`Zmodem\` → `src/`). Clone the repo and either `composer require` it as a path repository in a consuming project, or `require` the two files in `src/` directly (see `sz.php`/`rz.php` for an example that does this without Composer at all).

### How `send()`/`receive()` work

[](#how-sendreceive-work)

Both methods take a single PHP stream resource (`$conn`) representing an already-connected, full-duplex link to the remote peer — a telnet socket, an SSH channel stream, or an adapter like `StdioDuplexStream`. The library does not open connections itself; it only reads and writes framed ZMODEM data on whatever resource it's given via `fread()`/`fwrite()`/`stream_select()`.

The core protocol state machine lives entirely in `ZmodemTransfer.php`:

- Header building/parsing (`sendHexHeader`/`sendBinHeader`, `parseHexHeader`/`parseBinHeader`)
- Data subpacket framing with ZDLE escaping and CRC-16 (`sendDataSubpacket`, `receiveFileData`)
- The `send()`/`receive()` methods drive the frame exchange (ZRQINIT → ZRINIT → ZFILE → ZRPOS → ZDATA/ZEOF → ZFIN) and handle retries/resync against real-world client quirks (documented inline where non-obvious).

### Debugging

[](#debugging)

Set `ZMODEM_DEBUG=true` (or `Config::set('ZMODEM_DEBUG', 'true')`) to get frame-level logging of every header sent/received, written to `ZMODEM_LOG_FILE` (default `zdebug.log` in the project root). This is the fastest way to diagnose interop issues with a specific terminal client — compare the logged frame sequence against `ZMODEM.DOC`.

### Testing changes

[](#testing-changes)

There's no automated test suite yet. The most direct way to exercise a change is a loopback transfer using the CLI tools — bridge `sz.php`'s stdout/stdin to `rz.php`'s stdin/stdout (e.g. via two `proc_open()` calls piped together, or two real terminals/PTYs) and diff the received file against the source. When testing against real terminal emulators (SecureCRT, SyncTERM, ZOC, etc.), enable `ZMODEM_DEBUG` on both ends if possible to compare frame traces.

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/196827?v=4)[Matthew Asham](/maintainers/awehttam)[@awehttam](https://github.com/awehttam)

---

Top Contributors

[![awehttam](https://avatars.githubusercontent.com/u/196827?v=4)](https://github.com/awehttam "awehttam (6 commits)")

---

Tags

phpphp-libraryphp8zmodem

### Embed Badge

![Health badge](/badges/awehttam-zmodem-php/health.svg)

```
[![Health](https://phpackages.com/badges/awehttam-zmodem-php/health.svg)](https://phpackages.com/packages/awehttam-zmodem-php)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
