47

So I have this app that processes CSV files. I have a line of code to load the file.

$myFile = "data/FrontlineSMS_Message_Export_20120721.csv";  //The name of the CSV file
$fh = fopen($myFile, 'r');                             //Open the file

I would like to find a way in which I could look in the data directory and get the newest file (they all have date tags so they would be in order inside of data) and set the name equal to $myFile.

I really couldn't find and understand the documentation of php directories so any helpful resources would be appreciated as well. Thank you.

Mike
  • 2,514
  • 2
  • 23
  • 37
  • If your filenames don't have the timestamp in the name this question has already been answered here: https://stackoverflow.com/questions/1491020/php-get-the-latest-file-addition-in-a-directory – Oddible Apr 10 '14 at 21:31

4 Answers4

79

Here's an attempt using scandir, assuming the only files in the directory have timestamped filenames:

$files = scandir('data', SCANDIR_SORT_DESCENDING);
$newest_file = $files[0];

We first list all files in the directory in descending order, then, whichever one is first in that list has the "greatest" filename — and therefore the greatest timestamp value — and is therefore the newest.

Note that scandir was added in PHP 5, but its documentation page shows how to implement that behavior in PHP 4.

PaulH
  • 2,918
  • 2
  • 15
  • 31
Matchu
  • 83,922
  • 18
  • 153
  • 160
  • 2
    @Mike: Cool :) I'm away from my PHP-enabled box right now, so this sample, while only two lines long, is untested, so let me know if it doesn't do what it oughta. – Matchu Jul 22 '12 at 03:01
  • 1
    Worked great, thanks! I think I needed to add a '/' to 'data' to get it to work. – Mike Aug 03 '12 at 10:22
  • 5
    The constants SCANDIR_SORT_DESCENDING and SCANDIR_SORT_ASCENDING have been defined starting from PHP 5.4 -- with versions of PHP 5 prior to 5.4 you'll have to use 1 and 0 respectively to define the sort order. – adriano72 Aug 29 '13 at 14:11
  • 21
    Please correct me if I'm wrong, but scandir sorts node names by an alfabetical order, not the timestamp value order. – Mike Doe Sep 28 '13 at 16:29
  • 3
    @mike: That's correct! OP's files, as it happens, have the timestamps in the name, so sorting by name is sufficient here. Not sure how to do it by actual modification date… – Matchu Sep 28 '13 at 20:13
  • This doesn't work if there are sub-folders in the folder – HP. Feb 27 '14 at 07:34
  • 9
    IMPORTANT: As noted above, this is alpha sort, not an actual time stamp sort. So it will work fine if the date stamp is reflected in the filename, otherwise it will not give the expected results. – John Contarino Oct 23 '15 at 18:42
  • 1
    Learned "the hard way". *Author needs to add this info to his answer!* Please note that if you have a file pattern such as "name_NUMBER.format" and if the "NUMBER" will be incrementing - then the approach will not work as well. At some point it will stop showing the latest file correctly. In my case it stopped working after NUMBER=99. For real solution I recommend approach here: http://stackoverflow.com/questions/1491020/php-get-the-latest-file-addition-in-a-directory – Alex Feb 06 '17 at 15:44
  • This will become increasingly inefficient as the number of files increases. Is there a more memory-efficient method of doing this which doesn't involve loading the whole directory listing into memory? – HappyDog Oct 30 '19 at 21:31
  • SCANDIR_SORT_ASCENDING is only ALPHABETICAL - Has Nothing to do with DATE or TIME - it only works in this case because of the naming convention on the files the OP is using - hence this answer is confusing to those looking to see if any directory could be scanned by date. – TV-C-1-5 Mar 16 '22 at 00:07
10

For a search with wildcard you can use:

<?php
$path = "/var/www/html/*";

$latest_ctime = 0;
$latest_filename = '';

$files = glob($path);
foreach($files as $file)
{
        if (is_file($file) && filectime($file) > $latest_ctime)
        {
                $latest_ctime = filectime($file);
                $latest_filename = $file;
        }
}
return $latest_filename;
?>
Max Hofmann
  • 111
  • 1
  • 4
  • 1
    Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Zsolt Meszaros Jan 20 '21 at 15:40
  • To clear up any confusion: The [`c` in `filectime()` does not stand for `creation`](https://www.php.net/manual/en/function.filectime.php) and the [`C` in `getCTime()` does not stand for `creation`](https://www.php.net/manual/en/splfileinfo.getctime.php) – mickmackusa May 31 '23 at 07:27
0

My solution, improved solution from Max Hofmann:

$ret = [];
$dir = Yii::getAlias("@app") . "/web/uploads/problem-letters/{$this->id}"; // set directory in question

if(is_dir($dir)) {
   $ret = array_diff(scandir($dir), array(".", "..")); // get all files in dir as array and remove . and .. from it
}

usort($ret, function ($a, $b) use ($dir) {
    if(filectime($dir . "/" . $a) < filectime($dir . "/" . $b)) {
         return -1;
    } else if(filectime($dir . "/" . $a) == filectime($dir . "/" . $b)) {
         return 0;
    } else {
         return 1;
    }
}); // sort array by file creation time, older first

echo $ret[count($ret)-1]; // filename of last created file
  • To clear up any confusion: The [`c` in `filectime()` does not stand for `creation`](https://www.php.net/manual/en/function.filectime.php) and the [`C` in `getCTime()` does not stand for `creation`](https://www.php.net/manual/en/splfileinfo.getctime.php) – mickmackusa May 31 '23 at 07:27
0

Here's an example where I felt more confident in using my own validator rather than simply relying on a timestamp with scandir().

In this context, I want to check if my server has a more recent file version than the client's version. So I compare version numbers from the file names.

$clientAppVersion = "1.0.5";
$latestVersionFileName = "";

$directory = "../../download/updates/darwin/"
$arrayOfFiles = scandir($directory);
foreach ($arrayOfFiles as $file) {
    if (is_file($directory . $file)) {
        // Your custom code here... For example:
        $serverFileVersion = getVersionNumberFromFileName($file);
        if (isVersionNumberGreater($serverFileVersion, $clientAppVersion)) {
            $latestVersionFileName = $file;
        }
    }
}


// function declarations in my php file (used in the forEach loop)
function getVersionNumberFromFileName($fileName) {
    // extract the version number with regEx replacement
    return preg_replace("/Finance D - Tenue de livres-darwin-(x64|arm64)-|\.zip/", "", $fileName);
}

function removeAllNonDigits($semanticVersionString) {
    // use regex replacement to keep only numeric values in the semantic version string
    return preg_replace("/\D+/", "", $semanticVersionString);
}

function isVersionNumberGreater($serverFileVersion, $clientFileVersion): bool {
    // receives two semantic versions (1.0.4) and compares their numeric value (104)
    // true when server version is greater than client version (105 > 104)
    return removeAllNonDigits($serverFileVersion) > removeAllNonDigits($clientFileVersion);
}

Using this manual comparison instead of a timestamp I can achieve a more surgical result. I hope this can give you some useful ideas if you have a similar requirement.

(PS: I took time to post because I was not satisfied with the answers I found relating to the specific requirement I had. Please be kind I'm also not very used to StackOverflow - Thanks!)

  • Thanks for sharing your solution, but as it doesn't answer the asker's question this might not be useful here. Also, anyone searching for a similar solution as your will have a hard time finding it among the answers. If you want to share a novel idea or solution, maybe try asking a question about the problem you solved and answering it yourself. This will help others facing the same problem easily find your solution through search. – Sakibul Alam Jul 04 '21 at 15:13
  • Thanks @SakibulAlam. Should I delete it from here? – Alexandre Desroches Jul 07 '21 at 11:03