PHPプログラミング言語を使った「SFTPへの接続」、「ファイルのリストアップ」、「アップロード」、「ダウンロード」のやり方をここで習得しましょう。
SFTP は、データやファイルの安全な転送や共有のための標準的で安全なプロトコルです。どのような場合であれ、プログラムで SFTP サーバーに接続するのは簡単ではありません。次の投稿では、本格的な PHP ベースの SFTP クライアントを徐々に構築していき、このガイドの終盤には自分で全て出来るようになるでしょう。
必要事項
まずは、接続するための SFTP サーバが必要です。もしお持ちでなければ、SFTP To Goで30秒もかからずSFTPエンドポイントの取得ができます。

PHP には最初から SFTP ライブラリがバンドルされていないため、必要なライブラリ phpseclib をインストールする必要があります。
$ composer require phpseclib/phpseclib
12 factor-app クラウド開発手法に従って、次のcomposer.json のようにアプリの依存関係を明確に定義する必要があります。
{
"require": {
"phpseclib/phpseclib": "^3.0"
}
}
本当に面白いのは今からです!
SFTP接続の作成と削除
まず、始めは新しいクラスの作成からです。ここにはSFTPクライアントに必要な機能が入っています。最初に追加するパブリックメソッドは、パスワード認証またはキー認証のいずれかを使ってサーバーに接続し、既存のセッションを切断する手段を提供します。
<?php
class SFTPClient
{
private $sftp;
public function __construct($host, $port=22)
{
$this->sftp = new SFTP($host, $port);
if (!$this->sftp->isConnected())
throw new Exception("Failed to connect to ${host} on port ${port}.");
}
// Login with user and password
public function auth_password($username, $password)
{
if (!$this->sftp->login($username, $password))
throw new Exception("Failed to authenticate with username $username " .
"and password.");
}
// Login with SSH agent
public function auth_agent($username)
{
if (!$this->sftp->login($username))
throw new Exception("Failed to authenticate with username $username " .
"and public key.");
}
// Disconnect session
public function disconnect()
{
$this->sftp->disconnect();
}
}
?>
新しいメソッドを使うには、メイン関数でクラスをインスタンス化してauth_password
関数の呼び出しによって接続を開始し、直ちに disconnect
関数で接続を閉じます。次のようなURI 形式の環境変数 SFTPTOGO_URL
を使って、SFTP サーバーへの接続に必要なすべての情報を取得します:sftp://user:password@host
。該当変数は関数内で解析され、ユーザー名、パスワード、ホスト、オプションのポートが抽出されます。
<?php
function main()
{
$raw_url = getenv('SFTPTOGO_URL');
// Parse URL
$parsed_url = parse_url($raw_url);
if ($parsed_url === false)
{
fwrite(STDERR, "Failed to parse SFTP To Go URL.\n");
exit(1);
}
// Get user name and password
$user = isset($parsed_url['user']) ? $parsed_url['user'] : null;
$pass = isset($parsed_url['pass']) ? $parsed_url['pass'] : null;
// Parse Host and Port
$host = isset($parsed_url['host']) ? $parsed_url['host'] : null;
// Port is always 22
$port = isset($parsed_url['port']) ? $parsed_url['port'] : 22;
fwrite(STDOUT, "Connecting to [${host}] ...\n");
}
main();
?>
ファイルの一覧表示
接続がうまくいったら、クラスにリモート SFTP サーバー上のファイルをリストアップするために使う関数を追加します。listFiles
関数は $remote_dir
引数をとり、 ファイル名の配列とそのサイズ、更新時刻を返します。この関数を呼び出すには、単にパスを渡して (.
でカレントディレクトリの内容が全て閲覧できる)、 取得した配列を処理します。
<?php
// List remote directory files
function listFiles($remote_dir) {
$tempArray = array();
fwrite(STDOUT, "Listing [${remote_dir}] ...\n\n");
$files = $this->sftp->nlist($remote_dir);
foreach ($files as $file) {
$filetype = $this->sftp->is_dir($file) ? 'dir' : 'file';
$modTime = '';
$size = sprintf("%.2f", $this->sftp->size($file));
if ($filetype == "dir") {
$file .= '/';
$modTime = '';
$size = "PRE";
}
$tempArray[] = $file;
printf("%19s %12s %s\n", $modTime, $size, $file);
}
return $tempArray;
}
?>
ファイルのアップロード
では、ファイルをアップロードしましょう。uploadFile
関数を使って、【送信元のローカルファイルのパス】と【送信先のリモートパス】の2つの引数を渡します。そして関数の呼び出しは次のようになります:$client->uploadFile("./local.txt", "./remote.txt");
<?php
// Upload local file to remote file
public function uploadFile($local_file, $remote_file)
{
fwrite(STDOUT, "Uploading [${local_file}] to [${remote_file}] ...\n");
$data_to_send = file_get_contents($local_file);
if ($data_to_send === false)
throw new Exception("Could not open local file: $local_file.");
if (!$this->sftp->put($remote_file, $data_to_send))
throw new Exception("Could not send data from file: $local_file.");
}
?>
ファイルのダウンロード
最後に大事な一点: ファイルをダウンロードするには、downloadFile
関数を使いましょう。リモートファイルへのパスと、ダウンロードしたファイルを保存するローカルパスを関数に渡します。この関数は、$client->downloadFile("./remote.txt", "./download.txt");
のように呼び出すことになります。
<?php
// Download remote file to local file
public function downloadFile($remote_file, $local_file)
{
fwrite(STDOUT, "Downloading [${remote_file}] to [${local_file}] ...\n");
$contents = $this->sftp->get($remote_file);
if ($contents === false)
throw new Exception("Could not open remote file: $remote_file.");
file_put_contents($local_file, $contents);
}
?>

全体像
以上です!このプログラムを最初から最後まで通して実行したい場合は、以下のコードをコピーしてmain.php
として保存してください:
<?php
use phpseclib3\Net\SFTP;
class SFTPClient
{
private $sftp;
public function __construct($host, $port=22)
{
$this->sftp = new SFTP($host, $port);
if (!$this->sftp->isConnected())
throw new Exception("Failed to connect to ${host} on port ${port}.");
}
// Login with user and password
public function auth_password($username, $password)
{
if (!$this->sftp->login($username, $password))
throw new Exception("Failed to authenticate with username $username " .
"and password.");
}
// Login with SSH agent
public function auth_agent($username)
{
if (!$this->sftp->login($username))
throw new Exception("Failed to authenticate with username $username " .
"and public key.");
}
// Disconnect session
public function disconnect()
{
$this->sftp->disconnect();
}
// List remote directory files
function listFiles($remote_dir) {
$tempArray = array();
fwrite(STDOUT, "Listing [${remote_dir}] ...\n\n");
$files = $this->sftp->nlist($remote_dir);
foreach ($files as $file) {
$filetype = $this->sftp->is_dir($file) ? 'dir' : 'file';
$modTime = '';
$size = sprintf("%.2f", $this->sftp->size($file));
if ($filetype == "dir") {
$file .= '/';
$modTime = '';
$size = "PRE";
}
$tempArray[] = $file;
printf("%19s %12s %s\n", $modTime, $size, $file);
}
return $tempArray;
}
// Upload local file to remote file
public function uploadFile($local_file, $remote_file)
{
fwrite(STDOUT, "Uploading [${local_file}] to [${remote_file}] ...\n");
$data_to_send = file_get_contents($local_file);
if ($data_to_send === false)
throw new Exception("Could not open local file: $local_file.");
if (!$this->sftp->put($remote_file, $data_to_send))
throw new Exception("Could not send data from file: $local_file.");
}
// Download remote file to local file
public function downloadFile($remote_file, $local_file)
{
fwrite(STDOUT, "Downloading [${remote_file}] to [${local_file}] ...\n");
$contents = $this->sftp->get($remote_file);
if ($contents === false)
throw new Exception("Could not open remote file: $remote_file.");
file_put_contents($local_file, $contents);
}
// Delete remote file
public function deleteFile($remote_file){
fwrite(STDOUT, "Deleting [${remote_file}] ...\n");
$this->sftp->delete($remote_file);
}
}
function main()
{
$raw_url = getenv('SFTPTOGO_URL');
// Parse URL
$parsed_url = parse_url($raw_url);
if ($parsed_url === false)
{
fwrite(STDERR, "Failed to parse SFTP To Go URL.\n");
exit(1);
}
// Get user name and password
$user = isset($parsed_url['user']) ? $parsed_url['user'] : null;
$pass = isset($parsed_url['pass']) ? $parsed_url['pass'] : null;
// Parse Host and Port
$host = isset($parsed_url['host']) ? $parsed_url['host'] : null;
// Port is always 22
$port = isset($parsed_url['port']) ? $parsed_url['port'] : 22;
fwrite(STDOUT, "Connecting to [${host}] ...\n");
try
{
$client = new SFTPClient($host, $port);
$client->auth_password($user, $pass);
//*
//* List working directory files
//*
$client->listFiles(".");
//*
//* Upload local file to remote file
//*
$client->uploadFile("./local.txt", "./remote.txt");
//*
//* Download remote file to local file
//*
$client->downloadFile("./remote.txt", "./download.txt");
//*
//* Delete remote file
//*
$client->deleteFile("./remote.txt");
}
catch (Exception $e)
{
echo $e->getMessage() . "\n";
}
}
main();
?>
コードの実行には、下記のコマンドを使いましょう:
php main.php
また、Githubで我々のリポジトリをクローンしたり、他のコードサンプルをチェックアウトすることもできます。