phpseclib

phpseclib

  • Docs
  • API
  • Support
  • GitHub

›SSH2

Introduction

  • Why phpseclib?
  • Installation
  • Speed
  • Versioning

SSH2

  • Connecting
  • Authenticating
  • Running Commands
  • SFTP
  • Diagnosing Issues

Public Keys

  • Overview
  • RSA
  • DSA
  • Elliptic Curves
  • (EC)DH
  • Example: JWT

Symmetric Keys

  • Overview

X.509

  • X.509
  • CSR
  • SPKAC
  • CRL

Interoperability

  • Overview
  • Python
  • Java
  • JavaScript
  • Node.js
  • Go
  • Ruby
  • C#
  • C
  • PHP

SFTP

To use SFTP you'll need to use the SFTP class instead of the SSH2 class. eg.

use phpseclib3\Net\SFTP;

$sftp = new SFTP('localhost');
$sftp->login('username', 'password');

Because the SFTP class extends the SSH2 class the SFTP class has all the methods that the SSH2 class does.

Uploading Files

// puts a three-byte file named filename.remote on the SFTP server
$sftp->put('filename.remote', 'xxx');
// puts an x-byte file named filename.remote on the SFTP server,
// where x is the size of filename.local
$sftp->put('filename.remote', 'filename.local', SFTP::SOURCE_LOCAL_FILE);

The function definition for put() is as follows:

function put($remote_file, $data, $mode = SFTP::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)

Uploading strings vs. files

$sftp->put('filename.remote', 'filename.local') creates filename.remote on the remote server with 'filename.local' as the contents.

$sftp->put('filename.remote', 'filename.local', SFTP::SOURCE_LOCAL_FILE) creates filename.remote on the remote server such that the contents of it and filename.local match. ie. with SFTP::SOURCE_LOCAL_FILE it uploads a file and without it it uploads a string.

Resuming transfers

$sftp->put('filename.remote', 'xxx', SFTP::RESUME) will append 'xxx' to filename.remote.

$sftp->put('filename.remote', 'filename.local', SFTP::SOURCE_LOCAL_FILE | SFTP::RESUME_START) will append filename.remote to filename.local.

$sftp->put('filename.remote', 'filename.local', SFTP::SOURCE_LOCAL_FILE | SFTP::RESUME) will append all but the first $sftp->size('filename.remote') bytes of filename.local to filename.remote. The idea being that if your transfer is interupted you can restart it.

Positional control

$start and $local_start give you more fine grained control over this process and take precident over SFTP::RESUME when they're non-negative. ie. $start could let you write at the end of a file (like SFTP::RESUME) or in the middle of one. $local_start could let you start your reading from the end of a file (like SFTP::RESUME_START) or in the middle of one.

Downloading Files

// outputs the contents of filename.remote to the screen
echo $sftp->get('filename.remote');
// copies filename.remote to filename.local from the SFTP server
$sftp->get('filename.remote', 'filename.local');

The function definition for get() is as follows:

function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)

Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the operation.

If $local_file is an anonymous function you can stream the download real time or whatever. eg.

$sftp->get('filename.remote', function ($output) {
    echo $output;
});

Preserving the Date

If you want the uploaded or downloaded file to have the same last modified / accessed time as the original file by doing $sftp->enableDatePreservation(). $sftp->disableDatePreservation() will turn this behavior off. The default status is "off".

Directory Management

$sftp->mkdir('test'); // create directory 'test'
$sftp->chdir('test'); // open directory 'test'
echo $sftp->pwd(); // show that we're in the 'test' directory
$sftp->chdir('..'); // go back to the parent directory
$sftp->rmdir('test'); // delete the directory
// if the directory had files in it we'd need to do a recursive delete
//$sftp->delete('test');

mkdir

phpseclib's mkdir accepts the same parameters as PHP's mkdir. Here's the method definition:

public function mkdir($dir, $mode = -1, $recursive = false)

If $mode isn't specified (or if it's -1) then the operating system will most likely use the umask. If you do want to specify the permission note that it needs to be in octal. So instead of passing 777 you'd pass in 0777. The preceeding 0 is how PHP knows to treat an integer as an octal number as opposed to a decimal number.

$recursive allows the creation of nested directories specified in the pathname.

Directory Listing

print_r($sftp->nlist()); // == $sftp->nlist('.')
print_r($sftp->rawlist()); // == $sftp->rawlist('.')

$sftp->nlist():

  • 0
    • uploads
  • 1
    • ..
  • 2
    • .
  • 3
    • .profile
  • 4
    • .bashrc
  • 5
    • .bash_logout

$sftp->rawlist():

  • uploads
    • size
      • 4096
    • uid
      • 1001
    • gid
      • 1002
    • mode
      • 16877
    • type
      • 2
    • atime
      • 1338498490
    • mtime
      • 1338497853
    • filename
      • uploads
  • ..
    • size
      • 4096
    • uid
      • 0
    • gid
      • 0
    • mode
      • 16877
    • type
      • 2
    • atime
      • 1338499576
    • mtime
      • 1338497853
    • filename
      • ..
  • .
    • size
      • 4096
    • uid
      • 0
    • gid
      • 0
    • mode
      • 16877
    • type
      • 2
    • atime
      • 1338499576
    • mtime
      • 1338497853
    • filename
      • .
  • .profile
    • size
      • 675
    • uid
      • 1012
    • gid
      • 1013
    • mode
      • 33188
    • type
      • 1
    • atime
      • 1338497357
    • mtime
      • 1338497357
    • filename
      • .profile
  • bashrc
    • size
      • 3353
    • uid
      • 1012
    • gid
      • 1013
    • mode
      • 33188
    • type
      • 1
    • atime
      • 1338497357
    • mtime
      • 1338497357
    • filename
      • bashrc
  • .bash_logout
    • size
      • 270
    • uid
      • 1012
    • gid
      • 1013
    • mode
      • 33188
    • type
      • 1
    • atime
      • 1338497357
    • mtime
      • 1338497357
    • filename
      • .bash_logout
The type index corresponds to one of the following named constants:
  • NET_SFTP_TYPE_REGULAR (1)
  • NET_SFTP_TYPE_DIRECTORY (2)
  • NET_SFTP_TYPE_SYMLINK (3)
  • NET_SFTP_TYPE_SPECIAL (4)
  • NET_SFTP_TYPE_UNKNOWN (5)
  • NET_SFTP_TYPE_SOCKET (6)
  • NET_SFTP_TYPE_CHAR_DEVICE (7)
  • NET_SFTP_TYPE_FIFO (8)

Both nlist and rawlist accept an optional second parameter - $recursive - that, if set to bool(true), will return a list of all the files in the specified directory and all subdirectories contained therein (and all subdirectories contained within those subdirectories, etc).

Setting List Order

Directory output is not sorted by default.

If sorting is enabled directories and files will be sorted independently with directories appearing before files in the resultant array that is returned.

Any parameter returned by stat is a valid sort parameter for this function. Filename comparisons are case insensitive.

Examples:

$sftp->setListOrder('filename', SORT_ASC);
$sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC);
// separates directories from files but doesn't do any sorting beyond that:
$sftp->setListOrder(true);
// don't do any sort of sorting:
$sftp->setListOrder();

Empty Directories

In 1.0 / 2.0 $sftp->nlist() doesn't return empty directories in recursive mode. Here's the sample output you'd get back from nlist():

dir/file.ext
file.ext
.
..

If dir was an empty directory you wouldn't see it because . and .. were only included in the root directory - not in subdirectories. Here's the output you'll get in 3.0:

dir/.
dir/..
dir/file.ext
file.ext
.
..

Permissions

If you expand the $sftp->rawlist() output in the earlier example you'll see a key: mode. For .profile that value is set to 33188. What does that mean? To understand let's first convert that to binary: chunk_split(decbin(33188), 4, ' '). That gives us the following:

1000 0001 1010 0100

Each bit corresponds to the following:

16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
File Type Special Modes Owner Group Others
r w x r w x r w x

This first four bits mean that it's a regular file (NET_SFTP_TYPE_REGULAR). The last nine bits mean that the file's permissions, in numeric notation, are 644 or, in symbolic notation, -rw-r--r-. Here are a few scenarios;

Here are a few scenarios:

  • "Others" permissions: 33188 & 07 == 4 or 100.
  • "Owners" permissions: (33188 >> 6) & 07 == 6 or 110.
  • Test to see if g+r is set: (33188 >> 5) & 1

The special modes are setuid, setgid and sticky.

You can learn more about the file type by looking at the implementation of parseMode() in Net/SFTP.php or by looking at the "mode file types" table in PHP: stat - Manual

For further reading see PHP: fileperms - Manual

File Attributes

$sftp->chmod(0777, 'filename.remote');
//$sftp->chmod(0777, 'dirname.remote', true); // recursively change permissions on a directory
// has the same syntax as http://php.net/touch
$sftp->touch('filename.remote');
$sftp->chown('filename.remote', $uid);
//$sftp->chown('filename.remote', $uid, true); // recursively change the owner
$sftp->chgrp('filename.remote', $gid);
//$sftp->chgrp('filename.remote', $gid, true); // recursively change the group
$sftp->truncate('filename.remote', $size);

File Information

print_r($sftp->stat('filename.remote'));
print_r($sftp->lstat('filename.remote'));
echo $sftp->fileatime('filename.remote') . "\n"; // last accessed time
echo $sftp->filemtime('filename.remote') . "\n"; // last modified time
echo $sftp->fileperms('filename.remote') . "\n";
echo $sftp->fileowner('filename.remote') . "\n";
echo $sftp->filegroup('filename.remote') . "\n";
echo $sftp->filesize('filename.remote') . "\n";
echo $sftp->filetype('filename.remote') . "\n";
echo $sftp->file_exists('filename.remote') ? "exists\n" : "doesn't exist\n";
echo $sftp->is_dir('filename.remote') ? "is dir\n" : "is not dir\n";
echo $sftp->is_file('filename.remote') ? "is file\n" : "is not file\n";
echo $sftp->is_readable('filename.remote') ? "is readable\n" : "is not readable\n";
echo $sftp->is_writeable('filename.remote') ? "is writeable\n" : "is not writeable\n";
// $sftp->is_writable() works as well

stat() and lstat() return associative arrays with misc information about the files. lstat() and stat() are identical with the caveat that when the file in question is a symbolic link the information returned refers to the link itself and not the file (or directory) being linked to.

stat() example:

  • size
    • 856
  • uid
    • 1000
  • gid
    • 1000
  • mode
    • 33188
  • type
    • 1
  • atime
    • 1580971886
  • mtime
    • 1555785478

See Permissions for more information on mode or fileperms().

filetype() returns string translations of what stat()['type'] does.

is_readable() and is_writeable() only work on files. They work by actually opening up a file for reading or writing and then closing the file immediately thereafter. The same technique does not work for directories.

As of phpseclib v3.0.20 filesize() accepts an optional second parameter - $recursive. By default this parameter is false but if you set it to true it'll recurse through a directory and return the size of all the files in that directory and it's subdirectories.

Delete and Rename

$sftp->delete('filename.remote'); // deletes directories recursively
// non-recursive delete
$sftp->delete('dirname.remote', false);
// rename() return false if newname.remote already exists
$sftp->rename('filename.remote', 'newname.remote');

Changing SFTP Versions

There are seven different versions of SFTP that are defined (v0 through v6). OpenSSH only supports one version (v3) but some SFTP servers (eg. Bitvise SSH Server) can be configured to support all versions.

SFTP servers that support multiple versions will have a default version (usually v3) and will specifiy what other SFTP versions they support through the use of an extension.

If you're running phpseclib ≥ 3.0.11 you can set the preferred version by calling setPreferredVersion(6). You can see what version was ultimately negotiated by calling getNegotiatedVersion(). You can see what versions the server supports by calling getSupportedVersions(). When multiple verisons of SFTP are supported here's what the output of this method will look like:

  • version
    • 3
  • extensions
    • 3,4,5,6

If only one version of SFTP is supported then the extensions key will most likely not be present.

Stream Wrapper

Files can alternatively be accessed with a stream wrapper:

use phpseclib3\Net\SFTP\Stream;
use phpseclib3\Net\SFTP;

Stream::register();

$fp = fopen('sftp://user:pass@127.0.0.1:22/home/user/filename.ext', 'r'); // the port number is optional

$temp = '';
while (!feof($fp)) {
    $temp.= fread($fp, 1024);
}
fclose($fp);

Customizing the Protocol

You can change the protocol (which defaults to sftp://) thusly:

Stream::register('ssh2.sftp');

$fp = fopen("ssh2.sftp://$sftp/home/vagrant/1mb", 'r');

Non-Password Authentication

$sftp = new SFTP('127.0.0.1', 22); // the port number is optional
$sftp->login('username', $key);

$fp = fopen("sftp://$sftp/home/user/filename.ext", 'r');

$temp = '';
while (!feof($fp)) {
    $temp.= fread($fp, 1024);
}
fclose($fp);

With Stream Context

$protocol = 'sftp';

Stream::register($protocol);

$context = [
    $protocol => ['sftp' => $sftp]
];
$context = stream_context_create($context);

$fp = fopen($protocol . '://dummy.com/home/user/filename.txt', 'r', false, $context);

Context options

NameUsage
sessionPreconnected sftp resource to be reused
sftpPreconnected sftp resource to be reused
usernameUsername to connect as
passwordPassword to use with password authentication
privkeyPublic key resource to be used

(inspired by PHP: ssh2:// - Manual)

← Running CommandsDiagnosing Issues →
  • Uploading Files
    • Uploading strings vs. files
    • Resuming transfers
    • Positional control
  • Downloading Files
  • Preserving the Date
  • Directory Management
    • mkdir
  • Directory Listing
    • Setting List Order
    • Empty Directories
    • Permissions
  • File Attributes
  • File Information
  • Delete and Rename
  • Changing SFTP Versions
  • Stream Wrapper
    • Customizing the Protocol
    • Non-Password Authentication
    • With Stream Context
phpseclib
Docs
IntroductionSSH2 / SFTPPublic Key CryptoSymmetric Key CryptoX.509 / CSR / SPKAC / CRLInteroperability
Support
Docs (1.0 / 2.0)Stack OverflowGitHubStar
Sponsor
PatreonGitHubPayPal
Copyright © 2025 Jim Wigginton