IT系おじさんのチラシの裏
2018年10月~
当サイトの記事にはアフィリエイト広告のリンクが含まれる場合があります

Windows版PHPでSFTP接続するにはphp_ssh2.dllが必要

Web運営のメンテナンス作業(ローカルへのバックアップ作業とか)を自動化する方法について4回にわけて記事にしてきました。

  1. WSHでブラウザを自動操作する
  2. PHPでファイルとDB(PostgreSQL)をバックアップ
  3. レンタルサーバーでバックアップしたファイルを自動DLする
  4. WindowsでPHPを使えるようにする

1.~3.については直接的な解決策の提示でしたが、4.についてはこの記事の前準備みたいなもので、レンタルサーバーを借りてWeb運営しつつ、サーバー上のファイルをバックアップしてDLするなら、受け取り側はWindowsが多いよねってことで、PHPをWindowsで使う準備の話しでした。

そしてその準備が整ったはずなので、今度こそPHPでSFTP接続する具体例です。

そんなことしなくても3.で紹介したpsftp.exeを使えば良いんですけどね。

……ロマンです。

PHPでSFTP接続するサンプル

コード自体はとっても簡単です。

PHPサンプル

<?php
//設定
$host = "e1.valueserver.jp";	//接続先サーバー
$user = 'ユーザー名';				//ユーザー名
$pass = 'パスワード';			//パスワード
$target_path = '/virtual/ユーザー名/backup/';
$target_file = 'backup.zip';

//SSHで接続
$conn = ssh2_connect($host, 22);
//認証
if(!ssh2_auth_password($conn, $user, $pass)) {
    echo 'Login Failed';
    exit();
}
//SFTP接続を行う
$sftp = ssh2_sftp($conn);
//リモートファイルを読み込み
$data = file_get_contents('ssh2.sftp://'.$sftp.$target_path.$target_file);
//ローカルへ書き出す
file_put_contents($target_file, $data);
//サーバーから切断
ssh2_disconnect($conn);
?>

例によって、$host、$user、$pass、$target_path、$target_fileは借りているサーバーの種類によって適宜変更してもらうとして、SFTP接続の部分は実に単純です。
(簡略化のためエラー処理も認証失敗くらいしかチェックしていないので要修正)

PHPでのSFTP接続の流れ

  1. ssh2_connect関数でSSHサーバーへ接続
  2. ssh2_sftp関数でSFTPサブシステムを初期化
  3. file_get_contents関数で対象ファイルをメモリに読み込み
  4. file_put_contents関数でメモリに読み込んだデータをローカルファイルに書き出す
  5. ssh2_disconnect関数でSSHサーバーから切断
※ssh2の2はバージョン2の2です。1には脆弱性があるので2しか使わないと考えて構いません。

ここでポイントなのはssh2_sftp関数で、この関数が返すのは "Resource id #ほにゃらら" とかの文字列なのですが、これを使うことで、

ssh2.sftp://[SFTPのリソースID]/virtual/[ユーザー名]/backup/backup.zip

といったURL表記でSFTP上のファイルにアクセスできるようになります。おかげでfopen等のファイル操作系の関数がそのまま使いたい放題。

……とまぁ、コード自体は簡単なんですけど、はじめての人は準備で悩むかも知れません。

SSH関数を使うならphp.iniの書き換えが必要

PHP7.2に同梱されている拡張機能、例えばgd2(画像処理につかう拡張機能)とかなら、c:\php\php.ini に書かれている ;extension=gd2 の頭のセミコロンを外すだけで使えます。これは c:\php\ext\ の下に php_gd2.dll が存在するからなんですね。

同様にssh2を使うためには extension=ssh2 の記述が必要なのですが、デフォルトでは書かれていないため、下記のように追記する必要があります。

php.iniにssh2を追記する

ただ、これだけで上述のスクリプトを実行してもエラーが発生して動作しません。

PHP Warning: PHP Startup: Unable to load dynamic library 'ssh2'

'ssh2'というダイナミックライブラリがロードできませんでしたよ、と言っているんですね。そりゃロードできません。だって、ないもの。

Windows上でSSH関数を使うならphp_ssh2.dllも必要

件のssh2のダイナミックライブラリがどこにあるのかっていうと、ここです。
https://pecl.php.net/package/ssh2

Available Releasesの下のDownloadの下に ssh2-1.1.2.tgz とかありますが、それはソースなのでLinux用です。ありがたいことにその右横にDLLと書かれたリンクがあるのでそちらをクリックします。

すると、このページが開くのですが……
https://pecl.php.net/package/ssh2/1.1.2/windows

開くの……ですが……
PHP7.2用のSSH2がない

PHP 7.2用のDLLがない。なぜ…。PHP 7.2がリリースされてからそろそろ1年近く経つというのになんでか7.2用のDLLがありません。

まぁ、7.1用のでも動くのかな?と淡い期待を込めて試してみたのですが……

PHP Warning: PHP Startup: ssh2: Unable to initialize module Module compiled with module API=20160303 PHP compiled with module API=20170718

(コンパイルされた)バージョンが違うよっ!と出て実行できません。

2019年1月追記:
この記事を書いた2ヶ月後に確認したところ、PHP 7.2用のphp_ssh2.dllも無事本家にアップロードされていました。よかったよかった。

2020年6月追記:
PHP7.3とPHP7.4用のphp_ssh2.dllはこちらからどうぞ。
https://pecl.php.net/package/ssh2/1.2/windows

えー…… Visual Cインストールしてソースからコンパイルしなきゃいけないの?

少し検索すればWindowsでPHP拡張モジュールをコンパイルする方法は出てきますが、けっこう面倒くさい。

もう1年も経つんだし、誰かしらコンパイルしてphp_ssh2.dllを公開している人いるんじゃないかなーと、探してみたら、いましたよ、神が。

stackoverflowの掲示板
https://stackoverflow.com/questions/51157868/missing-php-ssh2-dll-extension-for-php-7-2-windows

神がアップロードした php_ssh2.dll
https://github.com/nufue/pecl-ssh2-windows

本家php.netにないなら、自分でコンパイルするのが一番安全なのでしょうけれど、ぼくは面倒なのでありがたくこのDLLを使わせて頂くことにしました。

先程、php.ini に extension=ssh2 という記述は完了していると思うので、今度は php_ssh2.dll を c:\php\ext ディレクトリへコピーするだけです。

ちなみに、Non Thread Safeの場合、php_ssh2_nts.dll というファイル名になっているので、これを php_ssh2.dll にリネームしてください。

大きいファイルをDLする場合はfreadを使おう

先述のPHPサンプルでは file_get_contents を使ってZIPファイルをDLしましたが、PHPが使えるメモリは限られていますし、そのサイズを超えるファイルを読み込もうとすればエラーになります。

また、ひとつのファイルだけではなく複数、しかも拡張子がzipのファイルだけDLしたい、という要望もあると思いますので、PHPサンプルを書き換えてみました。

PHPサンプル

<?php
//設定
$host = 'e1.valueserver.jp';    //接続先サーバー
$user = 'ユーザー名';                //ユーザー名
$pass = 'パスワード';            //パスワード
$target_path = '/virtual/ユーザー名/backup/';
$download_path = 'c:/test/';

//SSHで接続
$conn = ssh2_connect($host, 22);
//認証
if(!ssh2_auth_password($conn, $user, $pass)) {
    echo 'Login Failed';
    exit();
}
//SFTP接続を行う
$sftp = ssh2_sftp($conn);
//リモートのディレクトリを開く
$handle = opendir('ssh2.sftp://'.$sftp.$target_path);
while (false !== ($entry = readdir($handle))) {
    //zipファイルだけダウンロード
    if (substr($entry, -4) == '.zip') {
        downloadFile($entry, 'ssh2.sftp://'.$sftp.$target_path, $download_path);
    }
}
closedir($handle);
//サーバーから切断
ssh2_disconnect($conn);

/************************************************
 *ファイルをダウンロードする
 ************************************************/
function downloadFile($filename, $path1, $path2)
{
    $fp1 = fopen($path1.$filename, 'r');
    $fp2 = fopen($path2.$filename, 'w');
    while ($buff = fread($fp1, 8192 * 10)) {
        fwrite($fp2, $buff);
    }
    fclose($fp1);
    fclose($fp2);
}
?>

PHPでのSFTP接続の流れ(メイン部分)

  1. ssh2_connect関数でSSHサーバーへ接続
  2. ssh2_sftp関数でSFTPサブシステムを初期化
  3. opendir/readdir関数で対象ディレクトリのファイル名を順番に取得
  4. ファイル名の末尾が '.zip' に該当するファイルだけダウンロード処理(次項)を行う
  5. ssh2_disconnect関数でSSHサーバーから切断

PHPでのファイルダウンロード処理の概要

  1. fopen関数にSFTP用のURLを渡してリモートのファイルを開く
  2. fopen関数を使い、DL対象のファイル名と同名の空のローカルファイルを作成
  3. fread関数で8192×10バイトずつリモートファイルを読み込む
  4. fread関数で読み込んだデータをfwrite関数でローカルファイルに書き込む
  5. fread関数で読み込むデータがなくなったら戻る

極単純なfopen~fread~fwriteの組み合わせです。

ssh2の拡張機能でfopen系の関数をラッパーしてくれるので、普通のファイル操作と変わらずにコーディングできて便利ですね。

尚、fread関数の読み込みバッファー8192×10バイトは適当です。デフォルトが8192と少なすぎるので10倍くらい指定しておくか、という適当っぷり。バッファーが少ないとその分DLも遅くなるので、メモリに余裕があるなら100MB分くらい指定しても良いかも。

まとめ

PHPでSFTP接続するサンプルを書いておしまいのハズが、DLLのところでちょっと躓きました。

検索すれば情報があふれだす世の中ですが、PHPみたいにバージョンアップが早く、仕様もどんどん変更されていく技術だと検索で見つかった情報もどこまで正しいかわかりません。php_ssh2.dllの件を探しているときも、regsvrでDLLを登録しないといけないという情報があったり(実際は不要)別の場所からlibssh2.dllを持ってこなければいけないと書いてあったり(実際はこれも不要、というかphp7.2には同梱されている)、情報があふれすぎて正しいものを見つけるのが難しくなってきているようです。

2018年11月現在においては、Windows版PHPでSFTP接続をするためには

  • php.iniにextension=ssh2を追記
  • php_ssh2.dll を c:\php\ext へコピー
と、たったこれだけのことですが、それも将来はどうかわかりません。

ここを読んでいるあなたが2018年11月よりずっと未来の人で、「なんだよ、この古い情報!」とがっかりされていないことを祈ります(*´ω`*)

関連記事

簡単にWindowsでPHPを使えるようにする手順

備忘録も兼ねてWeb運営のメンテナンス(バックアップ)を自動化する方法について3回に渡り記事にしてきました。 WSHでブラウザを自動操作する PHPでファイルとDB(PostgreSQ

PHPでファイルとDB(PostgreSQL)をバックアップするサンプル

レンタルサーバーでも自動バックアップのサービスを提供しているところも増えてきましたが、安価なプランでは使えない場合もあります。 (実際、このブログで使っているバリューサーバー ではビジネスプランで

コメント

  • 当方php7.3ユーザー、本家に7.3のphp_ssh2.dllがなかったのですが、灰色部分のリンク先に7.3用が用意されていて助かりました。
    この記事に情報を繋いで頂いたおかげです。
    [返信]
    • お役に立てて良かったです(*´ω`*)
      7.2のときと同様、そのうち7.3も本家にアップされると思うのですが、ちと遅いのですよねー。
      [返信]

新しいコメントを投稿する

[新規投稿]
 
TOP