8

Still a beginner so bear with me...
So I found this function for system uptime and have been fooling around with it as I learn about php and web development in general.
My goal is to have the output look like days:hours:mins:secs but there was no $seconds variable so I have added that line based on what else I had. Everything works great except the seconds just shows up as 0. I'm not quite sure what I am doing wrong or if this is even the best way to do this.

function Uptime() {

    $uptime = @file_get_contents( "/proc/uptime");

    $uptime = explode(" ",$uptime);
    $uptime = $uptime[0];
    $days = explode(".",(($uptime % 31556926) / 86400));
    $hours = explode(".",((($uptime % 31556926) % 86400) / 3600));
    $minutes = explode(".",(((($uptime % 31556926) % 86400) % 3600) / 60));
    $seconds = explode(".",((((($uptime % 31556926) % 86400) % 3600) / 60) / 60));

    $time = $days[0].":".$hours[0].":".$minutes[0].":".$seconds[0];

    return $time;

}

EDIT: I was able to get it working in a different way new function is below . I am also still curious if anyone can answer why the above method did not work as expected, and if the new method below is the best way to accomplish this.

function Uptime() {
    $ut = strtok( exec( "cat /proc/uptime" ), "." );
    $days = sprintf( "%2d", ($ut/(3600*24)) );
    $hours = sprintf( "%2d", ( ($ut % (3600*24)) / 3600) );
    $min = sprintf( "%2d", ($ut % (3600*24) % 3600)/60  );
    $sec = sprintf( "%2d", ($ut % (3600*24) % 3600)%60  );


    return array( $days, $hours, $min, $sec );
}
$ut = Uptime();
echo "Uptime: $ut[0]:$ut[1]:$ut[2]:$ut[3]";

EDIT 2: I believe this last method is the best based on the answer given by nwellnhof. I had to tweak a bit to get the output exactly as I wanted. Thanks guys.

function Uptime() {
        $str   = @file_get_contents('/proc/uptime');
        $num   = floatval($str);
        $secs  = $num % 60;
        $num   = (int)($num / 60);
        $mins  = $num % 60;
        $num   = (int)($num / 60);
        $hours = $num % 24;
        $num   = (int)($num / 24);
        $days  = $num;

        return array(
            "days"  => $days,
            "hours" => $hours,
            "mins"  => $mins,
            "secs"  => $secs
        );
    }
Ethan Morris
  • 95
  • 1
  • 1
  • 8

5 Answers5

10

Reading directly from /proc/uptime is the most efficient solution on Linux. There are multiple ways to convert the output to days/hours/minutes/seconds. Try something like:

$str   = @file_get_contents('/proc/uptime');
$num   = floatval($str);
$secs  = fmod($num, 60); $num = (int)($num / 60);
$mins  = $num % 60;      $num = (int)($num / 60);
$hours = $num % 24;      $num = (int)($num / 24);
$days  = $num;

Or, with intdiv (PHP7):

$str   = @file_get_contents('/proc/uptime');
$num   = floatval($str);
$secs  = fmod($num, 60); $num = intdiv($num, 60);
$mins  = $num % 60;      $num = intdiv($num, 60);
$hours = $num % 24;      $num = intdiv($num, 24);
$days  = $num;
nwellnhof
  • 32,319
  • 7
  • 89
  • 113
  • Just out of curiosity, why is reading from /proc/uptime more efficient? – Ethan Morris Aug 13 '16 at 13:33
  • 3
    @EthanMorris Executing `uptime` or `cat /proc/cpuinfo` spawns a new process (fork/exec on Unix) which takes a couple of milliseconds. This new process then reads `/proc/cpuinfo` and pipes the output back to the original process. Reading `/proc/cpuinfo` directly avoids all the overhead. – nwellnhof Aug 13 '16 at 13:52
4

uptime supports the -p command line option. You can use this simple piece of code:

echo shell_exec('uptime -p');
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • What exactly does the -s argument do? – Ethan Morris Aug 13 '16 at 13:03
  • @EthanMorris Looking at this again, I think it is more the `-p` option which you want. – hek2mgl Aug 13 '16 at 13:09
  • Interesting... when I tried -p It gave me a completely random date, but when I tried -s it gave me the current date and a completely random time. – Ethan Morris Aug 13 '16 at 13:14
  • What do you mean with *completely random*? Note that the options are different. `-p` (pretty) prints the amount of time the machine is up. `-s` prints the time when the machine booted. – hek2mgl Aug 13 '16 at 13:18
  • Ahh sorry, im still a newbie. What I meant was the time was not what I was expecting (the uptime since last boot), they did't match the output from htop uptime so i just figured it was wrong. Makes sense it is a different parameter though. – Ethan Morris Aug 13 '16 at 13:25
  • `uptime -p` will give you that. Whats wrong with that output? (Just asking) – hek2mgl Aug 13 '16 at 13:26
  • 1
    Nevermind I was looking at it wrong the -p output is correct, It was just cut off in my html. – Ethan Morris Aug 13 '16 at 13:31
  • `-p` argument for `uptime` does not exist on Unix. – kenorb Jul 10 '17 at 14:04
  • Depends on the source where you installed `uptime` from. The version from `procps` supports it. – hek2mgl Jul 10 '17 at 14:16
1

variation of your initial example as a class:

class Uptime {
    private $uptime;

    private $modVals = array(31556926, 86400, 3600, 60, 60);

    public function __construct() {
        $this->read_uptime();
    }

    /**
     * actually trigger a read of the system clock and cache the value
     * @return string
     */
    private function read_uptime() {
        $uptime_raw = @file_get_contents("/proc/uptime");
        $this->uptime = floatval($uptime_raw);
        return $this->uptime;
    }

    private function get_uptime_cached() {
        if(is_null($this->uptime)) $this->read_uptime(); // only read if not yet stored or empty
        return $this->uptime;
    }

    /**
     * recursively run mods on time value up to given depth
     * @param int $d
     * @return int
     **/
    private function doModDep($d) {
        $start = $this->get_uptime_cached();
        for($i=0;$i<$d;$i++) {
            $start = $start % $this->modVals[$i];
        }
        return intval($start / $this->modVals[$d]);
    }

    public function getDays()
    {
        return $this->doModDep(1);
    }

    public function getHours() {
        return $this->doModDep(2);
    }

    public function getMinutes()
    {
        return $this->doModDep(3);
    }

    public function getSeconds()
    {
        return $this->doModDep(4);
    }

    public function getTime($cached=false) {
        if($cached != false) $this->read_uptime(); // resample cached system clock value
        return sprintf("%03d:%02d:%02d:%02d", $this->getDays(), $this->getHours(), $this->getMinutes(), $this->getSeconds());
    }
}
Scott
  • 7,983
  • 2
  • 26
  • 41
0

If you just look at the pattern in your statements, you can see that the one for seconds is different. It has two divisions. Additionally, the numbers you are using represent the number of seconds per time unit. The number of seconds per second should be 1, not 60. In short:

$seconds = explode(".",((((($uptime % 31556926) % 86400) % 3600) / 60) / 60));

Should be:

$seconds = explode(".",((((($uptime % 31556926) % 86400) % 3600) % 60) / 1));

Now this whole way of doing things is a bit weird. For example, (x % (n*m)) % m is just x % m.

A nicer way would be to do:

$uptime  = (int) $uptime;
$seconds =  $uptime               % 60;
$minutes = ($uptime /  60       ) % 60;
$hours   = ($uptime / (60*60)   ) % 24;
$days    =  $uptime / (60*60*24); # % 365, if you want
mweerden
  • 13,619
  • 5
  • 32
  • 32
  • Never ever do such calculations since they will be wrong when daylight saving time changes occur. Use PHP's Date/Time functions for that. – hek2mgl Aug 13 '16 at 13:20
  • @hek2mgl Never is a big strong; it depends on what one wants and how it is used. Also, it should be a comment on the OP. This is just a nicer way to write what was already posted. – mweerden Aug 13 '16 at 13:27
  • I'm just trying to learn honestly, every answer so far has been helpful. Although @hek2mgl does make a good point about daylight savings. – Ethan Morris Aug 13 '16 at 13:38
  • @hek2mgl If you only want to convert to some kind of human readable duration, it's perfectly fine to define a day as 24 hours (86,400 seconds). The `uptime` command from GNU coreutils [does the same](http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/uptime.c;h=74ea87d9fe5b8832fcf3b9a42345bd5bfd8ebe1f;hb=HEAD#l128). – nwellnhof Aug 13 '16 at 13:59
  • @nwellnhof Interesting. I have `uptime` from procps rather than coreutils. That version is using `localtime()` which is daylight time aware. – hek2mgl Aug 13 '16 at 14:38
  • @hek2mgl `uptime` from procps [also divides by 86,400](http://procps.cvs.sourceforge.net/viewvc/procps/procps/proc/whattime.c?view=markup#l50) without accounting for DST. `localtime` is only called to print the current time in front of the uptime. – nwellnhof Aug 13 '16 at 19:09
  • There are different versions of `procps`. The one from Ubuntu is not doing that. I wanted to show the link but it is a [dead end](https://launchpad.net/procps-ng/trunk) Mysterious procps package! Check `apt-get source procps` if you have Ubuntu by the hand. – hek2mgl Aug 14 '16 at 19:34
  • Nevermind. Just had a closer look to the code. Also that version is not dst aware. Probably that's really not desired in this case. – hek2mgl Aug 14 '16 at 21:10
0

On Unix/BSD, using /proc is not reliable since it is not mounted by default, on some Linux distributions it can be unmounted also, so it's better to parse using either uptime or sysctl command, e.g.

sysctl

<?php
preg_match('/sec = (\d+)/', shell_exec('sysctl -n kern.boottime'), $secs)
echo $secs[1];

or:

$s = explode( " ", exec("/sbin/sysctl -n kern.boottime") );
$a = str_replace( ",", "", $s[3]);
$uptime = time() - $a;  

or as per example taken from m0n0wall:

<?php
exec("/sbin/sysctl -n kern.boottime", $boottime);
preg_match("/sec = (\d+)/", $boottime[0], $matches);
$boottime = $matches[1];
$uptime = time() - $boottime;

if ($uptime > 60)
    $uptime += 30;
$updays = (int)($uptime / 86400);
$uptime %= 86400;
$uphours = (int)($uptime / 3600);
$uptime %= 3600;
$upmins = (int)($uptime / 60);

$uptimestr = "";
if ($updays > 1)
    $uptimestr .= "$updays days, ";
else if ($updays > 0)
    $uptimestr .= "1 day, ";
$uptimestr .= sprintf("%02d:%02d", $uphours, $upmins);
echo htmlspecialchars($uptimestr);

uptime

Example taken from 4webhelp:

<?php
$data = shell_exec('uptime');
$uptime = explode(' up ', $data);
$uptime = explode(',', $uptime[1]);
$uptime = $uptime[0].', '.$uptime[1];
echo ('Current server uptime: '.$uptime.'

or (tested on FreeBSD):

$uptime = exec("uptime");
$uptime = split(" ",$uptime);
$days = $uptime[3]; # NetBSD: $days = $uptime[4];
$time = split(",",$uptime[5]); # NetBSD: $time = split(",",$uptime[7]);
if (sizeof($hourmin = split(":",$time[0])) < 2){ ;
  $hours = "0";
  $mins = $hourmin[0];
} else {
  $hourmin=split(":",$time[0]);
  $hours = $hourmin[0];
  $mins = $hourmin[1];
}
$calcuptime =  "Uptime: ".$days." days ".$hours." hours ".$mins." mins" ;
echo $calcuptime;

Here is version which works for Windows:

<?php
$uptime = `c:\windows\system32\uptime2.bat $server`;
$uptime = explode(": ", $uptime);
$uptime = explode(", ", $uptime[1]);

$uptime_days = preg_replace($pattern, '', $uptime[0]);
$uptime_hours = preg_replace($pattern, '', $uptime[1]);
$uptime_minutes = preg_replace($pattern, '', $uptime[2]);
$uptime_seconds = preg_replace($pattern, '', $uptime[3]);

echo '<b>Uptime:</b><br><br>';

echo 'Days: '.$uptime_days.'<br>';
echo 'Hours: '.$uptime_hours.'<br>';
echo 'Minutes: '.$uptime_minutes.'<br>';
echo 'Seconds: '.$uptime_seconds.'<br>';
kenorb
  • 155,785
  • 88
  • 678
  • 743