Перевод: Использование SSH и SFTP с языком PHP / PHP. Особенности и фичи


Оригинал: Using SSH and SFTP with PHP

От переводчика: оригинальные листинги автора были немного доработаны.

В современном мире, где так много сторонних компонентов и программ для создания совместного доступа, важно понимать и использовать протоколы SCP и SFTP. Для PHP есть расширение-оболочка для библиотеки libssh2, которая реализует протокол SSH2. Она обеспечивает несколько функций, которые можно использовать для безопасной передачи файлов.
Перед тем как приступить к использованию этих функций, нужно установить пакет SSH2. Поскольку это PECL-компонент, процесс установки будет зависеть от вашей операционной системы. Следуйте инструкциям на php.net.



Установление соединения

Давайте начнем с подключения к службе SSH. Установление соединения выглядит просто:
$conn = ssh2_connect('example.com', 22);
ssh2_auth_password($conn, 'username', 'password');

Некоторые администраторы предпочитают использовать открытые и закрытые ключи для аутентификации входа в систему. Если вы хотите подключиться таким образом, нужно использовать следующий код:
$conn = ssh2_connect('example.com', 22);
ssh2_auth_pubkey_file(
	$conn,
	'username',
	'/home/username/.ssh/id_rsa.pub',
	'/home/username/.ssh/id_rsa'
);

Если при аутентификации вы используете имя пользователя или пароль, открытый или закрытый ключ, функции ssh2_auth_password () и ssh2_auth_pubkey_file () возвращают логическое значение, указывающее на то, была ли осуществлена проверка подлинности успешно.

Выполнение основных команд

После того как вы успешно прошли аутентификацию с сервером, вы можете совершить операцию передачи файлов. Функция SCP позволяет вам отправлять или получать файлы следующим образом:
// send a file
ssh2_scp_send($conn, '/local/filename', '/remote/filename', 0644);

// fetch file
ssh2_scp_recv($conn, '/remote/filename', '/local/filename');

При копировании файла на удаленный сервер с помощью дополнительного параметра в функции ssh2_scp_send () можно установить права доступа.
Большей функциональности можно добиться с использованием функции SFTP. Вы можете изменять права к файлу или директории, получать информацию о файле, создавать каталоги, переименовывать элементы, удалять элементы и т.д. Функция SFTP очень похожа на SCP, но дополнительное подключение через ssh2_sftp () должно быть сделано до начала использования функции:
$sftp = ssh2_sftp($conn);

// Create a new folder
ssh2_sftp_mkdir($sftp, '/home/username/newdir');

// Rename the folder
ssh2_sftp_rename($sftp, '/home/username/newdir', '/home/username/newnamedir');

// Remove the new folder
ssh2_sftp_rmdir($sftp, '/home/username/newnamedir');

// Create a symbolic link
ssh2_sftp_symlink($sftp, '/home/username/myfile', '/var/www/myfile');

// Remove a file
ssh2_sftp_unlink($sftp, '/home/username/myfile');

Посредством ssh2_sftp () устанавливается подключение к ресурсу, и затем все операции проходят через SFTP-соединение, которое использует вызовы различных ssh2_sftp_* функций. Вызов функции возвращает логическое значение, что позволяет определить, было ли действие успешным.

Использование функции-оболочки

Когда определенной функции управления файлами для SFTP или SCP-протокола не существует, на помощь приходит оболочка. Ниже приведено несколько примеров:
// Create a new folder
mkdir('ssh2.sftp://' . $sftp . '/home/username/newdir');

// Remove the new folder
rmdir('ssh2.sftp://' . $sftp . '/home/username/newdir');

// Retrieve a list of files
$files = scandir('ssh2.sftp://' . $sftp . '/home/username');

Перед выполнением любой из этих операций соединение с сервером SSH и SFTP должно быть установлено, так как используется ранее созданная переменная $sftp.

Собираем все вместе

Теперь, когда вы научились подключаться, проверять аутентификацию и выполнять команды на сервере SSH, мы можем создать несколько вспомогательных классов для упрощения процесса выполнения этих команд: один — для выполнения SCP вызовов, один — для вызовов SFTP, родительский класс для общей функциональности и пару классов для инкапсуляции идентификационной информации (паролей и ключей).
Давайте создадим класс аутентификации первым, так как он будет использоваться другими классами:
abstract class SSH2Authentication
{

	protected $connect;
	protected $username;
	
	public function setConnect($connect){
		$this->connect = $connect;
	}
	
	public function authenticate(){
		throw new Exception('Method "authenticate" must be defined.');
	}
}

class SSH2Password extends SSH2Authentication
{

	protected $password;

	public function __construct($username, $password) {
		$this->username = $username;
		$this->password = $password;
	}
	
	public function authenticate(){
		return ssh2_auth_password(
			$this->connect,
			$this->username,
			$this->password
		);
	}
}

class SSH2Key extends SSH2Authentication
{

	protected $publicKey;
	protected $privateKey;

	public function __construct($username, $publicKey, $privateKey) {
		$this->username = $username;
		$this->publicKey = $publicKey;
		$this->privateKey = $privateKey;
	}
	
	public function authenticate($passphrase=''){
		return ssh2_auth_pubkey_file(
			$this->connect,
			$this->username,
			$this->publicKey,
			$this->privateKey,
			$passphrase
		);
	}
}

SSH2Password и SSH2Key просто оборачивают соответствующие им данные аутентификации. Они имеют общий базовый класс, поэтому мы можем воспользоваться PHP для перехода от источника к получателю.
Двигаемся дальше. Давайте создадим протокол SSH2 для подключения и аутентификации на SSH сервере.
class SSH2
{

	protected $connect;

	public function __construct($host, SSH2Authentication $auth, $port = 22) {
		$this->connect = ssh2_connect($host, $port);
		$auth->setConnect($this->connect);
		if ($auth->authenticate() === false) {
			throw new Exception('SSH2 login is invalid.');
		}
	}
}

Будет создан очень простой SCP-класс, который расширяет функционал SSH2 и использует метод __call(). Это позволяет нам сделать две важные вещи: автоматически предварять «ssh_scp_» при вызове функции и поддерживать связь с переменной.
class SSH2SCP extends SSH2
{

	public function __call($func, $args) {
		$func = 'ssh2_scp_' . $func;
		if (function_exists($func)) {
			array_unshift($args, $this->conn);
			return call_user_func_array($func, $args);
		}
		else {
			throw new Exception($func . ' is not a valid SCP function.');
		}
	}
}

Класс SFTP схож в плане конструкции, хотя и перегружен вызовом функции ssh2_sftp (). Результаты будут храниться в защищенной переменной и автоматически добавляться ко всем вызовам SFTP-функции.
class SSH2SFTP extends SSH2
{

	protected $sftp;

	public function __construct($host, SSH2Authentication $auth, $port = 22) {
		parent::__construct($host, $auth, $port);
		$this->sftp = ssh2_ftp($this->conn);
	}
	
	public function __call($func, $args) {
		$func = 'ssh2_sftp_' . $func;
		if (function_exists($func)) {
			array_unshift($args, $this->sftp);
			return call_user_func_array($func, $args);
		}
		else {
			throw new Exception($func . ' is not a valid SFTP function.');
		}
	}
}

Эти классы могут быть использованы для вызова SCP- и SFTP-функций. Благодаря полезному методу __call в обоих классах нет необходимости заново открывать соединение или повторно вводить «ssh2_scp_» либо «ssh2_ftp_» при каждом вызове.
// Create SCP connection using a username and password
$scp = new SCP(
	'example.com',
	new SSH2Password('username', 'password')
);
// Receive a file via SCP
if ($scp->recv('remote/file', 'local/file')) {
	echo 'Successfully received file';
}
	
// Create SFTP connection using a public/private key
$sftp = new SSH2SFTP(
	'example.com',
	new SSH2Key('username', 'public_key', 'private_key')
);
// Create a directory via SFTP
if ($sftp->mkdir('directory/name')) {
	echo 'Successfully created directory';
} 


Заключение

Установка расширения SSH2 для PHP нужна для того, чтобы обеспечить выполнение скриптов с возможностью подключения к SSH2-серверу. У вас есть выбор: либо опираться на удобные классы, которые упрощают код создания SFTP- или SCP-функций, либо, если конкретная функция не предусмотрена библиотекой, пользоваться функциональностью SSH2-оболочки. При этом вам доступно большинство основных операций с файловой системой.