PHP FTP/SFTP开关类
好的,那么通过ftp或sftp从您自己的服务器访问其他服务器。。。我写了一个小类来处理这两个问题。。它显然是新的,可以很容易地改进,所以我想我会把它扔到这里,看看其他人怎么想(stackoverflow得到了很多观点,希望这能帮助其他人),以及他们如何改进它。。。所以我想问题是。。。如何改进这一点PHP FTP/SFTP开关类,php,design-patterns,ftp,sftp,phpseclib,Php,Design Patterns,Ftp,Sftp,Phpseclib,好的,那么通过ftp或sftp从您自己的服务器访问其他服务器。。。我写了一个小类来处理这两个问题。。它显然是新的,可以很容易地改进,所以我想我会把它扔到这里,看看其他人怎么想(stackoverflow得到了很多观点,希望这能帮助其他人),以及他们如何改进它。。。所以我想问题是。。。如何改进这一点 class ftp_sftp{ //determine, if ssh, to use phpseclib or php's inbuilt ssh_sftp 'libssh' public $ssh
class ftp_sftp{
//determine, if ssh, to use phpseclib or php's inbuilt ssh_sftp 'libssh'
public $ssh_type = 'phpseclib';
//set ths path to the directory containing the entire phpseclib files
public $phpseclib_path = 'scripts/phpseclib0.3.0';
//private vars generated by this class
public $host;
public $username;
public $password;
public $connection_type;
public $port_number;
public $connection = false;
//contruct method which will attempt to set the connection details and automatically attempt to establisha connection to the server
public function __construct( $host, $username, $password, $connection_type, $port_number = false ){
//add the webroot to the beginning of the $this->phpseclib_path (this is bespoke to my own configuration)
$this->phpseclib_path = WEBROOT_PRIVATE.$this->phpseclib_path;
//setting the classes vars
$this->host = $host;
$this->username = $username;
$this->password = $password;
$this->connection_type = $connection_type;
//set the port number to defaults based on connection type if none passed
if( $port_number === false ){
if( $connection_type == 'ftp' ){
$port_number = 21;
} else {
$port_number = 22;
}
}
$this->port_number = $port_number;
//now set the server connection into this classes connection var
$this->connection = $this->connect();
}
//tests the details passed and tries to establish a connection, returns false on fail.
function connect(){
br($this->connection_type);
switch( $this->connection_type )
{
case 'ftp':
$connection = ftp_connect($this->host);
$login = ftp_login($connection, $this->username, $this->password);
//if no connection was possible return false and leave $this-connection as false
if(!$connection || !$login){
return false;
} else {
// enabling passive mode
ftp_pasv( $connection, true );
return $connection;
}
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
//inlcude the phpseclib path in the include array and include the ssh2 class
set_include_path($this->phpseclib_path );
if(!include('Net/SSH2.php')){
echo 'Sorry failed to load SSH2 class';
br();
}
if(!include('Net/SFTP.php')){
echo 'Sorry failed to load SFTP class';
br();
}
$connection = new Net_SFTP($this->host, $this->port_number);
$login = $connection->login($this->username, $this->password);
break;
case 'libssh2':
$connection = ssh2_connect($this->host, $this->port_number);
$login = ssh2_auth_password($connection, 'username', 'secret');
break;
default:
echo 'No ssh method defined, please define one in: $ftp_sftp->ssh_type';
exit();
break;
}
//if no connection was possible return false and leave $this-connection as false
if (!$connection || !$login) {
return false;
} else {
return $connection;
}
break;
default: echo 'No connection type set cannot choose a method to connect';
break;
}
}
//acces the phpseclib errors
public function errors(){
if($this->connection_type == 'sftp' && $this->ssh_type == 'phpseclib'){
print_r($this->connection->getErrors());
} else {
echo 'no error logs available';
}
}
//function used by this class to check certain values are set
public function connection_check(){
if( $this->connection === false){
echo 'Sorry there seems to be a connection problem please try again';
br();
}
if( $this->connection_type === false){
echo 'Sorry there seems to be a no connection type set';
}
if( $this->connection === false || $this->connection_type === false ){
exit();
}
}
//transfers a file to the connected server
public function put($targetLocationToSendTo, $existingLocationToSendFrom){
//check the connection
$this->connection_check();
switch( $this->connection_type )
{
case 'ftp':
//ftp_put the file across
$put = ftp_put( $this->connection, $targetLocationToSendTo, $existingLocationToSendFrom, FTP_BINARY);
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
$put = $this->connection->put( $targetLocationToSendTo, $existingLocationToSendFrom, NET_SFTP_LOCAL_FILE );
break;
case 'libssh2':
$put = ssh2_scp_send($this->connection, $targetLocationToSendTo, $existingLocationToSendFrom, 0755);
break;
}
break;
}
return $put;
}
//list the contents of a remote directory
public function dir_list( $dirToList ){
//check the connection
$this->connection_check();
//run appropriate list
switch( $this->connection_type )
{
case 'ftp':
$list = $this->connection = ftp_nlist($this->connection, $dirToList);
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
$list = $this->connection->nlist( $dirToList );
break;
case 'libssh2':
echo 'Sorry there is no support for nlist with libssh2, however this link has a possible answer: http://randomdrake.com/2012/02/08/listing-and-downloading-files-over-sftp-with-php-and-ssh2/';
break;
}
break;
}
return $list;
}
//get the timestamp of the file on another server
public function remote_filemtime( $pathToFile ){
//check the connection
$this->connection_check();
//run appropriate list
switch( $this->connection_type )
{
case 'ftp':
$timeStamp = ftp_mdtm($this->connection, $pathToFile);
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
$statinfo = $this->connection->stat( $pathToFile );
break;
case 'libssh2':
$statinfo = ssh2_sftp_stat($this->connection, $pathToFile);
break;
}
if($statinfo['mtime']){
$timeStamp = $statinfo['mtime'];
} else {
$timeStamp = false;
}
break;
}
return $timeStamp;
}
//make a directory on the remote server
public function make_dir( $dirToMake ){
//check the connection
$this->connection_check();
//run appropriate list
switch( $this->connection_type )
{
case 'ftp':
$dir_made = ftp_mkdir($this->connection, $dirToMake);
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
$statinfo = $this->connection->mkdir( $dirToMake );
break;
case 'libssh2':
$statinfo = ssh2_sftp_mkdir($this->connection, $dirToMake, 0755);
break;
}
break;
}
return $dir_made;
}
//change directory
public function change_dir( $dirToMoveTo ){
//check the connection
$this->connection_check();
//run appropriate list
switch( $this->connection_type )
{
case 'ftp': $chdir = ftp_chdir($this->connection, $dirToMoveTo );
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
$chdir = $this->connection->chdir( $dirToMoveTo );
break;
case 'libssh2':
echo 'Sorry this feature does exist yet for when using libssh2 with the ftp_sftp class';
exit();
break;
}
break;
}
return $chdir;
}
//curent directory we are looking in
public function pwd(){
//check the connection
$this->connection_check();
//run appropriate list
switch( $this->connection_type )
{
case 'ftp': $pwd = ftp_pwd($this->connection);
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
$pwd = $this->connection->pwd();
break;
case 'libssh2':
echo 'Sorry this feature does exist yet for when using libssh2';
exit();
break;
}
break;
}
return $pwd;
}
//delete file
public function delete_file($fileToDelete){
//check the connection
$this->connection_check();
//run appropriate list
switch( $this->connection_type )
{
case 'ftp': $unlink = ftp_delete($this->connection, $fileToDelete);
break;
case 'sftp':
//decide which ssh type to use
switch( $this->ssh_type ){
case 'phpseclib':
$unlink = $this->connection->delete( $fileToDelete );
break;
case 'libssh2':
$unlink = ssh2_sftp_unlink($this->connection, $fileToDelete);
break;
}
break;
}
return $unlink;
} }//end of class
使用类:
$ftp_sftp = new ftp_sftp( '92.21.627.163', 'ftpuser', 'yourpassword', '', 'ftp', '21' );
echo $ftp_sftp->pwd();
我在使用EasyHP将phpseclib连接到我的win7机器上时遇到了一点问题,并启动了一个Q。。如果有人有任何想法,我将非常感激。。。
Wordpress的SFTP实现以一种奇怪的方式执行chdir和pwd。它们执行ssh2\u exec()。不知道这是否适用于那里的文件下载/上传。以下是它们的实现: 我认为phpseclib更好,但仅供参考 另外,phpseclib的最新版本是0.3.1。idk。。。仅供参考,哈哈 最后,可能确实包含_once()而不是include()?这样可以多次实例化该类?此代码的主要问题是它可以垂直扩展。假设您决定添加另一个实现,一个本地文件系统,为'file'的
$connection\u type
引入第三个潜在值。现在你必须通过代码添加case'file':
到处都是的条件,使得ftp\u sftp
失控。现在,在代码的“sftp”分支中,您又遇到了同样的问题,处理$ssh\u type
原始代码
switch($connection_type) {
case 'ftp' : // ...
case 'stfp' : // ...
}
变为
switch($connection_type) {
case 'ftp' : // ...
case 'stfp' : // ...
case 'file' : // ...
}
请记住,这适用于打开$connection\u type
的代码中的每个开关语句。想象一下,我们将向ftp\u sftp
类添加多少行代码来合并每个附加行为
事实上,看起来您现在实际上有3种策略,ftp、sftp和ssh
这里要做的是将代码设置为水平扩展,而不是垂直扩展,方法是通过
基本上,您所做的是在代码中提取公共接口,然后为FTP和SFTP创建单独的实现。有很多不同的方法来进行实际的实施,所以调整到你的心的内容
通用接口
switch($connection_type) {
case 'ftp' : // ...
case 'stfp' : // ...
}
这是任何“ftp类”必须能够完成的定义
interface PhpFtp
{
public function __construct($host, $username, $password, $port_number = false);
public function connect();
public function errors();
public function connection_check();
public function put($targetLocationToSendTo, $existingLocationToSendFrom);
public function dir_list( $dirToList );
public function remote_filemtime( $pathToFile );
public function make_dir( $dirToMake );
public function change_dir( $dirToMoveTo );
public function pwd();
public function delete_file($fileToDelete);
}
工厂
现在,代码中只有一条switch语句用于检查$connection\u type
。您可能会决定以不同的方式处理默认情况
class PhpFtpFactory
{
public static function create($connection_type)
{
switch($connection_type) {
case 'ftp':
$oFtp = new Ftp();
break;
case 'sftp':
$oFtp = new Sftp();
break;
default:
throw new UnexpectedValueExcpetion(
'No connection type set cannot choose a method to connect');
}
// Potential follow-up construction steps
return $oFtp;
}
}
基类
switch($connection_type) {
case 'ftp' : // ...
case 'stfp' : // ...
case 'file' : // ...
}
这不是绝对必要的,但很有帮助。另外,我们在这里潜入另一种设计模式,叫做BaseFtp::pwd
是一种模板方法,它控制整个pwd算法,但将其一部分委托给具体的子对象
abstract class BaseFtp implements PhpFtp
{
public function pwd()
{
$this->connection_check();
return $this->_pwd();
}
abstract protected function _pwd();
}
具体的FTP实施
现在我们有了一个简洁的FTP类,它只执行FTP操作,它对SFTP一无所知
class Ftp extends BaseFtp
{
protected function _pwd()
{
return ftp_pwd($this->connection);
}
public function connect()
{
$connection = ftp_connect($this->host);
$login = ftp_login($connection, $this->username, $this->password);
// if no connection was possible return false and leave $this-connection as false
if(!$connection || !$login)
return false;
// enabling passive mode
ftp_pasv( $connection, true );
return $connection;
}
}
混凝土SFTP等级
switch($connection_type) {
case 'ftp' : // ...
case 'stfp' : // ...
case 'file' : // ...
}
现在我们有了一个独立的SFTP类,但是请注意它会打开$ssh\u type
,就像原来的ftp\u SFTP
类对$connection\u type
所做的那样。一旦你掌握了策略模式,并且完成了Ftp
&Sftp
类,你就可以回到Sftp
实现中实现策略模式,例如,你有两个类Phpseclib
&Libssh2
class Sftp extends BaseFtp
{
protected function _pwd()
{
// decide which ssh type to use
switch($this->ssh_type) {
case 'phpseclib':
return $this->connection->pwd();
case 'libssh2':
echo 'Sorry this feature does exist yet for when using libssh2';
return false;
}
}
public function connect()
{
// decide which ssh type to use
switch( $this->ssh_type ) {
case 'phpseclib':
// inlcude the phpseclib path in the include array
// and include the ssh2 class
set_include_path($this->phpseclib_path );
if(!include('Net/SSH2.php')){
echo 'Sorry failed to load SSH2 class';
br();
}
if(!include('Net/SFTP.php')){
echo 'Sorry failed to load SFTP class';
br();
}
$connection = new Net_SFTP($this->host, $this->port_number);
$login = $connection->login($this->username, $this->password);
break;
case 'libssh2':
$connection = ssh2_connect($this->host, $this->port_number);
$login = ssh2_auth_password($connection, 'username', 'secret');
break;
default:
echo 'No ssh method defined, please define one in: $ftp_sftp->ssh_type';
exit();
break;
}
if(!$connection || !$login)
return false;
}
}
使用新代码
switch($connection_type) {
case 'ftp' : // ...
case 'stfp' : // ...
}
随着新系统的就位,更容易看到什么是什么,每个类都专注于它的特定领域,代码水平增长。这是什么意思?还记得上文吗,当我们考虑在原始范例中添加第三个$connection\u type
'file'时?在新安排中,我们更新了一条开关语句,即工厂中的语句:
class PhpFtpFactory
{
public static function create($connection_type)
{
switch($connection_type) {
case 'ftp':
$oFtp = new Ftp();
break;
case 'sftp':
$oFtp = new Sftp();
break;
case 'file':
$oFtp = new File();
break;
default:
throw new UnexpectedValueExcpetion(
'No connection type set cannot choose a method to connect');
}
// Potential follow-up construction steps
return $oFtp;
}
}
然后当然要添加新的具体实现
class File extends PhpFtp
{
// ...
}
由于新的文件
类可以放在它自己的空间中,因此我们没有扩展一个中心类,即原始的ftp\u sftp
类
为了使用新的系统,您向工厂查询一个实例,然后从那里开始
// get an instance of the Ftp class
$ftp = PhpFtpFactory::create('ftp');
// get an instance of the Sftp class
$sftp = PhpFtpFactory::create('sftp');
这两个实例都支持所有PhpFtp
函数,因为它们实现了接口!这也使您能够编写多态代码。考虑类型对接口的提示函数
// This is polymorphic code
function doFtpStuff(PhpFtp $oFtp) {
// As mentioned above $oFtp can be an instance of any class that implements PhpFtp
$oFtp->connect();
$oFtp->pwd();
}
我建议继承减少那些switch语句。你能详细说明一下吗?我已经尽了全力寻找答案,因为这在野外是一个非常普遍的问题。希望这是有道理的。如果你掌握了最简单的设计模式——策略模式,我向你保证,你会立即绕过你周围75%的开发人员!如果你迷上了图案,拿起标准包,你就会成为一个忍者。不过,仅供参考,听起来你在问两个问题,1。如何改进代码,2。为什么会出现win7/EasyHP问题。我回答了1。,听起来好像@neubert回答了2。哇。非常感谢你在那里的帮助。我要到星期五才能实施这个计划,但我会告诉你我的进展情况