9

I there, I am currently writing a unit test which asserts that a file did not get modified. The test code execution takes less than one second and therefore I would like to know if it is possible to retrieve the file modification time in milliseconds. filemtime() function returns the UNIX timestamp in seconds.

My current solution is using the sleep(1) function which will assure me that 1 second passed before checking if it was modified or not. I don't like that solution since it slows down the test by a great deal.

I cannot assert the content equality via get_file_contents() since the data that can be rewritten would be the same.

I am guessing it is impossible, is it?

hakre
  • 193,403
  • 52
  • 435
  • 836
Steven Rosato
  • 2,174
  • 1
  • 21
  • 32

5 Answers5

10
function getTime($path){
    clearstatcache($path);
    $dateUnix = shell_exec('stat --format "%y" '.$path);
    $date = explode(".", $dateUnix);
    return filemtime($path).".".substr($date[1], 0, 8);
}

getTime("myFile");

Eliasz Kubala
  • 3,836
  • 1
  • 23
  • 28
4

Try this simple command:

ls --full-time 'filename'

and you can see the file timestamp precision is not second, it is more precise. (using Linux, but don't think it differs in Unix) but I still don't know of a PHP function for getting precise timestamp, maybe you can parse the result of the system call.

Balu
  • 41
  • 2
3

If the file system is ext4 (common on more recent unixes / linuxes like Ubuntu) or ntfs (Windows), then the mtime does have sub-second precision.

If the file system is ext3 (or perhaps others; this was the standard a while ago and is still used by RHEL), then the mtime is only stored to the nearest second. Perhaps that old default is why PHP only supports mtime to the nearest second.

To fetch the value in PHP, you need to call an external util, since PHP itself does not support it.

(I have tested the following on a system with an English locale only; the "human readable" output of stat may differ, or the strtotime behaviour may differ on non-English locales. It should work fine in any timezone, as the output of stat includes a timezone specifier which is honoured by strtotime.)

class FileModTimeHelper
{
    /**
     * Returns the file mtime for the specified file, in the format returned by microtime()
     *
     * On file systems which do not support sub-second mtime precision (such as ext3), the value
     * will be rounded to the nearest second.
     *
     * There must be a posix standard "stat" on your path (e.g. on unix or Windows with Cygwin)
     *
     * @param $filename string the name of the file
     * @return string like microtime()
     */
    public static function getFileModMicrotime($filename)
    {
        $stat = `stat --format=%y $filename`;
        $patt = '/^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\.(\d+) (.*)$/';
        if (!preg_match($patt, $stat, $matches)) {
            throw new \Exception("Unrecognised output from stat. Expecting something like '$patt', found: '$stat'");
        }
        $mtimeSeconds = strtotime("{$matches[1]} {$matches[3]}");
        $mtimeMillis = $matches[2];
        return "$mtimeSeconds.$mtimeMillis";
    }
}
Rich
  • 15,048
  • 2
  • 66
  • 119
  • (It might be safer to avoid the '`strtotime`' call by using e.g. '`stat --format="%Y %y" $filename`', but I've written this version now.) – Rich Sep 30 '13 at 17:49
2

AFAIK UNIX timestamp's precision is seconds, so this may not be a possibility.

BTW, note that PHP caches the return value of filemtime() internally, thus clearstatcache() should be called before.

An alternative method could be to modify (or delete) the contents of the file first so that you can easily identify changes. Since the state of the system should remain the same after each test is executed, it would make sense anyways to restore the original file contents after the unit test has run.

nuqqsa
  • 4,511
  • 1
  • 25
  • 30
0

Hmm. Imagine a web server where we use a combination of PHP and say for example nodeJS. In nodeJS land when we get the modification time of a file it includes the milliseconds. In PHP it does not. Now lets say that for any large files (many Gigabytes) which we allow the user to download we have and maintain corresponding file containing the pre-calculated MD5 checksum of the entire file. We store this checksum in a file with the same name as the original file but prepended also with the size and modification time of the file. INCLUDING THE MILLISECONDS! (with all of that being done by node)

Example: GEBCO_2019.nc (11723617646 bytes) and GEBCO_2019.nc.11723617646-2019-10-31T15-03-06.687Z.md5

Now on the downloads page we decide to display the MD5 checksum This is using PHP code

OH NO!!!

We call filemtime but alas we can't get the milliseconds so we can't work out the name of the Md5 file.

WE COULD CHANGE THE NODE CODE to ignore the millisecond part.

Its like the deficiency in PHP here is causing us to choose a lower common denominator than we should have to.

It's also exceptionally HORRIBLE to have to resort to running an external program just to get the file modification time. If you had to do this 50 or 60 times for a single page load I expect it would slow things down just a bit!!!

Now the accuracy of file mod time is of course system dependent. PHP should directly provide us with a way to get this time and it's a serious deficiency if it cannot.