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

Documentation generated on Mon, 11 Mar 2019 14:55:29 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.