标题:【PHP小课堂】一起学习PHP中SSH相关扩展的使用
一起学习PHP中SSH相关扩展的使用
对于 PHP 来说,很少有用它来做运维相关管理系统的,不过,这并不代表我们 PHP 就不能远程管理服务器了。其实,也是有直接进行远程操作相关的扩展工具供我们使用的,就像我们今天要学习的这个 SSH 扩展。扩展的安装就是正常的 PHP 安装,使用的是 ssh2 这个扩展包,具体的安装过程就不详细说了,直接来看如何使用吧。
连接远程 SSH 服务器
连接过程非常简单,建立连接,然后登录就可以了。
$conn = ssh2_connect('192.168.56.106');
var_dump(ssh2_auth_password($conn, 'root', '123456')); // bool(true)
使用 ssh2_connect() 就可以建立连接并且获得连接句柄。这里我们是本地建立的一台虚拟机,所以直接就使用 root 而且密码也非常简单,大家操作线上服务器时可不能这么任性。而且如果真的需要操作线上真实环境的主机的话,更建议使用密钥的方式来连接,而不是像我们这样直接用户名密码的方式。关于密钥方式也有很多函数可以供大家使用,大家可以自行了解一下。
执行远程命令
连接服务器成功后,最主要的就是能够执行各种操作命令,这个也是我们要使用 ssh2 这个扩展的核心。当然,目的也是为了要进行远程服务器的操控管理。我们有两种执行命令的方式。
单条语句执行
$stream = ssh2_exec($conn, "ls -l /");
$dio_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
$err_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
stream_set_blocking($dio_stream, true);
stream_set_blocking($err_stream, true);
echo stream_get_contents($dio_stream);
echo stream_get_contents($err_stream);
// total 152
// drwxrwx--- 1 root vboxsf 131072 Dec 31 1979 adata_hd330
// lrwxrwxrwx. 1 root root 7 May 10 2019 bin -> usr/bin
// dr-xr-xr-x. 6 root root 4096 Jan 29 01:54 boot
// drwxr-xr-x 20 root root 3080 Mar 4 19:42 dev
// drwxr-xr-x. 103 root root 8192 Mar 4 19:42 etc
// drwxr-xr-x. 3 root root 23 Mar 4 20:33 home
// lrwxrwxrwx. 1 root root 7 May 10 2019 lib -> usr/lib
// lrwxrwxrwx. 1 root root 9 May 10 2019 lib64 -> usr/lib64
// drwxr-xr-x. 3 root root 19 Jan 29 01:40 media
// drwxr-xr-x. 2 root root 6 May 10 2019 mnt
// drwxr-xr-x. 3 root root 39 Jan 29 02:07 opt
// dr-xr-xr-x 118 root root 0 Mar 4 19:42 proc
// dr-xr-x---. 15 root root 4096 Feb 25 04:58 root
// drwxr-xr-x 32 root root 940 Mar 4 19:43 run
// lrwxrwxrwx. 1 root root 8 May 10 2019 sbin -> usr/sbin
// drwxr-xr-x. 2 root root 6 May 10 2019 srv
// dr-xr-xr-x 13 root root 0 Mar 4 19:41 sys
// drwxrwxrwt. 5 root root 118 Mar 4 20:36 tmp
// drwxr-xr-x. 12 root root 144 Mar 16 2020 usr
// drwxr-xr-x. 21 root root 4096 Mar 16 2020 var
fclose($stream);
对于单独的一条语句来说,我们可以使用 ssh2_exec() 这个函数来直接执行这条命令。它返回的结果是一个流,所以我们需要通过流的方式来读取返回的内容。在这里,我们就是简单地查看一下根目录下的内容。这块的操作非常简单,不过需要注意的是,如果返回的内容非常多的话,就不要使用 stream_get_contents() 了,它的返回大小是有限制的,我们可以使用 fgets() 这类的文件流相关函数来遍历读取。具体内容大家可以查阅官方文档,其中有不少 Notes 都会讲到这个问题。
多条语句批量执行
有时候,我们想一次执行多条命令,这时就可以使用另外一种方式来进行操作。
$shell = ssh2_shell($conn, 'xterm');
fwrite($shell, "mkdir /home/shelltest/;".PHP_EOL);
fwrite($shell, "cd /home;".PHP_EOL);
fwrite($shell, "ls -l /home;". PHP_EOL);
sleep(1);
echo stream_get_contents($shell);
// Activate the web console with: systemctl enable --now cockpit.socket
// Last login: Thu Mar 4 20:36:36 2021 from 192.168.56.102
// mkdir /home/shelltest/;
// cd /home;
// ls -l /home;
// [root@localhost ~]# mkdir /home/shelltest/;
// [root@localhost ~]# cd /home;
// [root@localhost home]# ls -l /home;
// total 0
// drwxr-xr-x 2 root root 20 Mar 4 20:36 shelltest
fclose($shell);
使用 ssh2_shell() 可以看作是打开了一个可以写入的流句柄,然后我们使用 fwrite() 向这个流中写入命令。在这里为什么要 sleep() 一下呢?其实这个流的写入操作并不是同步的,所以如果不加一个暂停的话,可能 PHP 就直接执行过去了,而命令并没有正常地发送完成就中断了。这个是需要注意的地方。同样,我们可以使用 stream_get_contents() 或者其它读取流的方式来获得执行的结果。
在这段代码中,我们建立了一个目录,然后进入它的上级 home 目录中,最后返回目录里面的内容信息。从打印的结果还能够看出,它会输出整个登录后的信息,就像我们真的打开了一个 ssh shell 工具一样。
文件传输
除了命令的操控之外,SSH 中另外一个非常重要的能力就是可以实现 sftp 以及 scp 之类的文件传输的功能,这些功能在 ssh2 扩展中也是支持的。
sftp 上传下载
$sftp = ssh2_sftp($conn);
ssh2_sftp_mkdir($sftp, '/tmp/test/');
copy('./1.txt', "ssh2.sftp://{$sftp}/tmp/test/11.txt");
$stream = ssh2_exec($conn, "ls -l /tmp/test/");
$dio_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
$err_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
stream_set_blocking($dio_stream, true);
stream_set_blocking($err_stream, true);
echo stream_get_contents($dio_stream);
echo stream_get_contents($err_stream);
// total 1
// -rw-r--r-- 1 root root 73 Mar 4 20:37 11.txt
fclose($stream);
echo file_get_contents("ssh2.sftp://{$sftp}/tmp/test/11.txt"); // 123123123123123123123123123123123123123123123123123123123123123123123123
首先,我们使用 ssh2_sftp() 函数获取一个 sftp 句柄。然后通过 ssh2_sftp_mkdir() 创建了一个目录。接着,通过普通的 PHP 函数配合 ssh2.sftp 这个伪协议,就可以像操作本地文件一样来操作远程的文件了。是不是非常高大上。
中间的一段代码是通过 ssh2_exec() 去查看我们上传的文件,可以看到文件和目录都是正常存在的。最后,我们通过 file_get_contents() 并且配合伪协议来读取文件的内容,其实这就相当于从远程服务器下载文件了。
之前的文章中我们已经简单地学习过一些伪协议相关的内容,所以这里也就不多解释了,但是不得不说,这种形式的操作真的非常方便和直观。不管是 copy() 还是 file_get_contents() 真的就和我们操作本地文件一样的感觉。
当然,除了上面介绍的 ssh2_sftp() 和 ssh2_sftp_mkdir() 之外,扩展中还有 ssh2_sftp_chmod()、ssh2_sftp_rename()、ssh2_sftp_unlink() 等等的函数,相信从名字大家也能看出它们的作用,这里就不多做解释了,大家可以查阅文档获取相关的资料。
scp 传输文件
最后,我们再来看看更为简单的 scp 方式的文件传输。
ssh2_scp_send($conn, './1.txt', '/home/shelltest/22.txt'); // /home/shelltest/22.txt
ssh2_scp_recv($conn, '/home/shelltest/22.txt', './222.txt'); // ./222.txt
虽说 scp 现在用得不多了(没 rsync 快),不过它的使用可真是方便。ssh2_scp_send() 用于发送一个本地文件到远程服务器,ssh2_scp_recv() 用于从远程服务器拉取一个文件,是不是有点简单的过头了。就跟我们平常使用的 copy() 之类的函数一样的感觉。
总结
今天我们简单的了解了一下 ssh2 这个扩展的一些简单操作,核心的东西也就是这些了。在官方文档中还有很多其它的函数不过都是和密钥登录相关的内容,在这里也就不多赘述了,有需要的小伙伴自己查阅一下就可以,而且他们的使用都并不复杂。剩下的就是 sftp 和 scp 相关的内容了。总体来说,这个扩展还是比较方便好用的,如果只是简单的小型的运维管理功能,是完全可以满足的,当然,具体业务具体分析,使用什么还是大家自己定夺。
测试代码:
参考文档:
https://www.php.net/manual/zh/book.ssh2.php