Console_ProgressBar
[ class tree: Console_ProgressBar ] [ index: Console_ProgressBar ] [ all elements ]

Source for file ProgressBar.php

Documentation is available at ProgressBar.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
  3.  
  4. // Copyright (c) 2007 Stefan Walk
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to
  8. // deal in the Software without restriction, including without limitation the
  9. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  10. // sell copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  21. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  22. // IN THE SOFTWARE.
  23.  
  24.  
  25. // Authors: Stefan Walk <et@php.net>
  26.  
  27. /**
  28.  * Class to display a progressbar in the console
  29.  *
  30.  * @package Console_ProgressBar
  31.  * @category Console
  32.  * @version 0.5.2
  33.  * @author Stefan Walk <et@php.net>
  34.  * @license MIT License
  35.  */
  36.  
  37.  
  38.     // properties {{{
  39.     /**
  40.      * Skeleton for use with sprintf
  41.      */
  42.     var $_skeleton;
  43.     /**
  44.      * The bar gets filled with this
  45.      */
  46.     var $_bar;
  47.     /**
  48.      * The width of the bar
  49.      */
  50.     var $_blen;
  51.     /**
  52.      * The total width of the display
  53.      */
  54.     var $_tlen;
  55.     /**
  56.      * The position of the counter when the job is `done'
  57.      */
  58.     var $_target_num;
  59.     /**
  60.      * Options, like the precision used to display the numbers
  61.      */
  62.     var $_options = array();
  63.     /**
  64.      * Length to erase
  65.      */
  66.     var $_rlen = 0;
  67.     /**
  68.      * When the progress started
  69.      */
  70.     var $_start_time = null;
  71.     var $_rate_datapoints = array();
  72.     /**
  73.      * Time when the bar was last drawn
  74.      */
  75.     var $_last_update_time = 0.0;
  76.     // }}}
  77.     
  78.     // constructor() {{{
  79.     /** 
  80.      * Constructor, sets format and size
  81.      *
  82.      * See the reset() method for documentation.
  83.      *
  84.      * @param string The format string
  85.      * @param string The string filling the progress bar
  86.      * @param string The string filling empty space in the bar
  87.      * @param int    The width of the display
  88.      * @param float  The target number for the bar
  89.      * @param array  Options for the progress bar
  90.      * @see reset
  91.      */ 
  92.     function Console_ProgressBar($formatstring$bar$prefill$width
  93.                                   $target_num$options = array()) 
  94.     {
  95.         $this->reset($formatstring$bar$prefill$width$target_num
  96.                      $options);
  97.     }
  98.     // }}}
  99.  
  100.     // {{{ reset($formatstring, $bar, $prefill, $width, $target_num[, $options])
  101.     /**
  102.      * Re-sets format and size.
  103.      *
  104.      * <pre>
  105.      * The reset method expects 5 to 6 arguments:
  106.      * - The first argument is the format string used to display the progress
  107.      *   bar. It may (and should) contain placeholders that the class will
  108.      *   replace with information like the progress bar itself, the progress in
  109.      *   percent, and so on. Current placeholders are:
  110.      *     %bar%         The progress bar
  111.      *     %current%     The current value
  112.      *     %max%         The maximum malue (the "target" value)
  113.      *     %fraction%    The same as %current%/%max%
  114.      *     %percent%     The status in percent
  115.      *     %elapsed%     The elapsed time
  116.      *     %estimate%    An estimate of how long the progress will take
  117.      *   More placeholders will follow. A format string like:
  118.      *   "* stuff.tar %fraction% KB [%bar%] %percent%"
  119.      *   will lead to a bar looking like this:
  120.      *   "* stuff.tar 391/900 KB [=====>---------]  43.44%"
  121.      * - The second argument is the string that is going to fill the progress
  122.      *   bar. In the above example, the string "=>" was used. If the string you
  123.      *   pass is too short (like "=>" in this example), the leftmost character
  124.      *   is used to pad it to the needed size. If the string you pass is too long,
  125.      *   excessive characters are stripped from the left.
  126.      * - The third argument is the string that fills the "empty" space in the
  127.      *   progress bar. In the above example, that would be "-". If the string
  128.      *   you pass is too short (like "-" in this example), the rightmost
  129.      *   character is used to pad it to the needed size. If the string you pass
  130.      *   is too short, excessive characters are stripped from the right.
  131.      * - The fourth argument specifies the width of the display. If the options
  132.      *   are left untouched, it will tell how many characters the display should
  133.      *   use in total. If the "absolute_width" option is set to false, it tells
  134.      *   how many characters the actual bar (that replaces the %bar%
  135.      *   placeholder) should use.
  136.      * - The fifth argument is the target number of the progress bar. For
  137.      *   example, if you wanted to display a progress bar for a download of a
  138.      *   file that is 115 KB big, you would pass 115 here.
  139.      * - The sixth argument optional. If passed, it should contain an array of
  140.      *   options. For example, passing array('absolute_width' => false) would
  141.      *   set the absolute_width option to false. Current options are:
  142.      *
  143.      *     option             | def.  |  meaning
  144.      *     --------------------------------------------------------------------
  145.      *     percent_precision  | 2     |  Number of decimal places to show when
  146.      *                        |       |  displaying the percentage.
  147.      *     fraction_precision | 0     |  Number of decimal places to show when
  148.      *                        |       |  displaying the current or target
  149.      *                        |       |  number.
  150.      *     percent_pad        | ' '   |  Character to use when padding the
  151.      *                        |       |  percentage to a fixed size. Senseful
  152.      *                        |       |  values are ' ' and '0', but any are
  153.      *                        |       |  possible.
  154.      *     fraction_pad       | ' '   |  Character to use when padding max and
  155.      *                        |       |  current number to a fixed size.
  156.      *                        |       |  Senseful values are ' ' and '0', but
  157.      *                        |       |  any are possible.
  158.      *     width_absolute     | true  |  If the width passed as an argument
  159.      *                        |       |  should mean the total size (true) or
  160.      *                        |       |  the width of the bar alone.
  161.      *     ansi_terminal      | false |  If this option is true, a better
  162.      *                        |       |  (faster) method for erasing the bar is
  163.      *                        |       |  used. CAUTION - this is known to cause
  164.      *                        |       |  problems with some terminal emulators,
  165.      *                        |       |  for example Eterm.
  166.      *     ansi_clear         | false |  If the bar should be cleared everytime
  167.      *     num_datapoints     | 5     |  How many datapoints to use to create
  168.      *                        |       |  the estimated remaining time
  169.      *     min_draw_interval  | 0.0   |  If the last call to update() was less
  170.      *                        |       |  than this amount of seconds ago,
  171.      *                        |       |  don't update.
  172.      * </pre>
  173.      *
  174.      * @param string The format string
  175.      * @param string The string filling the progress bar
  176.      * @param string The string filling empty space in the bar
  177.      * @param int    The width of the display
  178.      * @param float  The target number for the bar
  179.      * @param array  Options for the progress bar
  180.      * @return bool 
  181.      */
  182.     function reset($formatstring$bar$prefill$width$target_num
  183.                    $options = array()) 
  184.     {
  185.         if ($target_num == 0{
  186.             trigger_error("PEAR::Console_ProgressBar: Using a target number equal to 0 is invalid, setting to 1 instead");
  187.             $this->_target_num = 1;
  188.         else {
  189.             $this->_target_num $target_num;
  190.         }
  191.         $default_options = array(
  192.             'percent_precision' => 2,
  193.             'fraction_precision' => 0,
  194.             'percent_pad' => ' ',
  195.             'fraction_pad' => ' ',
  196.             'width_absolute' => true,
  197.             'ansi_terminal' => false,
  198.             'ansi_clear' => false,
  199.             'num_datapoints' => 5,
  200.             'min_draw_interval' => 0.0,
  201.         );
  202.         $intopts = array();
  203.         foreach ($default_options as $key => $value{
  204.             if (!isset($options[$key])) {
  205.                 $intopts[$key$value;
  206.             else {
  207.                 settype($options[$key]gettype($value));
  208.                 $intopts[$key$options[$key];
  209.             }
  210.         }
  211.         $this->_options $options $intopts;
  212.         // placeholder
  213.         $cur '%2$\''.$options['fraction_pad']{0}.strlen((int)$target_num).'.'
  214.                .$options['fraction_precision'].'f';
  215.         $max $cur$max{1= 3;
  216.         // pre php-4.3.7 %3.2f meant 3 characters before . and two after
  217.         // php-4.3.7 and later it means 3 characters for the whole number
  218.         if (version_compare(PHP_VERSION'4.3.7''ge')) {
  219.             $padding = 4 + $options['percent_precision'];
  220.         else {
  221.             $padding = 3;
  222.         }
  223.         $perc '%4$\''.$options['percent_pad']{0}.$padding.'.'
  224.                 .$options['percent_precision'].'f';
  225.         
  226.         $transitions = array(
  227.             '%%' => '%%',
  228.             '%fraction%' => $cur.'/'.$max,
  229.             '%current%' => $cur,
  230.             '%max%' => $max,
  231.             '%percent%' => $perc.'%%',
  232.             '%bar%' => '%1$s',
  233.             '%elapsed%' => '%5$s',
  234.             '%estimate%' => '%6$s',
  235.         );
  236.         
  237.         $this->_skeleton strtr($formatstring$transitions);
  238.  
  239.         $slen strlen(sprintf($this->_skeleton''000'00:00:00','00:00:00'));
  240.  
  241.         if ($options['width_absolute']{
  242.             $blen $width $slen;
  243.             $tlen $width;
  244.         else {
  245.             $tlen $width $slen;
  246.             $blen $width;
  247.         }
  248.  
  249.         $lbar str_pad($bar$blen$bar{0}STR_PAD_LEFT);
  250.         $rbar str_pad($prefill$blensubstr($prefill-11));
  251.  
  252.         $this->_bar   substr($lbar,-$blen).substr($rbar,0,$blen);
  253.         $this->_blen  $blen;
  254.         $this->_tlen  $tlen;
  255.         $this->_first = true;
  256.  
  257.  
  258.         return true;
  259.     }
  260.     // }}}
  261.     
  262.     // {{{ update($current)
  263.     /**
  264.      * Updates the bar with new progress information
  265.      *
  266.      * @param int current position of the progress counter
  267.      * @return bool 
  268.      */
  269.     function update($current)
  270.     {
  271.         $time $this->_fetchTime();
  272.         $this->_addDatapoint($current$time);
  273.         if ($this->_first{
  274.             if ($this->_options['ansi_terminal']{
  275.                 echo "\x1b[s"// save cursor position
  276.             }
  277.             $this->_first = false;
  278.             $this->_start_time $this->_fetchTime();
  279.             $this->display($current);
  280.             return;
  281.         }
  282.         if ($time $this->_last_update_time 
  283.             $this->_options['min_draw_interval'and $current != $this->_target_num{
  284.             return;
  285.         }
  286.         $this->erase();
  287.         $this->display($current);
  288.         $this->_last_update_time $time;
  289.     }
  290.     // }}}
  291.     
  292.     // {{{ display($current)
  293.     /**
  294.      * Prints the bar. Usually, you don't need this method, just use update()
  295.      * which handles erasing the previously printed bar also. If you use a
  296.      * custom function (for whatever reason) to erase the bar, use this method.
  297.      *
  298.      * @param int current position of the progress counter
  299.      * @return bool 
  300.      */
  301.     function display($current
  302.     {
  303.         $percent $current $this->_target_num;
  304.         $filled round($percent $this->_blen);
  305.         $visbar substr($this->_bar$this->_blen $filled$this->_blen);
  306.         $elapsed $this->_formatSeconds(
  307.             $this->_fetchTime($this->_start_time
  308.         );
  309.         $estimate $this->_formatSeconds($this->_generateEstimate());
  310.         $this->_rlen printf($this->_skeleton
  311.             $visbar$current$this->_target_num$percent * 100$elapsed,
  312.             $estimate
  313.         );
  314.         // fix for php-versions where printf doesn't return anything
  315.         if (is_null($this->_rlen)) {
  316.             $this->_rlen $this->_tlen;
  317.         // fix for php versions between 4.3.7 and 5.x.y(?)
  318.         elseif ($this->_rlen $this->_tlen{
  319.             echo str_repeat(' '$this->_tlen $this->_rlen);
  320.             $this->_rlen $this->_tlen;
  321.         
  322.         return true;
  323.     }
  324.     // }}}
  325.  
  326.     // {{{ erase($clear = false)
  327.     /**
  328.      * Erases a previously printed bar.
  329.      *
  330.      * @param bool if the bar should be cleared in addition to resetting the
  331.      *              cursor position
  332.      * @return bool 
  333.      */
  334.     function erase($clear = false
  335.     {
  336.         if ($this->_options['ansi_terminal'and !$clear{
  337.             if ($this->_options['ansi_clear']{
  338.                 echo "\x1b[2K\x1b[u"// restore cursor position
  339.             else {
  340.                 echo "\x1b[u"// restore cursor position
  341.             }
  342.         elseif (!$clear{
  343.             echo str_repeat(chr(0x08)$this->_rlen);
  344.         else {
  345.             echo str_repeat(chr(0x08)$this->_rlen),
  346.                  str_repeat(chr(0x20)$this->_rlen),
  347.                  str_repeat(chr(0x08)$this->_rlen);
  348.         }
  349.     }
  350.     // }}}
  351.  
  352.     // {{{ format_seconds()
  353.     /**
  354.      * Returns a string containing the formatted number of seconds
  355.      *
  356.      * @param float The number of seconds
  357.      * @return string 
  358.      */
  359.     function _formatSeconds($seconds
  360.     {
  361.         $hou floor($seconds/3600);
  362.         $min floor(($seconds $hou * 3600/ 60);
  363.         $sec $seconds $hou * 3600 - $min * 60;
  364.         if ($hou == 0{
  365.             if (version_compare(PHP_VERSION'4.3.7''ge')) {
  366.                 $format '%2$02d:%3$05.2f';
  367.             else {
  368.                 $format '%2$02d:%3$02.2f';
  369.             }
  370.         elseif ($hou < 100{
  371.             $format '%02d:%02d:%02d';
  372.         else {
  373.             $format '%05d:%02d';
  374.         }
  375.         return sprintf($format$hou$min$sec);
  376.     }
  377.     // }}}
  378.  
  379.     function _fetchTime({
  380.         if (!function_exists('microtime')) {
  381.             return time();
  382.         }
  383.         if (version_compare(PHP_VERSION'5.0.0''ge')) {
  384.             return microtime(true);
  385.         }
  386.         return array_sum(explode(' 'microtime()));
  387.     }
  388.  
  389.     function _addDatapoint($val$time{
  390.         if (count($this->_rate_datapoints
  391.             == $this->_options['num_datapoints']{
  392.             array_shift($this->_rate_datapoints);
  393.         }
  394.         $this->_rate_datapoints[= array(
  395.             'time' => $time,
  396.             'value' => $val,
  397.         );
  398.     }
  399.  
  400.     function _generateEstimate({
  401.         if (count($this->_rate_datapoints< 2{
  402.             return 0.0;
  403.         }
  404.         $first $this->_rate_datapoints[0];
  405.         $last end($this->_rate_datapoints);
  406.         return ($this->_target_num $last['value'])/($last['value'$first['value']($last['time'$first['time']);
  407.     }
  408. }

Documentation generated on Sun, 25 Nov 2007 13:30:03 -0500 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.