Programming

While working on a project, I found myself having to download files using SFTP. This seemed like a fairly straightforward task and I thought PHP would surely have something that could help me along. A quick Google search provided me with ssh2_sftp to take a look at.

I work in Micro$oft shop, so the ssh2 library was, unfortunately, not immediately available without building my own. A huge thanks to Pierre Joye for providing compiled DLLs of common libraries for those of us stuck using MS. Finding the right DLL for your needs is just a Ctrl+F away on his list. Once the DLL was dropped in the proper directory and the web server restarted, we were in business.

Thankfully the comments, on the manual page for the ssh2_sftp function, provided me with some guidance to actually get started. What I discovered, was that the ssh2_sftp function creates a resource which allows us to create a stream that you can use with familiar functions like fopen. The function also lets us use the ssh2:// protocol wrapper (specifically, ssh2.sftp://), which you’ll see in the example. One of the neat things about the ssh2:// protocol wrapper is described in the manual:

In addition to accepting traditional URI login details, the ssh2 wrappers will also reuse open connections by passing the connection resource in the host portion of the URL.

This means, we can use the resource itself as part of our URL when opening our stream with fopen. You can see this in the example as well.

After tinkering around, I came up with some code that seemed to be a bit cleaner than the examples in the manual and with a bit more commenting, for explanation. Hopefully it will help to describe how you can list and download files over SFTP with the ssh2 library. This example uses basic password authentication to make an SFTP connection, grab the list of files and download them. Should you require authentication using a pubkey, check out ssh2_auth_pubkey_file.

<?php
// Make our connection
$connection = ssh2_connect('ftp.somehost.com');
 
// Authenticate
if (!ssh2_auth_password($connection, 'username', 'password')) {
    throw new Exception('Unable to connect.');
}
 
// Create our SFTP resource
if (!$sftp = ssh2_sftp($connection)) {
    throw new Exception('Unable to create SFTP connection.');
}
 
/**
  * Now that we have our SFTP resource, we can open a directory resource
  * to get us a list of files. Here we will use the $sftp resource in
  * our address string as I previously mentioned since our ssh2:// 
  * protocol allows it.
  */
$files = array();
$dirHandle = opendir("ssh2.sftp://$sftp/");
 
// Properly scan through the directory for files, ignoring directory indexes (. & ..)
while (false !== ($file = readdir($dirHandle))) {
    if ($file != '.' && $file != '..') {
        $files[] = $file;
    }
}
 
/**
  * Using our newly created list of files, we can go about downloading. We will
  * open a remote stream and a local stream and write from one to the other.
  * We will use error suppression on the fopen call to suppress warnings from
  * not being able to open the file.
  */
if (count($files)) {
    foreach ($files as $fileName) {
        // Remote stream
        if (!$remoteStream = @fopen("ssh2.sftp://$sftp/$fileName", 'r')) {
            throw new Exception("Unable to open remote file: $fileName");
        } 
 
        // Local stream
        if (!$localStream = @fopen("/localdir/$fileName", 'w')) {
            throw new Exception("Unable to open local file for writing: /localdir/$fileName");
        }
 
        // Write from our remote stream to our local stream
        $read = 0;
        $fileSize = filesize("ssh2.sftp://$sftp/$fileName");
        while ($read < $fileSize && ($buffer = fread($remoteStream, $fileSize - $read))) {
            // Increase our bytes read
            $read += strlen($buffer);
 
            // Write to our local file
            if (fwrite($localStream, $buffer) === FALSE) {
                throw new Exception("Unable to write to local file: /localdir/$fileName");
            }
        }
 
        // Close our streams
        fclose($localStream);
        fclose($remoteStream);
    }
}

There you have it! Hopefully this post was helpful in regards to assisting your understanding of how to use SFTP and the ssh2 library in PHP. If you liked this post, then please consider following me on Twitter or subscribing to my feed.

Programming

While typing in a terminal, tab completion is a huge time saver. Being able to auto-complete file names or commands is very nice, especially if you can’t quite remember what it is.

There are times, however, that this has become quite annoying for me as a developer. More than once a day when I really get in the zone and start hacking, I will accidentally hit tab when I have not yet input anything. While this doesn’t slow things down too much for me on a native Linux installation, it causes a huge delay when using Cygwin. Instead of just quickly asking me if I want display all the options, it appears to start to build the command list which has a delay of many seconds. This completely breaks my flow of programming.

I’m not entirely sure if it has to do with the number of possible auto-completes in my Cygwin installation or if it just is slower than a Linux installation but I didn’t want it anymore. I never find myself just hitting tab to get the list of every possibility; I am always auto-completing.

Having searched online before for a solution but never coming up with anything I just sort of accepted that my flow would be interrupted every once in a while by this silly behavior. Today, I ran into the issue multiple times and decided I had, had enough. I hopped into ##linux on Freenode.

There, I was delivered the option that would make my problem go away:

shopt -s no_empty_cmd_completion

Dropping this line in my ~/.bashrc file accompanied by a quick restart finally made my problem go away.

Much better.

If you liked this post, then please consider following me on Twitter or subscribing to my feed.