Skip to content

FTP SFTP

Ray Fung edited this page Feb 26, 2026 · 3 revisions

FTP & SFTP

Razy provides fluent FTP and SFTP clients for remote file operations. Both clients share a similar API, support logging, and feature auto-disconnect on destruction.


Table of Contents


FTP Client

Requires: ext-ftp

Connection & Authentication

use Razy\FTPClient;



// Standard FTP

$ftp = new FTPClient(

    host: 'ftp.example.com',

    port: 21,

    timeout: 30,

    ssl: false

);



// FTPS (FTP over SSL)

$ftp = new FTPClient('ftp.example.com', 21, 30, ssl: true);



// Login

$ftp->login('username', 'password');



// Anonymous login

$ftp->login();



// Check connection

$ftp->isConnected();   // true

$ftp->isSecure();      // true if SSL



// Disconnect

$ftp->disconnect();

File Operations

// Upload local file

$ftp->upload('/local/path/file.txt', '/remote/path/file.txt');



// Upload with specific transfer mode

$ftp->upload('/local/image.jpg', '/remote/image.jpg', FTPClient::MODE_BINARY);



// Upload string content directly

$ftp->uploadString('Hello, World!', '/remote/hello.txt');



// Download to local file

$ftp->download('/remote/file.txt', '/local/file.txt');



// Download to string

$content = $ftp->downloadString('/remote/hello.txt');



// Delete file

$ftp->delete('/remote/old-file.txt');



// Rename / move

$ftp->rename('/remote/old-name.txt', '/remote/new-name.txt');



// Change permissions

$ftp->chmod('/remote/script.sh', 0755);

Directory Operations

// Current directory

$cwd = $ftp->pwd();



// Change directory

$ftp->chdir('/var/www/html');

$ftp->cdup(); // go up one level



// Create directory

$ftp->mkdir('/remote/new-folder');



// Create recursively (nested)

$ftp->mkdirRecursive('/remote/a/b/c/d');



// Remove directory

$ftp->rmdir('/remote/empty-folder');



// Remove recursively (with contents)

$ftp->rmdirRecursive('/remote/folder-with-files');



// List files (names only)

$files = $ftp->listFiles('/remote/path');

// ['file1.txt', 'file2.jpg', 'subfolder']



// List with details (parsed ls -l output)

$details = $ftp->listDetails('/remote/path');

// [['name' => 'file1.txt', 'size' => 1024, 'type' => 'file', ...], ...]

File Information

$size = $ftp->size('/remote/file.txt');        // bytes

$mtime = $ftp->lastModified('/remote/file.txt'); // Unix timestamp

$exists = $ftp->exists('/remote/file.txt');    // bool

$isDir = $ftp->isDir('/remote/folder');        // bool

Settings

// Passive mode (recommended, enabled by default after login)

$ftp->setPassive(true);



// Transfer mode

$ftp->setTransferMode(FTPClient::MODE_BINARY);

$ftp->setTransferMode(FTPClient::MODE_ASCII);



// Timeout

$ftp->setTimeout(60);



// Raw FTP command

$response = $ftp->raw('STAT');



// Server type

$type = $ftp->systemType(); // e.g. 'UNIX'

Fluent API

All methods return $this (except read operations), enabling chains:

$ftp = (new FTPClient('ftp.example.com'))

    ->login('user', 'pass')

    ->setPassive(true)

    ->chdir('/var/www')

    ->upload('/local/index.html', 'index.html')

    ->upload('/local/style.css', 'style.css')

    ->disconnect();

SFTP Client

Requires: ext-ssh2

SFTP Connection & Authentication

use Razy\SFTPClient;



$sftp = new SFTPClient(

    host: 'ssh.example.com',

    port: 22,

    timeout: 30

);



// Password authentication

$sftp->loginWithPassword('username', 'password');



// Public key authentication

$sftp->loginWithKey(

    username: 'deploy',

    publicKeyFile: '/home/user/.ssh/id_rsa.pub',

    privateKeyFile: '/home/user/.ssh/id_rsa',

    passphrase: 'optional-passphrase'

);



// SSH agent authentication

$sftp->loginWithAgent('deploy');



// Connection info

$sftp->isConnected();                    // true

$sftp->getFingerprint();                  // MD5 hex fingerprint

$sftp->getAuthMethods('username');        // ['publickey', 'password']



$sftp->disconnect();

File Operations (SFTP)

// Upload with permissions

$sftp->upload('/local/file.txt', '/remote/file.txt', permissions: 0644);



// Upload string content

$sftp->uploadString('Hello, World!', '/remote/hello.txt', permissions: 0644);



// Download

$sftp->download('/remote/file.txt', '/local/file.txt');



// Download to string

$content = $sftp->downloadString('/remote/hello.txt');



// Delete

$sftp->delete('/remote/old-file.txt');



// Rename / move

$sftp->rename('/remote/old.txt', '/remote/new.txt');



// Change permissions

$sftp->chmod('/remote/script.sh', 0755);

Directory Operations (SFTP)

// Create directory

$sftp->mkdir('/remote/new-folder', permissions: 0755);



// Create recursively

$sftp->mkdir('/remote/a/b/c', permissions: 0755, recursive: true);



// Remove directory

$sftp->rmdir('/remote/empty-folder');



// Remove recursively

$sftp->rmdirRecursive('/remote/folder-with-files');



// List files

$files = $sftp->listFiles('/remote/path');

// ['file1.txt', 'file2.jpg', 'subfolder']

Symlinks & Paths

// Create symlink

$sftp->symlink('/remote/target', '/remote/link');



// Read symlink target

$target = $sftp->readlink('/remote/link');



// Resolve real path

$real = $sftp->realpath('/remote/./path/../file.txt');

File Information (SFTP)

// Full stat

$stat = $sftp->stat('/remote/file.txt');

// ['size' => 1024, 'uid' => 1000, 'gid' => 1000, 'mode' => 33188, 'atime' => ..., 'mtime' => ...]



// Symlink stat (doesn't follow links)

$lstat = $sftp->lstat('/remote/link');



// Convenience methods

$exists = $sftp->exists('/remote/file.txt');    // bool

$isDir  = $sftp->isDir('/remote/folder');       // bool

$isFile = $sftp->isFile('/remote/file.txt');     // bool

$isLink = $sftp->isLink('/remote/link');         // bool

$size   = $sftp->size('/remote/file.txt');       // bytes

$mtime  = $sftp->lastModified('/remote/file.txt'); // Unix timestamp

Remote Commands

Execute SSH commands on the remote server:

$output = $sftp->exec('ls -la /var/www');

$output = $sftp->exec('df -h');

$output = $sftp->exec('whoami');

Logging

Both clients maintain an internal operation log:

// FTP

$ftp->upload('/local/file.txt', '/remote/file.txt');

$logs = $ftp->getLogs();

// [

//   ['operation' => 'login', 'details' => '...', 'timestamp' => '...'],

//   ['operation' => 'upload', 'details' => '...', 'timestamp' => '...'],

// ]

$ftp->clearLogs();



// SFTP

$sftp->upload('/local/file.txt', '/remote/file.txt');

$logs = $sftp->getLogs();

$sftp->clearLogs();

Error Handling

  • FTPClient throws Razy\Exception\FTPException (extends NetworkException)

  • SFTPClient throws Razy\Exception\SSHException (extends NetworkException)

use Razy\Exception\FTPException;

use Razy\Exception\SSHException;



try {

    $ftp->upload('/local/missing.txt', '/remote/file.txt');

} catch (FTPException $e) {

    echo "FTP error: " . $e->getMessage();

}



try {

    $sftp->download('/remote/nonexistent.txt', '/local/file.txt');

} catch (SSHException $e) {

    echo "SFTP error: " . $e->getMessage();

}

Both clients auto-disconnect on __destruct() → no need for explicit cleanup in normal flows.


API Reference

FTPClient

| Category | Method | Signature |

| --- | --- | --- |

| Connect | __construct | (string $host, int $port = 21, int $timeout = 30, bool $ssl = false) |

| | login | (string $username = 'anonymous', string $password = ''): static |

| | disconnect | (): static |

| | isConnected | (): bool |

| | isSecure | (): bool |

| Files | upload | (string $localPath, string $remotePath, ?int $mode = null): static |

| | download | (string $remotePath, string $localPath, ?int $mode = null): static |

| | uploadString | (string $content, string $remotePath, ?int $mode = null): static |

| | downloadString | (string $remotePath, ?int $mode = null): string |

| | delete | (string $remotePath): static |

| | rename | (string $oldPath, string $newPath): static |

| | chmod | (string $remotePath, int $mode): static |

| Dirs | pwd | (): string |

| | chdir | (string $directory): static |

| | cdup | (): static |

| | mkdir | (string $directory): static |

| | mkdirRecursive | (string $directory): static |

| | rmdir | (string $directory): static |

| | rmdirRecursive | (string $directory): static |

| | listFiles | (string $directory = '.'): array |

| | listDetails | (string $directory = '.'): array |

| Info | size | (string $remotePath): int |

| | lastModified | (string $remotePath): int |

| | exists | (string $remotePath): bool |

| | isDir | (string $remotePath): bool |

| Settings | setPassive | (bool $passive = true): static |

| | setTransferMode | (int $mode): static |

| | setTimeout | (int $seconds): static |

| | raw | (string $command): array |

| | systemType | (): string |

| Log | getLogs | (): array |

| | clearLogs | (): static |

SFTPClient

| Category | Method | Signature |

| --- | --- | --- |

| Connect | __construct | (string $host, int $port = 22, int $timeout = 30) |

| | loginWithPassword | (string $username, string $password): static |

| | loginWithKey | (string $username, string $publicKeyFile, string $privateKeyFile, ?string $passphrase = null): static |

| | loginWithAgent | (string $username): static |

| | disconnect | (): static |

| | isConnected | (): bool |

| | getFingerprint | (int $flags = ...): string |

| | getAuthMethods | (string $username): array |

| Files | upload | (string $localPath, string $remotePath, int $permissions = 0644): static |

| | download | (string $remotePath, string $localPath): static |

| | uploadString | (string $content, string $remotePath, int $permissions = 0644): static |

| | downloadString | (string $remotePath): string |

| | delete | (string $remotePath): static |

| | rename | (string $oldPath, string $newPath): static |

| | chmod | (string $remotePath, int $mode): static |

| Dirs | mkdir | (string $directory, int $permissions = 0755, bool $recursive = false): static |

| | rmdir | (string $directory): static |

| | rmdirRecursive | (string $directory): static |

| | listFiles | (string $directory = '.'): array |

| Links | symlink | (string $target, string $linkPath): static |

| | readlink | (string $linkPath): string |

| | realpath | (string $remotePath): string |

| Info | stat | (string $remotePath): array\|false |

| | lstat | (string $remotePath): array\|false |

| | exists | (string $remotePath): bool |

| | isDir | (string $remotePath): bool |

| | isFile | (string $remotePath): bool |

| | isLink | (string $remotePath): bool |

| | size | (string $remotePath): int |

| | lastModified | (string $remotePath): int |

| SSH | exec | (string $command): string |

| Log | getLogs | (): array |

| | clearLogs | (): static |

← Previous: Profiler

Worker-Lifecycle

Clone this wiki locally