Can't get SFTP to work in PHP

2020-07-02 08:11发布

I am writing a simple SFTP client in PHP because we have the need to programatically retrieve files via n remote servers. I am using the PECL SSH2 extension.

I have run up against a road block, though. The documentation on php.net suggests that you can do this:

$stream = fopen("ssh2.sftp://$sftp/path/to/file", 'r');

However, I have an ls method that attempts to something similar

public function ls($dir)
{
    $rd = "ssh2.sftp://{$this->sftp}/$dir";
    $handle = opendir($rd);
    if (!is_resource($handle)) {
        throw new SFTPException("Could not open directory.");
    }

    while (false !== ($file = readdir($handle))) {
        if (substr($file, 0, 1) != '.'){
            print $file . "\n";
        }
    }
    closedir($handle);
}

I get the following error:

PHP Warning:  opendir(): Unable to open ssh2.sftp://Resource id #5/outgoing on remote host

This makes perfect sense because that's what happens when you cast a resource to string. Is the documentation wrong? I tried replacing the resource with host, username, and host and that didn't work either. I know the path is correct because I can run SFTP from the command line and it works fine.

Has anyone else tried to use the SSH2 extenstion with SFTP? Am I missing something obvious here?

UPDATE:

I setup sftp on another machine in-house and it works just fine. So, there must be something about the server I am trying to connect to that isn't working.

10条回答
趁早两清
2楼-- · 2020-07-02 08:49

I recently tried to get SFTP on PHP working and found that phpseclib was a lot easier to use:

http://phpseclib.sourceforge.net/

If you have the luxury of not being on a shared host and can install whatever extensions you want to maybe the PECL extension would be better but not all of us are so lucky. Plus, phpseclib's API looks a bit more intuitive, being OOP and all.

查看更多
手持菜刀,她持情操
3楼-- · 2020-07-02 08:50

This is a bug in the ssh2 package that I found years ago and posted a patch to php.net. It fixes this issue but requires a rebuild of the ssh2 pecl package. You can read more here: https://bugs.php.net/bug.php?id=69981. I included a patch there to the ssh2_fopen_wrappers.c file in the package to fix the issue. Here is a comment I included:

Here is a line of code from ssh2_fopen_wrappers.c that causes this bug: (comment included)

/*
       Find resource->path in the path string, then copy the entire string from the original path.
       This includes ?query#fragment in the path string
*/
    resource->path = estrdup(strstr(path, resource->path));

This line of code -- and therefore this bug -- was introduced as a fix for bug #59794. That line of code is attempting to get a string containing the part, query and fragment from the path variable. Consider this value for the path variable:

ssh2.sftp://Resource id #5/topdir?a=something#heading1

When resource->path is "/topdir", the result is that "/topdir?a=something#heading1" gets assigned to resource->path just like the comment says.

Now consider the case when resource->path is "/". After the line of code is executed, resource->path becomes "//Resource id#5/topdir#heading1". This is clearly not what you want. Here's a line of code that does:

 resource->path = estrdup( strstr( strstr( path, "//" ) + 2, "/" ) );

You may also need to apply the patch for bug # 73597 which removes "Resource id #" from the path string before calling php_url_parse().

查看更多
▲ chillily
4楼-- · 2020-07-02 08:51

The documentation on that page contains an error. Take a look at the example here instead: http://php.net/ssh2_sftp - what you actually need to do is to open a special SFTP resource using ssh2_sftp() prior to using it with fopen(). And yes, it looks just like that, e.g. "Resource #24" when converted to string... a bit weird but apparently it works.

Another caveat is that SFTP starts in the root directory rather than the home directory of the remote user, so your remote path in the URI should always be an absolute one.

查看更多
Evening l夕情丶
5楼-- · 2020-07-02 08:55

For php versions > 5.6.27 use intval()

$sftpConnection = ssh2_connect($host);
$sftp = ssh2_sftp($sftpConnection);
$fp = fopen("ssh2.sftp://" . intval($sftp) . $remoteFile, "r");

https://bugs.php.net/bug.php?id=73597

查看更多
相关推荐>>
6楼-- · 2020-07-02 08:57

When connecting to a SFTP server and you need to connect to the root folder (for instance for reading the content of the folder) you would still get the error when using just "/" as the path.

The solution that I found was to use the path "/./", that's a valid path that references to the root folder. This is useful when the user you are logging with has access only to its own root folder and no full path is available.

So the request to the server when trying to read the contents of the root folder should be something like this:

$rd = "ssh2.sftp://{$this->sftp}/./";
查看更多
看我几分像从前
7楼-- · 2020-07-02 08:57

Solved my issue by enabling sftp support on the (Powershell) server

查看更多
登录 后发表回答