-
Notifications
You must be signed in to change notification settings - Fork 0
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.
Requires: ext-ftp
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();// 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);// 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', ...], ...]$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// 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'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();Requires: ext-ssh2
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();// 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);// 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']// 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');// 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 timestampExecute SSH commands on the remote server:
$output = $sftp->exec('ls -la /var/www');
$output = $sftp->exec('df -h');
$output = $sftp->exec('whoami');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();-
FTPClient throws
Razy\Exception\FTPException(extendsNetworkException) -
SFTPClient throws
Razy\Exception\SSHException(extendsNetworkException)
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.
| 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 |
| 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 |