Skip to content

Instantly share code, notes, and snippets.

@thijzert
Created June 11, 2012 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thijzert/2910214 to your computer and use it in GitHub Desktop.
Save thijzert/2910214 to your computer and use it in GitHub Desktop.
A PHP progress bar
<?php
require_once( "termcolours.inc" );
class ProgressBar
{
protected $ticktime = false;
protected $exp;
protected $xsquare;
protected $count = 0;
protected $max = 100;
protected $lmax = 0;
protected $variance = 0;
protected $line_size = 80;
protected $line_pos = 0;
public function __construct( $max = 100 )
{
$this->reset( $max );
}
public function reset( $max = 100 )
{
$this->max = $max;
$this->lmax = (floor(log10($max))+1); // number of digits in $max;
$this->exp = new Expectation();
$this->xsquare = new Expectation();
$this->stop();
$this->start();
}
public function start()
{
if ( $this->ticktime !== false )
throw new Exception( "Timer already started." );
$this->ticktime = microtime( true );
$this->detect_line_size();
}
public function stop()
{
if ( $this->line_pos > 0 )
$this->big_tick();
$this->ticktime = false;
$this->line_pos = 0;
}
public function tick()
{
$this->count++;
$time = microtime(true) - $this->ticktime;
// Update expected value and variance
$delta = $this->exp->point( $time );
$this->xsquare->point( $time * $time );
if ( $this->variance <= 0 )
print( "." );
elseif ( $delta < -1*($this->variance) )
print( green(".") );
elseif ( $delta < $this->variance )
print( "." );
elseif ( $delta < 2*$this->variance )
print( yellow(".") );
else
print( bred(".") );
$this->line_pos++;
if ( $this->line_pos == $this->line_size )
$this->big_tick();
$this->ticktime = microtime(true);
}
protected function detect_line_size()
{
@list($height,$width) = explode(" ",shell_exec("stty size 2>/dev/null"));
if ( !$width || $width < 40 )
$width = 80;
$infobox_size = 2 + // Left spacing
1 + // [
(2 * $this->lmax) + 1 + // "123/456" progress indicator
1 + // a space
6 + // "(100%)" progress indicator
1 + // another space
4 + (2+1+2+1) + // "ETA 10m24s" progress indicator
// 1 + // yet another space
1 + // ]
1; // Right spacing
$this->line_size = $width - $infobox_size;
}
protected function big_tick()
{
$exp = $this->exp->exp();
$this->variance = sqrt( ($this->xsquare->exp()) - ($exp*$exp) );
print( str_repeat(" ",($this->line_size - $this->line_pos)) );
print( " [" );
print( str_pad( "{$this->count}/{$this->max}", (2 * $this->lmax) + 1, " ", STR_PAD_LEFT ) . " " );
$perc = floor( 100 * ($this->count / $this->max) );
print( str_pad( "({$perc}%)", 6, " ", STR_PAD_LEFT ) . " " );
print( "ETA " . $this->format_time($this->ETR()) );
print( "]\n" );
$this->line_pos = 0;
}
/**
* Estimate Time Remaining (in seconds)
*
* Calculates a pessimistic average of the average time between ticks, and
* multiplies that time by the number of ticks remaining.
**/
protected function ETR()
{
$count = $this->max - $this->count;
if ( $count <= 0 ) return 0;
return $count * ( $this->exp->exp() + sqrt($this->xsquare->exp()) );
}
protected function format_time( $time )
{
if ( $time < 3600 )
{
$s = str_pad( floor($time % 60), 2, "0", STR_PAD_LEFT );
$m = str_pad( floor($time / 60), 2, " ", STR_PAD_LEFT );
return "{$m}m{$s}s";
}
if ( $time < 86400 )
{
$m = str_pad( round(($time % 3600)/60), 2, "0", STR_PAD_LEFT );
$h = floor($time / 3600);
if ( $m >= "60" )
{
$m = "00";
$h++;
}
$h = str_pad( $h, 2, " ", STR_PAD_LEFT );
return "{$h}h{$m}m";
}
if ( $time < 86400*30 )
{
$h = str_pad( round(($time % 86400)/3600), 2, "0", STR_PAD_LEFT );
$d = floor($time / 86400);
if ( $h >= "24" )
{
$h = "00";
$d++;
}
$d = str_pad( $d, 2, " ", STR_PAD_LEFT );
return "{$d}d{$h}h";
}
if ( $time < 86400*365 )
{
$d = str_pad( round(($time % 86400*30)/86400), 2, "0", STR_PAD_LEFT );
$m = floor($time / 86400*30);
if ( $d >= "30" )
{
$d = "00";
$m++;
}
$m = str_pad( $d, 2, " ", STR_PAD_LEFT );
return "{$m}m{$d}d";
}
$m = str_pad( round(($time % 86400*365)/86400*30), 2, "0", STR_PAD_LEFT );
$y = floor($time / 86400*365);
if ( $m >= 12 )
{
$m = "00";
$y++;
}
$y = str_pad( $d, 2, " ", STR_PAD_LEFT );
return "{$y}y{$d}d";
}
}
class Expectation
{
private $count = 0;
private $sum = 0;
public function point( $p )
{
$this->count++;
$this->sum += $p;
return $p - $this->exp();
}
public function exp()
{
return $this->sum / $this->count;
}
}
if ( basename(@$argv[0]) == basename(__FILE__) )
{
$max = mt_rand( 150, 500 );
$pb = new ProgressBar( $max );
require_once( "stats.inc" );
$av = pow( 1.25, mt_rand( 40, 61 ) );
$sd = $av * 0.15;
for ( $i = 0; $i < $max; $i++ )
{
usleep( St::normal( $av, $sd ) );
$pb->tick();
}
$pb->stop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment