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

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