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) 2006 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. // $Id: ProgressBar.php,v 1.6 2007/01/31 16:42:21 et Exp $
  27.  
  28.  
  29. /**
  30.  * Class to display a progressbar in the console
  31.  *
  32.  * @package Console_ProgressBar
  33.  * @category Console
  34.  * @version 0.4
  35.  * @author Stefan Walk <et@php.net>
  36.  * @license MIT License
  37.  */
  38.  
  39.  
  40.     // properties {{{
  41.     /**
  42.      * Skeleton for use with sprintf
  43.      */
  44.     var $_skeleton;
  45.     /**
  46.      * The bar gets filled with this
  47.      */
  48.     var $_bar;
  49.     /**
  50.      * The width of the bar
  51.      */
  52.     var $_blen;
  53.     /**
  54.      * The total width of the display
  55.      */
  56.     var $_tlen;
  57.     /**
  58.      * The position of the counter when the job is `done'
  59.      */
  60.     var $_target_num;
  61.     /**
  62.      * Options, like the precision used to display the numbers
  63.      */
  64.     var $_options = array();
  65.     /**
  66.      * Length to erase
  67.      */
  68.     var $_rlen = 0;
  69.     /**
  70.      * When the progress started
  71.      */
  72.     var $_start_time = null;
  73.     var $_rate_datapoints = array();
  74.     /**
  75.      * Time when the bar was last drawn
  76.      */
  77.     var $_last_update_time = 0.0;
  78.     // }}}
  79.     
  80.     // constructor() {{{
  81.     /** 
  82.      * Constructor, sets format and size
  83.      *
  84.      * See the reset() method for documentation.
  85.      *
  86.      * @param string The format string
  87.      * @param string The string filling the progress bar
  88.      * @param string The string filling empty space in the bar
  89.      * @param int    The width of the display
  90.      * @param float  The target number for the bar
  91.      * @param array  Options for the progress bar
  92.      * @see reset
  93.      */ 
  94.     function Console_ProgressBar($formatstring$bar$prefill$width
  95.                                   $target_num$options = array()) 
  96.     {
  97.         $this->reset($formatstring$bar$prefill$width$target_num
  98.                      $options);
  99.     }
  100.     // }}}
  101.  
  102.     // {{{ reset($formatstring, $bar, $prefill, $width, $target_num[, $options])
  103.     /**
  104.      * Re-sets format and size.
  105.      *
  106.      * <pre>
  107.      * The reset method expects 5 to 6 arguments:
  108.      * - The first argument is the format string used to display the progress
  109.      *   bar. It may (and should) contain placeholders that the class will
  110.      *   replace with information like the progress bar itself, the progress in
  111.      *   percent, and so on. Current placeholders are:
  112.      *     %bar%         The progress bar
  113.      *     %current%     The current value
  114.      *     %max%         The maximum malue (the "target" value)
  115.      *     %fraction%    The same as %current%/%max%
  116.      *     %percent%     The status in percent
  117.      *     %elapsed%     The elapsed time
  118.      *     %estimate%    An estimate of how long the progress will take
  119.      *   More placeholders will follow. A format string like:
  120.      *   "* stuff.tar %fraction% KB [%bar%] %percent%"
  121.      *   will lead to a bar looking like this:
  122.      *   "* stuff.tar 391/900 KB [=====>---------]  43.44%"
  123.      * - The second argument is the string that is going to fill the progress
  124.      *   bar. In the above example, the string "=>" was used. If the string you
  125.      *   pass is too short (like "=>" in this example), the leftmost character
  126.      *   is used to pad it to the needed size. If the string you pass is too long,
  127.      *   excessive characters are stripped from the left.
  128.      * - The third argument is the string that fills the "empty" space in the
  129.      *   progress bar. In the above example, that would be "-". If the string
  130.      *   you pass is too short (like "-" in this example), the rightmost
  131.      *   character is used to pad it to the needed size. If the string you pass
  132.      *   is too short, excessive characters are stripped from the right.
  133.      * - The fourth argument specifies the width of the display. If the options
  134.      *   are left untouched, it will tell how many characters the display should
  135.      *   use in total. If the "absolute_width" option is set to false, it tells
  136.      *   how many characters the actual bar (that replaces the %bar%
  137.      *   placeholder) should use.
  138.      * - The fifth argument is the target number of the progress bar. For
  139.      *   example, if you wanted to display a progress bar for a download of a
  140.      *   file that is 115 KB big, you would pass 115 here.
  141.      * - The sixth argument optional. If passed, it should contain an array of
  142.      *   options. For example, passing array('absolute_width' => false) would
  143.      *   set the absolute_width option to false. Current options are:
  144.      *
  145.      *     option             | def.  |  meaning
  146.      *     --------------------------------------------------------------------
  147.      *     percent_precision  | 2     |  Number of decimal places to show when
  148.      *                        |       |  displaying the percentage.
  149.      *     fraction_precision | 0     |  Number of decimal places to show when
  150.      *                        |       |  displaying the current or target
  151.      *                        |       |  number.
  152.      *     percent_pad        | ' '   |  Character to use when padding the
  153.      *                        |       |  percentage to a fixed size. Senseful
  154.      *                        |       |  values are ' ' and '0', but any are
  155.      *                        |       |  possible.
  156.      *     fraction_pad       | ' '   |  Character to use when padding max and
  157.      *                        |       |  current number to a fixed size.
  158.      *                        |       |  Senseful values are ' ' and '0', but
  159.      *                        |       |  any are possible.
  160.      *     width_absolute     | true  |  If the width passed as an argument
  161.      *                        |       |  should mean the total size (true) or
  162.      *                        |       |  the width of the bar alone.
  163.      *     ansi_terminal      | false |  If this option is true, a better
  164.      *                        |       |  (faster) method for erasing the bar is
  165.      *                        |       |  used. CAUTION - this is known to cause
  166.      *                        |       |  problems with some terminal emulators,
  167.      *                        |       |  for example Eterm.
  168.      *     ansi_clear         | false |  If the bar should be cleared everytime
  169.      *     num_datapoints     | 5     |  How many datapoints to use to create
  170.      *                        |       |  the estimated remaining time
  171.      *     min_draw_interval  | 0.0   |  If the last call to update() was less
  172.      *                        |       |  than this amount of seconds ago,
  173.      *                        |       |  don't update.
  174.      * </pre>
  175.      *
  176.      * @param string The format string
  177.      * @param string The string filling the progress bar
  178.      * @param string The string filling empty space in the bar
  179.      * @param int    The width of the display
  180.      * @param float  The target number for the bar
  181.      * @param array  Options for the progress bar
  182.      * @return bool 
  183.      */
  184.     function reset($formatstring$bar$prefill$width$target_num
  185.                    $options = array()) 
  186.     {
  187.         if ($target_num == 0{
  188.             trigger_error("PEAR::Console_ProgressBar: Using a target number equal to 0 is invalid, setting to 1 instead");
  189.             $this->_target_num = 1;
  190.         else {
  191.             $this->_target_num $target_num;
  192.         }
  193.         $default_options = array(
  194.             'percent_precision' => 2,
  195.             'fraction_precision' => 0,
  196.             'percent_pad' => ' ',
  197.             'fraction_pad' => ' ',
  198.             'width_absolute' => true,
  199.             'ansi_terminal' => false,
  200.             'ansi_clear' => false,
  201.             'num_datapoints' => 5,
  202.             'min_draw_interval' => 0.0,
  203.         );
  204.         $intopts = array();
  205.         foreach ($default_options as $key => $value{
  206.             if (!isset($options[$key])) {
  207.                 $intopts[$key$value;
  208.             else {
  209.                 settype($options[$key]gettype($value));
  210.                 $intopts[$key$options[$key];
  211.             }
  212.         }
  213.         $this->_options $options $intopts;
  214.         // placeholder
  215.         $cur '%2$\''.$options['fraction_pad']{0}.strlen((int)$target_num).'.'
  216.                .$options['fraction_precision'].'f';
  217.         $max $cur$max{1= 3;
  218.         // pre php-4.3.7 %3.2f meant 3 characters before . and two after
  219.         // php-4.3.7 and later it means 3 characters for the whole number
  220.         if (version_compare(PHP_VERSION'4.3.7''ge')) {
  221.             $padding = 4 + $options['percent_precision'];
  222.         else {
  223.             $padding = 3;
  224.         }
  225.         $perc '%4$\''.$options['percent_pad']{0}.$padding.'.'
  226.                 .$options['percent_precision'].'f';
  227.         
  228.         $transitions = array(
  229.             '%%' => '%%',
  230.             '%fraction%' => $cur.'/'.$max,
  231.             '%current%' => $cur,
  232.             '%max%' => $max,
  233.             '%percent%' => $perc.'%%',
  234.             '%bar%' => '%1$s',
  235.             '%elapsed%' => '%5$s',
  236.             '%estimate%' => '%6$s',
  237.         );
  238.         
  239.         $this->_skeleton strtr($formatstring$transitions);
  240.  
  241.         $slen strlen(sprintf($this->_skeleton''000'00:00:00','00:00:00'));
  242.  
  243.         if ($options['width_absolute']{
  244.             $blen $width $slen;
  245.             $tlen $width;
  246.         else {
  247.             $tlen $width $slen;
  248.             $blen $width;
  249.         }
  250.  
  251.         $lbar str_pad($bar$blen$bar{0}STR_PAD_LEFT);
  252.         $rbar str_pad($prefill$blensubstr($prefill-11));
  253.  
  254.         $this->_bar   substr($lbar,-$blen).substr($rbar,0,$blen);
  255.         $this->_blen  $blen;
  256.         $this->_tlen  $tlen;
  257.         $this->_first = true;
  258.  
  259.  
  260.         return true;
  261.     }
  262.     // }}}
  263.     
  264.     // {{{ update($current)
  265.     /**
  266.      * Updates the bar with new progress information
  267.      *
  268.      * @param int current position of the progress counter
  269.      * @return bool 
  270.      */
  271.     function update($current)
  272.     {
  273.         $time $this->_fetchTime();
  274.         $this->_addDatapoint($current$time);
  275.         if ($this->_first{
  276.             if ($this->_options['ansi_terminal']{
  277.                 echo "\x1b[s"// save cursor position
  278.             }
  279.             $this->_first = false;
  280.             $this->_start_time $this->_fetchTime();
  281.             $this->display($current);
  282.             return;
  283.         }
  284.         if ($time $this->_last_update_time 
  285.             $this->_options['min_draw_interval'and $current != $this->_target_num{
  286.             return;
  287.         }
  288.         $this->erase();
  289.         $this->display($current);
  290.         $this->_last_update_time $time;
  291.     }
  292.     // }}}
  293.     
  294.     // {{{ display($current)
  295.     /**
  296.      * Prints the bar. Usually, you don't need this method, just use update()
  297.      * which handles erasing the previously printed bar also. If you use a
  298.      * custom function (for whatever reason) to erase the bar, use this method.
  299.      *
  300.      * @param int current position of the progress counter
  301.      * @return bool 
  302.      */
  303.     function display($current
  304.     {
  305.         $percent $current $this->_target_num;
  306.         $filled round($percent $this->_blen);
  307.         $visbar substr($this->_bar$this->_blen $filled$this->_blen);
  308.         $elapsed $this->_formatSeconds(
  309.             $this->_fetchTime($this->_start_time
  310.         );
  311.         $estimate $this->_formatSeconds($this->_generateEstimate());
  312.         $this->_rlen printf($this->_skeleton
  313.             $visbar$current$this->_target_num$percent * 100$elapsed,
  314.             $estimate
  315.         );
  316.         // fix for php-versions where printf doesn't return anything
  317.         if (is_null($this->_rlen)) {
  318.             $this->_rlen $this->_tlen;
  319.         // fix for php versions between 4.3.7 and 5.x.y(?)
  320.         elseif ($this->_rlen $this->_tlen{
  321.             echo str_repeat(' '$this->_tlen $this->_rlen);
  322.             $this->_rlen $this->_tlen;
  323.         
  324.         return true;
  325.     }
  326.     // }}}
  327.  
  328.     // {{{ erase($clear = false)
  329.     /**
  330.      * Erases a previously printed bar.
  331.      *
  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.             echo str_repeat(chr(0x20)$this->_rlen);
  347.             echo 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 Mon, 11 Mar 2019 15:11:39 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.