PHPackages                             cxxi/ftp-client - 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. [File &amp; Storage](/categories/file-storage)
4. /
5. cxxi/ftp-client

ActiveLibrary[File &amp; Storage](/categories/file-storage)

cxxi/ftp-client
===============

Pure PHP FTP/FTPS/SFTP client (framework agnostic).

v1.0.1(4mo ago)02MITPHPPHP &gt;=8.2CI passing

Since Feb 16Pushed 4mo agoCompare

[ Source](https://github.com/cxxi/ftp-client)[ Packagist](https://packagist.org/packages/cxxi/ftp-client)[ RSS](/packages/cxxi-ftp-client/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (5)Versions (4)Used By (0)

cxxi/ftp-client
===============

[](#cxxiftp-client)

[![PHP Version](https://camo.githubusercontent.com/744f8821cc27dec8b0013ade48179731a44eadf4f943e0b1d9ffcb93f80177de/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d382e322532422d626c75652e737667)](https://php.net)[![CI](https://github.com/cxxi/ftp-client/actions/workflows/ci.yml/badge.svg)](https://github.com/cxxi/ftp-client/actions)[![Tests](https://camo.githubusercontent.com/6cbac8fde04a1f6a149a30f60cefcbe2a9a47c5594e2209ff8e947485a36a70a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d756e6974253230253242253230696e746567726174696f6e2d627269676874677265656e2e737667)](https://github.com/cxxi/ftp-client/actions)[![codecov](https://camo.githubusercontent.com/753b754d9edcc9e287055eff458ed9d64a5b198ce982d06033ddbbe19e5d8727/68747470733a2f2f636f6465636f762e696f2f6769746875622f637878692f6674702d636c69656e742f67726170682f62616467652e7376673f746f6b656e3d574d505954594f4f5051)](https://codecov.io/github/cxxi/ftp-client)[![PHPStan Level](https://camo.githubusercontent.com/011029175e639c1888586735f494d5c36b2d1eda37e759002e6db7c2aab082b2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068707374616e2d6c6576656c2532306d617825323028392925323025324225323073747269637425323072756c65732d627269676874677265656e2e737667)](https://phpstan.org/)[![License](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)[![Packagist](https://camo.githubusercontent.com/83c716b2f02a086d44b071f5f3d5ad61f47748f51a0091057091dc2e638ca92f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f637878692f6674702d636c69656e742e737667)](https://packagist.org/packages/cxxi/ftp-client)[![Downloads](https://camo.githubusercontent.com/5a840c098752853c3a77e30b973798917f75b07f97d3a8727a03f61ad0a47006/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f637878692f6674702d636c69656e742e737667)](https://packagist.org/packages/cxxi/ftp-client)

Pure PHP **FTP / FTPS / SFTP** client (framework agnostic).

Simple, expressive API:

```
$clientFtp = FtpClient::fromUrl('ftp://user:pass@example.com:21/path');

$clientFtps = FtpClient::fromUrl('ftps://user:pass@example.com:21/path');

$clientSftp = FtpClient::fromUrl('sftp://user:pass@example.com:22/path');
```

A clean, testable and production-ready transport layer designed for:

- Modern PHP applications (8.2+)
- CLI tools &amp; workers
- Framework integration ([Symfony bundle available](https://github.com/cxxi/ftp-client-bundle))
- High reliability environments

Supports:

- Automatic protocol resolution (`ftp://`, `ftps://`, `sftp://`)
- Safe retry policies with exponential backoff &amp; jitter
- SFTP host key verification (MD5 / SHA1)
- PSR-3 logging
- Clean architecture (Ports &amp; Adapters)

---

Table of Contents
-----------------

[](#table-of-contents)

- [Why this library?](#why-this-library)
- [Installation](#installation)
- [Requirements](#requirements)
- [Quick Start](#quick-start)
- [URL Format](#url-format)
- [Using Connection Options](#using-connection-options)
- [Supported Options](#supported-options)
- [Retry Policy](#retry-policy)
- [Common Operations](#common-operations)
- [Authentication](#authentication)
- [Logging](#logging)
- [Connection Lifecycle](#connection-lifecycle)
- [Architecture](#architecture)
- [Quality &amp; Tests](#quality--tests)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
- [Security](#security)
- [Roadmap](#roadmap)
- [License](#license)

---

Why this library?
-----------------

[](#why-this-library)

PHP already provides `ext-ftp` and `ext-ssh2`. So why wrap them?

Because raw extensions:

- Expose global functions
- Mix transport logic with application logic
- Lack retry mechanisms
- Provide no structured error handling
- Are difficult to unit test
- Offer no safety model for destructive operations

This library adds:

### Unified API

[](#unified-api)

Same interface for FTP, FTPS and SFTP.

Switch protocol by changing the URL scheme.

---

### Built-in Retry Strategy

[](#built-in-retry-strategy)

- Configurable retry count
- Exponential backoff
- Optional jitter
- Safe vs unsafe operation handling

Designed for unstable networks and production systems.

---

### Secure SFTP Support

[](#secure-sftp-support)

- Host key algorithm control
- MD5 / SHA1 fingerprint verification (via ext-ssh2)
- Strict host key checking option

No silent trust of unknown hosts.

---

### Clean Architecture

[](#clean-architecture)

- Ports &amp; adapters
- Fully mockable infrastructure layer
- Decoupled from PHP global functions
- Designed for strict static analysis (PHPStan level max (9) + strict rules + bleeding edge)

---

### Framework Agnostic

[](#framework-agnostic)

Works in:

- Plain PHP scripts
- Symfony (recommended via the [FtpClientBundle](https://github.com/cxxi/ftp-client-bundle))
- Laravel
- CLI workers
- Cron jobs

No framework dependency.

---

### Production Ready

[](#production-ready)

This library is:

- Fully covered by unit tests (100%)
- Integration tested against real FTP / FTPS / SFTP servers
- Static analysis clean (PHPStan level max (9) + strict rules)
- Free of global state
- Designed for deterministic error handling

It is built to be used in critical environments where network instability and safe retries matter.

---

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

[](#installation)

```
composer require cxxi/ftp-client
```

---

Requirements
------------

[](#requirements)

- PHP **8.2+**
- `psr/log` ^3.0

Optional extensions:

- `ext-ftp` → required for FTP / FTPS
- `ext-ssh2` → required for SFTP

Note: FTPS requires `ext-ftp` with SSL support (`ftp_ssl_connect()` available).

---

Quick Start
-----------

[](#quick-start)

```
use Cxxi\FtpClient\FtpClient;

$client = FtpClient::fromUrl('ftp://user:pass@example.com:21/path');

$client
    ->connect()
    ->loginWithPassword()
    ->listFiles('.');
```

---

URL Format
----------

[](#url-format)

```
ftp://user:pass@host:21/path
ftps://user:pass@host:21/path
sftp://user:pass@host:22/path

```

Notes:

- Credentials may be URL-encoded.
- Transport is resolved automatically from the scheme.
- Path becomes the working directory after connection.

---

Using Connection Options
------------------------

[](#using-connection-options)

You can pass a `ConnectionOptions` instance:

```
use Cxxi\FtpClient\Model\ConnectionOptions;

$options = new ConnectionOptions(
    timeout: 15,
    retryMax: 3,
    retryDelayMs: 500,
    retryBackoff: 2.0,
    retryJitter: true
);

$client = FtpClient::fromUrl(
    'ftps://user:pass@example.com:21/path',
    options: $options
);
```

### Array format (canonical structure)

[](#array-format-canonical-structure)

You can also build options from an array. The canonical structure groups transport-layer options under dedicated keys, so protocol-specific settings stay isolated and easy to extend.

```
$options = ConnectionOptions::fromArray([
    'timeout' => 15,
    'passive' => 'auto',

    'retry' => [
        'max' => 3,
        'delay_ms' => 500,
        'backoff' => 2.0,
        'jitter' => true,
        'unsafe_operations' => false,
    ],

    'ssh' => [
        'host_key_algo' => 'ssh-ed25519',
        'expected_fingerprint' => 'MD5:aa:bb:cc:dd:...',
        'strict_host_key_checking' => true,
    ],
]);
```

Protocol-specific keys are ignored when not applicable (e.g. `passive` is ignored for SFTP).

---

Supported Options
-----------------

[](#supported-options)

### timeout

[](#timeout)

Type: `int|null`Applies to: FTP / FTPS / SFTP

- FTP/FTPS → connect timeout
- SFTP → stream timeout for transfers

---

### passive (FTP / FTPS only)

[](#passive-ftp--ftps-only)

Type: `auto | true | false`

- `true` → force passive mode
- `false` → active mode
- `auto` → try passive, fallback to active

Ignored for SFTP.

---

### SFTP Host Key Verification

[](#sftp-host-key-verification)

SFTP supports strict host key verification using MD5 or SHA1 fingerprints (as supported by ext-ssh2).

```
$options = new ConnectionOptions(
    hostKeyAlgo: 'ssh-ed25519',
    expectedFingerprint: 'MD5:aa:bb:cc:dd:...',
    strictHostKeyChecking: true
);
```

If `strictHostKeyChecking` is enabled and fingerprint does not match, connection will fail.

---

SFTP Fingerprint Limitations (ext-ssh2)
---------------------------------------

[](#sftp-fingerprint-limitations-ext-ssh2)

When using the `ext-ssh2` extension (PECL ssh2), only the following fingerprint algorithms are available via `ssh2_fingerprint()`:

- `MD5`
- `SHA1`

The extension does **not** expose SHA256 fingerprints, even though the underlying libssh2 library supports it.

As a consequence:

- `SHA256:` fingerprints (OpenSSH default format) cannot be verified when using ext-ssh2.
- Only `MD5:` and `SHA1:` prefixed fingerprints are supported.
- There is no automatic fallback between algorithms.

If a `SHA256:` fingerprint is provided, the connection will fail when strict host key checking is enabled.

This limitation comes from the PHP extension API, not from the library itself.

---

Retry Policy
------------

[](#retry-policy)

Retry is **disabled by default** (`retryMax = 0`).

When enabled:

Safe operations are retried:

- connect
- login
- listFiles
- downloadFile
- getSize
- getMTime
- isDirectory

Unsafe operations are not retried unless explicitly allowed:

- deleteFile
- rename
- chmod
- removeDirectory
- removeDirectoryRecursive
- makeDirectory

Enable unsafe retries:

```
$options = new ConnectionOptions(
    retryMax: 3,
    retryUnsafeOperations: true
);
```

---

Common Operations
-----------------

[](#common-operations)

### Upload / Download

[](#upload--download)

```
$client->putFile('remote.csv', '/local/file.csv');

$client->downloadFile('remote.csv', '/tmp/remote.csv');
```

---

### Directory Utilities

[](#directory-utilities)

```
$client->isDirectory('subdir');

$client->makeDirectory('subdir', recursive: true);

$client->removeDirectory('empty-dir');

$client->removeDirectoryRecursive('dir-to-delete');
```

---

### File Utilities

[](#file-utilities)

```
$client->deleteFile('old.csv');

$client->rename('old.csv', 'new.csv');

$size = $client->getSize('file.csv');

$mtime = $client->getMTime('file.csv');

$client->chmod('file.csv', 0644);
```

---

FTP-only Advanced Listing
-------------------------

[](#ftp-only-advanced-listing)

Available only on FTP / FTPS:

```
$raw = $client->rawList('.', recursive: false);

$mlsd = $client->mlsd('.');
```

---

Authentication
--------------

[](#authentication)

### Password

[](#password)

```
$client
    ->connect()
    ->loginWithPassword();
```

Override credentials:

```
$client->loginWithPassword('user', 'pass');
```

---

### SFTP Public Key

[](#sftp-public-key)

```
$client
    ->connect()
    ->loginWithPubkey(
        '/home/me/.ssh/id_rsa.pub',
        '/home/me/.ssh/id_rsa',
        user: 'my-user'
    );
```

Only valid for SFTP connections.

---

Logging
-------

[](#logging)

Pass a `Psr\Log\LoggerInterface` to the factory:

```
use Cxxi\FtpClient\FtpClient;

$client = FtpClient::fromUrl(
    'ftp://user:pass@example.com:21/path',
    logger: $logger
);
```

Logged events include:

- connection attempts
- authentication
- transfers
- destructive operations

Credentials are never logged.

---

Connection Lifecycle
--------------------

[](#connection-lifecycle)

Connections auto-close on destruction.

For long-running scripts:

```
$client->closeConnection();
```

Safe to call even if not connected.

---

Architecture
------------

[](#architecture)

The library follows a clean architecture approach:

- Transport contracts (`Contracts`)
- Protocol-specific transports (FTP / SFTP)
- Infrastructure ports (filesystem, streams, ssh2, ftp)
- Native adapters
- Retry wrapper with safe/unsafe semantics

This design allows easy mocking and full unit testing.

---

Quality &amp; Tests
-------------------

[](#quality--tests)

This library is designed for reliability in production environments.

### Unit Tests

[](#unit-tests)

- 100% code coverage on the core domain and infrastructure
- Strict PHPUnit 11 configuration
- Full mocking of native adapters
- Error handling and retry semantics fully tested
- PHPStan level max (9) clean (source + tests)
- phpstan-strict-rules enabled
- bleedingEdge rules enabled

Run unit tests:

```
composer test:unit
```

Generate coverage report:

```
composer test:coverage
```

---

### Integration Tests

[](#integration-tests)

All protocols are tested against real Dockerized servers.

ProtocolServerTested FeaturesFTPpure-ftpdActive / Passive / Auto mode, transfers, directory ops, MLSDFTPSpure-ftpd (TLS required)Explicit TLS, transfers, error casesSFTPOpenSSHPassword auth, public key auth, fingerprint verificationEach integration stack is fully isolated and reproducible.

Run individual protocol tests:

```
composer test:integration:ftp
composer test:integration:ftps
composer test:integration:sftp
```

Run all integration tests:

```
composer test:integration
```

Run everything (unit + integration):

```
composer test:all
```

---

### Static Analysis

[](#static-analysis)

PHPStan level max (9) with strict rules and bleeding edge enabled:

```
composer phpstan
```

Configuration includes:

- level: max (9)
- phpstan-strict-rules
- bleedingEdge.neon
- treatPhpDocTypesAsCertain: true

---

### Code Style

[](#code-style)

```
composer cs
composer cs:check
```

---

### CI

[](#ci)

All checks are enforced in CI:

- Unit tests (100% coverage)
- FTP integration tests
- FTPS (TLS) integration tests
- SFTP integration tests
- PHPStan level max (9) + strict rules
- Coding standards

Every protocol feature documented in this README is covered by automated tests.

---

Troubleshooting
---------------

[](#troubleshooting)

### FTPS: `ftp_ssl_connect()` not available

[](#ftps-ftp_ssl_connect-not-available)

Make sure `ext-ftp` is compiled with SSL support. `ftp_ssl_connect()` must be available.

### SFTP: host key verification fails

[](#sftp-host-key-verification-fails)

- Ensure the fingerprint prefix matches (`MD5:` or `SHA1:`).
- SHA256 fingerprints are not supported by `ext-ssh2`.

### Passive mode issues (FTP/FTPS)

[](#passive-mode-issues-ftpftps)

If transfers hang behind NAT/firewalls:

- Try forcing passive mode (`passive: true`)
- Or use `passive: auto`

### Connection timeouts

[](#connection-timeouts)

Adjust the `timeout` option depending on network conditions.

---

Contributing
------------

[](#contributing)

Contributions are welcome.

Before submitting a pull request:

1. Ensure all unit tests pass.
2. Ensure all integration tests pass.
3. Run PHPStan (level max (9) must remain clean).
4. Follow existing coding standards.

Useful commands:

```
composer test:all
composer phpstan
composer cs
```

Please open an issue first for significant changes or architectural discussions.

---

Security
--------

[](#security)

If you discover a security vulnerability, please open a GitHub Security Advisory or contact the maintainer privately before disclosing it publicly.

Credentials are never logged by design.

see [SECURITY.md](SECURITY).

---

Roadmap
-------

[](#roadmap)

The project roadmap is maintained separately to keep this README focused and concise.

**Full roadmap available here:****[docs/ROADMAP.md](docs/ROADMAP.md)**

The roadmap covers:

- SFTP backend improvements (phpseclib support, SHA256 fingerprints)
- OpenSSH `known_hosts` integration
- FTPS security enhancements (TLS controls, certificate validation)
- Stream-based API &amp; atomic uploads
- Transfer progress callbacks
- Resumable transfers
- Long-running worker stability improvements
- Observability &amp; structured events
- Advanced retry safety
- Performance &amp; scalability explorations

All roadmap items follow the project principles:

- Clean architecture
- Deterministic behavior
- Strong safety guarantees
- Backward compatibility (unless major version bump)

Community feedback is welcome via GitHub Issues and Discussions.

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

35

—

LowBetter than 77% of packages

Maintenance75

Regular maintenance activity

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity49

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

Every ~0 days

Total

2

Last Release

137d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3498dd2e1756f43ec928b5d514b5a7f1008517b04bb7776a01b1f55a77016ee0?d=identicon)[cxxi](/maintainers/cxxi)

---

Top Contributors

[![cxxi](https://avatars.githubusercontent.com/u/22669831?v=4)](https://github.com/cxxi "cxxi (24 commits)")

---

Tags

file-transferftpftpsnetworkphppsr-3retrysftpsshftpclientsshsftpuploaddownloadtransferftps

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/cxxi-ftp-client/health.svg)

```
[![Health](https://phpackages.com/badges/cxxi-ftp-client/health.svg)](https://phpackages.com/packages/cxxi-ftp-client)
```

###  Alternatives

[league/flysystem

File storage abstraction for PHP

13.6k679.9M2.5k](/packages/league-flysystem)[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.7k38.9k](/packages/matomo-matomo)[algolia/algoliasearch-client-php

API powering the features of Algolia.

69735.1M159](/packages/algolia-algoliasearch-client-php)[nicolab/php-ftp-client

A flexible FTP and SSL-FTP client for PHP. This lib provides helpers easy to use to manage the remote files.

6435.6M28](/packages/nicolab-php-ftp-client)[creocoder/yii2-flysystem

The flysystem extension for the Yii framework

2931.7M63](/packages/creocoder-yii2-flysystem)[yii2mod/yii2-ftp

A flexible FTP and SSL-FTP client for PHP. This lib provides helpers easy to use to manage the remote files.

34452.6k3](/packages/yii2mod-yii2-ftp)

PHPackages © 2026

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