Sunday, October 30, 2022

[SOLVED] Getting latest file from SFTP in PHP (using CURL)

Issue

I've got this function to get files from a directory on an SFTP. It returns all files in the folder, but with a filetime that is hard to parse.

What I want is to get the latest modified file.

private function getDirList()
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this -> host);
        curl_setopt($ch, CURLOPT_USERPWD, $this -> user . ":" . $this -> pass);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

        if (curl_exec($ch) === false) {
            echo 'Curl error: ' . curl_error($ch);
            exit();
        }

        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'LIST');
        $result = trim(curl_exec($ch));
        $files = array();
        foreach (explode("\n", trim($result, "\n")) as $line) {
            $files[] = $line;
        }

        return $files;
    }

What I get from the function:

Array
(
    [0] => -rw-rw-rw-   1 user     group     4482926 Jan 19 01:30 file1.csv
    [1] => -rw-rw-rw-   1 user     group     4291169 Oct 21 00:33 file2.csv
    [2] => -rw-rw-rw-   1 user     group     4469721 Dec 14 01:30 file3.csv
    [3] => -rw-rw-rw-   1 user     group     4275439 Sep 28 00:30 file4.csv
    [4] => -rw-rw-rw-   1 user     group     4244881 Sep  6  2018 file5.csv
    [5] => -rw-rw-rw-   1 user     group     4310557 Nov  2 01:30 file6.csv
    [6] => -rw-rw-rw-   1 user     group     4310765 Nov  5 01:30 file7.csv
    [7] => -rw-rw-rw-   1 user     group     4245407 Sep 11 00:30 file8.csv
    [8] => -rw-rw-rw-   1 user     group     4198017 Sep  1  2018 file9.csv
    [9] => -rw-rw-rw-   1 user     group     4311228 Nov 10 01:31 file10.csv
...

Could I do a call for each file and get a better filetime? Could I send a different customrequest for a different listing?


Solution

Consider using phpseclib.

With it, you can use the following code:

$sftp = new phpseclib\Net\SFTP($hostname);

$sftp->login($username, $password) or die("Cannot login");

$files = $sftp->rawlist($path);

// filter out folders     
$files_only_callback = function($a) { return ($a["type"] == NET_SFTP_TYPE_REGULAR); };
$files = array_filter($files, $files_only_callback);

// sort by timestamp
usort($files, function($a, $b) { return $b["mtime"] - $a["mtime"]; });
// In PHP 7, you can use spaceship operator instead:
// usort($files, function($a, $b) { return $a["mtime"] <=> $b["mtime"]; }); 

// pick the latest file
$latest = $files[0]["filename"];

phpseclib does not need any installation. Just extract the source code package along with your code. Though to load the latest phpseclib 2.0, they recommend using Composer. If you do not want to rely on yet another library for loading, you can use the following code to load phpseclib:

spl_autoload_register(function ($class) {
    $file = stream_resolve_include_path(
        str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php');
    if ($file !== false) {
        require $file;
        return true;
    }
    return false;
});
        
$phpseclib_path = "phpseclib";
set_include_path(get_include_path() . PATH_SEPARATOR . $phpseclib_path);

(based on Test whether a file exists anywhere in the include path)



Answered By - Martin Prikryl
Answer Checked By - Clifford M. (WPSolving Volunteer)