I currently have a script which allows me to output the list of files inside the same directory.

The output shows the names, and then I used filemtime() function to show the date when the file was modified.

How will I sort the output to show the latest modified file?

This is what I have for now:

if ($handle = opendir('.')) {
    while (false !== ($file = readdir($handle))) {
        if ($file != "." && $file != "..") {
            $lastModified = date('F d Y, H:i:s', filemtime($file));
            if(strlen($file)-strpos($file, ".swf") == 4) {
                echo "$file - $lastModified";
            }
        }
    }
    closedir($handle);
}

Solution 1

This would get all files in path/to/files with an .swf extension into an array and then sort that array by the file's mtime

$files = glob('path/to/files/*.swf');
usort($files, function($a, $b) {
    return filemtime($b) - filemtime($a);
});

The above uses an Lambda function and requires PHP 5.3. Prior to 5.3, you would do

usort($files, create_function('$a,$b', 'return filemtime($b)-filemtime($a);'));

If you don't want to use an anonymous function, you can just as well define the callback as a regular function and pass the function name to usort instead.

With the resulting array, you would then iterate over the files like this:

foreach($files as $file){
    printf('<tr><td><input type="checkbox" name="box[]"></td>
            <td><a href="%1$s" target="_blank">%1$s</a></td>
            <td>%2$s</td></tr>', 
            $file, // or basename($file) for just the filename w\out path
            date('F d Y, H:i:s', filemtime($file)));
}

Note that because you already called filemtime when sorting the files, there is no additional cost when calling it again in the foreach loop due to the stat cache.

Solution 2

You need to put the files into an array in order to sort and find the last modified file.

$files = array();
if ($handle = opendir('.')) {
    while (false !== ($file = readdir($handle))) {
        if ($file != "." && $file != "..") {
           $files[filemtime($file)] = $file;
        }
    }
    closedir($handle);

    // sort
    ksort($files);
    // find the last modification
    $reallyLastModified = end($files);

    foreach($files as $file) {
        $lastModified = date('F d Y, H:i:s',filemtime($file));
        if(strlen($file)-strpos($file,".swf")== 4){
           if ($file == $reallyLastModified) {
             // do stuff for the real last modified file
           }
           echo "<tr><td><input type=\"checkbox\" name=\"box[]\"></td><td><a href=\"$file\" target=\"_blank\">$file</a></td><td>$lastModified</td></tr>";
        }
    }
}

Not tested, but that's how to do it.

Solution 3

Here is an example that uses the RecursiveDirectoryIterator class. It's a convenient way to iterate recursively over file system.

$output = array();
foreach( new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator( 'path', FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS ) ) as $value ) {
        if ( $value->isFile() ) {
            $output[] = array( $value->getMTime(), $value->getRealPath() );
        }
}

usort ( $output, function( $a, $b ) {
    return $a[0] > $b[0];
});

Solution 4

$files = array_diff(scandir($dir,SCANDIR_SORT_DESCENDING), array('..', '.'));
print_r($files);

Solution 5

I use your exact proposed code with only some few additional lines. The idea is more or less the same of the one proposed by elias, but in this solution there cannot be conflicts on the keys since each file in the directory has a different filename and so adding it to the key solves the conflicts.

The first part of the key is the datetime string formatted in a manner such that I can lexicographically compare two of them.

if ($handle = opendir('.')) {
    $result = array();
    while (false !== ($file = readdir($handle))) {
        if ($file != "." && $file != "..") {
            $lastModified = date('F d Y, H:i:s', filemtime($file));
            if(strlen($file)-strpos($file, ".swf") == 4) {
                $result [date('Y-m-d H:i:s', filemtime($file)).$file] =
                    "<tr><td><input type=\"checkbox\" name=\"box[]\"></td><td><a href=\"$file\" target=\"_blank\">$file</a></td><td>$lastModified</td></tr>";
            }
        }
    }
    closedir($handle);
    krsort($result);
    echo implode('', $result);
}

Solution 6

For anyone who is desperately focused on micro-optimizing performance (minimizing the total number of iterated function calls), you can store the file modification time as the first element and the filename as the second element of an array of rows.

Once this is set up, you can simply call sort() and the algorithm will compare by the mod times first, then break ties using the unique filenames.

Code: (Demo)

$result = [];
foreach (glob('path/to/files/*.swf') as $file) {
    $result[] = [filemtime($file), $file];
}
sort($result);
var_export($result);

*note: if you don't want the path prepended to the filename ($file), then use chdir('path/to/files') to move into the targeted directory before calling glob().

Of course, using the stock-standard sort() means the you are locked into ASC for all elements in all rows. As well, rsort() would sort by all columns in DESC order. These outcomes will not satisfy in some circumstances. I suppose you could hack the mod time sorting to be the opposite of the filename alphabetizing by storing the mod time as a negative integer. ...I'm digressing.