web/lib/Zend/ProgressBar/Adapter/Console.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * LICENSE
       
     4  *
       
     5  * This source file is subject to the new BSD license that is bundled
       
     6  * with this package in the file LICENSE.txt.
       
     7  * It is also available through the world-wide-web at this URL:
       
     8  * http://framework.zend.com/license/new-bsd
       
     9  * If you did not receive a copy of the license and are unable to
       
    10  * obtain it through the world-wide-web, please send an email
       
    11  * to license@zend.com so we can send you a copy immediately.
       
    12  *
       
    13  * @category   Zend
       
    14  * @package    Zend_ProgressBar
       
    15  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    16  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    17  * @version    $Id: Console.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    18  */
       
    19 
       
    20 /**
       
    21  * @see Zend_ProgressBar_Adapter
       
    22  */
       
    23 require_once 'Zend/ProgressBar/Adapter.php';
       
    24 
       
    25 /**
       
    26  * @see Zend_Text_MultiByte
       
    27  */
       
    28 require_once 'Zend/Text/MultiByte.php';
       
    29 
       
    30 /**
       
    31  * Zend_ProgressBar_Adapter_Console offers a text-based progressbar for console
       
    32  * applications
       
    33  *
       
    34  * @category  Zend
       
    35  * @package   Zend_ProgressBar
       
    36  * @uses      Zend_ProgressBar_Adapter_Interface
       
    37  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    38  * @license   http://framework.zend.com/license/new-bsd     New BSD License
       
    39  */
       
    40 class Zend_ProgressBar_Adapter_Console extends Zend_ProgressBar_Adapter
       
    41 {
       
    42     /**
       
    43      * Percentage value of the progress
       
    44      */
       
    45     const ELEMENT_PERCENT = 'ELEMENT_PERCENT';
       
    46 
       
    47     /**
       
    48      * Visual value of the progress
       
    49      */
       
    50     const ELEMENT_BAR = 'ELEMENT_BAR';
       
    51 
       
    52     /**
       
    53      * ETA of the progress
       
    54      */
       
    55     const ELEMENT_ETA = 'ELEMENT_ETA';
       
    56 
       
    57     /**
       
    58      * Text part of the progress
       
    59      */
       
    60     const ELEMENT_TEXT = 'ELEMENT_TEXT';
       
    61 
       
    62     /**
       
    63      * Finish action: End of Line
       
    64      */
       
    65     const FINISH_ACTION_EOL = 'FINISH_ACTION_EOL';
       
    66 
       
    67     /**
       
    68      * Finish action: Clear Line
       
    69      */
       
    70     const FINISH_ACTION_CLEAR_LINE = 'FINISH_ACTION_CLEAR_LINE';
       
    71 
       
    72     /**
       
    73      * Finish action: None
       
    74      */
       
    75     const FINISH_ACTION_NONE = 'FINISH_ACTION_NONE';
       
    76 
       
    77     /**
       
    78      * Width of the progressbar
       
    79      *
       
    80      * @var integer
       
    81      */
       
    82     protected $_width = null;
       
    83 
       
    84     /**
       
    85      * Elements to display
       
    86      *
       
    87      * @var array
       
    88      */
       
    89     protected $_elements = array(self::ELEMENT_PERCENT,
       
    90                                  self::ELEMENT_BAR,
       
    91                                  self::ELEMENT_ETA);
       
    92 
       
    93     /**
       
    94      * Which action to do at finish call
       
    95      *
       
    96      * @var string
       
    97      */
       
    98     protected $_finishAction = self::FINISH_ACTION_EOL;
       
    99 
       
   100     /**
       
   101      * Width of the bar element
       
   102      *
       
   103      * @var integer
       
   104      */
       
   105     protected $_barWidth;
       
   106 
       
   107     /**
       
   108      * Left character(s) within the bar
       
   109      *
       
   110      * @var string
       
   111      */
       
   112     protected $_barLeftChar = '#';
       
   113 
       
   114     /**
       
   115      * Indicator character(s) within the bar
       
   116      *
       
   117      * @var string
       
   118      */
       
   119     protected $_barIndicatorChar = '';
       
   120 
       
   121     /**
       
   122      * Right character(s) within the bar
       
   123      *
       
   124      * @var string
       
   125      */
       
   126     protected $_barRightChar = '-';
       
   127 
       
   128     /**
       
   129      * Output-stream, when STDOUT is not defined (e.g. in CGI) or set manually
       
   130      *
       
   131      * @var resource
       
   132      */
       
   133     protected $_outputStream = null;
       
   134 
       
   135     /**
       
   136      * Width of the text element
       
   137      *
       
   138      * @var string
       
   139      */
       
   140     protected $_textWidth = 20;
       
   141 
       
   142     /**
       
   143      * Wether the output started yet or not
       
   144      *
       
   145      * @var boolean
       
   146      */
       
   147     protected $_outputStarted = false;
       
   148 
       
   149     /**
       
   150      * Charset of text element
       
   151      *
       
   152      * @var string
       
   153      */
       
   154     protected $_charset = 'utf-8';
       
   155 
       
   156     /**
       
   157      * Defined by Zend_ProgressBar_Adapter
       
   158      *
       
   159      * @param null|array|Zend_Config $options
       
   160      */
       
   161     public function __construct($options = null)
       
   162     {
       
   163         // Call parent constructor with options
       
   164         parent::__construct($options);
       
   165 
       
   166         // Check if a width was set, else use auto width
       
   167         if ($this->_width === null) {
       
   168             $this->setWidth();
       
   169         }
       
   170     }
       
   171 
       
   172     /**
       
   173      * Close local stdout, when open
       
   174      */
       
   175     public function __destruct()
       
   176     {
       
   177         if ($this->_outputStream !== null) {
       
   178             fclose($this->_outputStream);
       
   179         }
       
   180     }
       
   181 
       
   182     /**
       
   183      * Set a different output-stream
       
   184      *
       
   185      * @param  string $resource
       
   186      * @return Zend_ProgressBar_Adapter_Console
       
   187      */
       
   188     public function setOutputStream($resource)
       
   189     {
       
   190        $stream = @fopen($resource, 'w');
       
   191 
       
   192        if ($stream === false) {
       
   193             require_once 'Zend/ProgressBar/Adapter/Exception.php';
       
   194             throw new Zend_ProgressBar_Adapter_Exception('Unable to open stream');
       
   195        }
       
   196 
       
   197        if ($this->_outputStream !== null) {
       
   198            fclose($this->_outputStream);
       
   199        }
       
   200 
       
   201        $this->_outputStream = $stream;
       
   202     }
       
   203 
       
   204     /**
       
   205      * Get the current output stream
       
   206      *
       
   207      * @return resource
       
   208      */
       
   209     public function getOutputStream()
       
   210     {
       
   211         if ($this->_outputStream === null) {
       
   212             if (!defined('STDOUT')) {
       
   213                 $this->_outputStream = fopen('php://stdout', 'w');
       
   214             } else {
       
   215                 return STDOUT;
       
   216             }
       
   217         }
       
   218 
       
   219         return $this->_outputStream;
       
   220     }
       
   221 
       
   222     /**
       
   223      * Set the width of the progressbar
       
   224      *
       
   225      * @param  integer $width
       
   226      * @return Zend_ProgressBar_Adapter_Console
       
   227      */
       
   228     public function setWidth($width = null)
       
   229     {
       
   230         if ($width === null || !is_integer($width)) {
       
   231             if (substr(PHP_OS, 0, 3) === 'WIN') {
       
   232                 // We have to default to 79 on windows, because the windows
       
   233                 // terminal always has a fixed width of 80 characters and the
       
   234                 // cursor is counted to the line, else windows would line break
       
   235                 // after every update.
       
   236                 $this->_width = 79;
       
   237             } else {
       
   238                 // Set the default width of 80
       
   239                 $this->_width = 80;
       
   240 
       
   241                 // Try to determine the width through stty
       
   242                 if (preg_match('#\d+ (\d+)#', @shell_exec('stty size'), $match) === 1) {
       
   243                     $this->_width = (int) $match[1];
       
   244                 } else if (preg_match('#columns = (\d+);#', @shell_exec('stty'), $match) === 1) {
       
   245                     $this->_width = (int) $match[1];
       
   246                 }
       
   247             }
       
   248         } else {
       
   249             $this->_width = (int) $width;
       
   250         }
       
   251 
       
   252         $this->_calculateBarWidth();
       
   253 
       
   254         return $this;
       
   255     }
       
   256 
       
   257     /**
       
   258      * Set the elements to display with the progressbar
       
   259      *
       
   260      * @param  array $elements
       
   261      * @throws Zend_ProgressBar_Adapter_Exception When an invalid element is foudn in the array
       
   262      * @return Zend_ProgressBar_Adapter_Console
       
   263      */
       
   264     public function setElements(array $elements)
       
   265     {
       
   266         $allowedElements = array(self::ELEMENT_PERCENT,
       
   267                                  self::ELEMENT_BAR,
       
   268                                  self::ELEMENT_ETA,
       
   269                                  self::ELEMENT_TEXT);
       
   270 
       
   271         if (count(array_diff($elements, $allowedElements)) > 0) {
       
   272             require_once 'Zend/ProgressBar/Adapter/Exception.php';
       
   273             throw new Zend_ProgressBar_Adapter_Exception('Invalid element found in $elements array');
       
   274         }
       
   275 
       
   276         $this->_elements = $elements;
       
   277 
       
   278         $this->_calculateBarWidth();
       
   279 
       
   280         return $this;
       
   281     }
       
   282 
       
   283     /**
       
   284      * Set the left-hand character for the bar
       
   285      *
       
   286      * @param  string $char
       
   287      * @throws Zend_ProgressBar_Adapter_Exception When character is empty
       
   288      * @return Zend_ProgressBar_Adapter_Console
       
   289      */
       
   290     public function setBarLeftChar($char)
       
   291     {
       
   292         if (empty($char)) {
       
   293             require_once 'Zend/ProgressBar/Adapter/Exception.php';
       
   294             throw new Zend_ProgressBar_Adapter_Exception('Character may not be empty');
       
   295         }
       
   296 
       
   297         $this->_barLeftChar = (string) $char;
       
   298 
       
   299         return $this;
       
   300     }
       
   301 
       
   302     /**
       
   303      * Set the right-hand character for the bar
       
   304      *
       
   305      * @param  string $char
       
   306      * @throws Zend_ProgressBar_Adapter_Exception When character is empty
       
   307      * @return Zend_ProgressBar_Adapter_Console
       
   308      */
       
   309     public function setBarRightChar($char)
       
   310     {
       
   311         if (empty($char)) {
       
   312             require_once 'Zend/ProgressBar/Adapter/Exception.php';
       
   313             throw new Zend_ProgressBar_Adapter_Exception('Character may not be empty');
       
   314         }
       
   315 
       
   316         $this->_barRightChar = (string) $char;
       
   317 
       
   318         return $this;
       
   319     }
       
   320 
       
   321     /**
       
   322      * Set the indicator character for the bar
       
   323      *
       
   324      * @param  string $char
       
   325      * @return Zend_ProgressBar_Adapter_Console
       
   326      */
       
   327     public function setBarIndicatorChar($char)
       
   328     {
       
   329         $this->_barIndicatorChar = (string) $char;
       
   330 
       
   331         return $this;
       
   332     }
       
   333 
       
   334     /**
       
   335      * Set the width of the text element
       
   336      *
       
   337      * @param  integer $width
       
   338      * @return Zend_ProgressBar_Adapter_Console
       
   339      */
       
   340     public function setTextWidth($width)
       
   341     {
       
   342         $this->_textWidth = (int) $width;
       
   343 
       
   344         $this->_calculateBarWidth();
       
   345 
       
   346         return $this;
       
   347     }
       
   348 
       
   349     /**
       
   350      * Set the charset of the text element
       
   351      *
       
   352      * @param string $charset
       
   353      */
       
   354     public function setCharset($charset)
       
   355     {
       
   356         $this->_charset = $charset;
       
   357     }
       
   358 
       
   359     /**
       
   360      * Set the finish action
       
   361      *
       
   362      * @param  string $action
       
   363      * @throws Zend_ProgressBar_Adapter_Exception When an invalid action is specified
       
   364      * @return Zend_ProgressBar_Adapter_Console
       
   365      */
       
   366     public function setFinishAction($action)
       
   367     {
       
   368         $allowedActions = array(self::FINISH_ACTION_CLEAR_LINE,
       
   369                                 self::FINISH_ACTION_EOL,
       
   370                                 self::FINISH_ACTION_NONE);
       
   371 
       
   372         if (!in_array($action, $allowedActions)) {
       
   373             require_once 'Zend/ProgressBar/Adapter/Exception.php';
       
   374             throw new Zend_ProgressBar_Adapter_Exception('Invalid finish action specified');
       
   375         }
       
   376 
       
   377         $this->_finishAction = $action;
       
   378 
       
   379         return $this;
       
   380     }
       
   381 
       
   382     /**
       
   383      * Defined by Zend_ProgressBar_Adapter_Interface
       
   384      *
       
   385      * @param  float   $current       Current progress value
       
   386      * @param  float   $max           Max progress value
       
   387      * @param  float   $percent       Current percent value
       
   388      * @param  integer $timeTaken     Taken time in seconds
       
   389      * @param  integer $timeRemaining Remaining time in seconds
       
   390      * @param  string  $text          Status text
       
   391      * @return void
       
   392      */
       
   393     public function notify($current, $max, $percent, $timeTaken, $timeRemaining, $text)
       
   394     {
       
   395         // See if we must clear the line
       
   396         if ($this->_outputStarted) {
       
   397             $data = str_repeat("\x08", $this->_width);
       
   398         } else {
       
   399             $data = '';
       
   400             $this->_outputStarted = true;
       
   401         }
       
   402 
       
   403         // Build all elements
       
   404         $renderedElements = array();
       
   405 
       
   406         foreach ($this->_elements as $element) {
       
   407             switch ($element) {
       
   408                 case self::ELEMENT_BAR:
       
   409                     $visualWidth = $this->_barWidth - 2;
       
   410                     $bar         = '[';
       
   411 
       
   412                     $indicatorWidth = strlen($this->_barIndicatorChar);
       
   413 
       
   414                     $doneWidth = min($visualWidth - $indicatorWidth, round($visualWidth * $percent));
       
   415                     if ($doneWidth > 0) {
       
   416                         $bar .= substr(str_repeat($this->_barLeftChar, ceil($doneWidth / strlen($this->_barLeftChar))), 0, $doneWidth);
       
   417                     }
       
   418 
       
   419                     $bar .= $this->_barIndicatorChar;
       
   420 
       
   421                     $leftWidth = $visualWidth - $doneWidth - $indicatorWidth;
       
   422                     if ($leftWidth > 0) {
       
   423                         $bar .= substr(str_repeat($this->_barRightChar, ceil($leftWidth / strlen($this->_barRightChar))), 0, $leftWidth);
       
   424                     }
       
   425 
       
   426                     $bar .= ']';
       
   427 
       
   428                     $renderedElements[] = $bar;
       
   429                     break;
       
   430 
       
   431                 case self::ELEMENT_PERCENT:
       
   432                     $renderedElements[] = str_pad(round($percent * 100), 3, ' ', STR_PAD_LEFT) . '%';
       
   433                     break;
       
   434 
       
   435                 case self::ELEMENT_ETA:
       
   436                     // In the first 5 seconds we don't get accurate results,
       
   437                     // this skipping technique is found in many progressbar
       
   438                     // implementations.
       
   439                     if ($timeTaken < 5) {
       
   440                         $renderedElements[] = str_repeat(' ', 12);
       
   441                         break;
       
   442                     }
       
   443 
       
   444                     if ($timeRemaining === null || $timeRemaining > 86400) {
       
   445                         $etaFormatted = '??:??:??';
       
   446                     } else {
       
   447                         $hours   = floor($timeRemaining / 3600);
       
   448                         $minutes = floor(($timeRemaining % 3600) / 60);
       
   449                         $seconds = ($timeRemaining % 3600 % 60);
       
   450 
       
   451                         $etaFormatted = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
       
   452                     }
       
   453 
       
   454                     $renderedElements[] = 'ETA ' . $etaFormatted;
       
   455                     break;
       
   456 
       
   457                 case self::ELEMENT_TEXT:
       
   458                     $renderedElements[] = Zend_Text_MultiByte::strPad(substr($text, 0, $this->_textWidth), $this->_textWidth, ' ', STR_PAD_RIGHT, $this->_charset);
       
   459                     break;
       
   460             }
       
   461         }
       
   462 
       
   463         $data .= implode(' ', $renderedElements);
       
   464 
       
   465         // Output line data
       
   466         $this->_outputData($data);
       
   467     }
       
   468 
       
   469     /**
       
   470      * Defined by Zend_ProgressBar_Adapter_Interface
       
   471      *
       
   472      * @return void
       
   473      */
       
   474     public function finish()
       
   475     {
       
   476         switch ($this->_finishAction) {
       
   477             case self::FINISH_ACTION_EOL:
       
   478                 $this->_outputData(PHP_EOL);
       
   479                 break;
       
   480 
       
   481             case self::FINISH_ACTION_CLEAR_LINE:
       
   482                 if ($this->_outputStarted) {
       
   483                     $data = str_repeat("\x08", $this->_width)
       
   484                           . str_repeat(' ', $this->_width)
       
   485                           . str_repeat("\x08", $this->_width);
       
   486 
       
   487                     $this->_outputData($data);
       
   488                 }
       
   489                 break;
       
   490 
       
   491             case self::FINISH_ACTION_NONE:
       
   492                 break;
       
   493         }
       
   494     }
       
   495 
       
   496     /**
       
   497      * Calculate the bar width when other elements changed
       
   498      *
       
   499      * @return void
       
   500      */
       
   501     protected function _calculateBarWidth()
       
   502     {
       
   503         if (in_array(self::ELEMENT_BAR, $this->_elements)) {
       
   504             $barWidth = $this->_width;
       
   505 
       
   506             if (in_array(self::ELEMENT_PERCENT, $this->_elements)) {
       
   507                 $barWidth -= 4;
       
   508             }
       
   509 
       
   510             if (in_array(self::ELEMENT_ETA, $this->_elements)) {
       
   511                 $barWidth -= 12;
       
   512             }
       
   513 
       
   514             if (in_array(self::ELEMENT_TEXT, $this->_elements)) {
       
   515                 $barWidth -= $this->_textWidth;
       
   516             }
       
   517 
       
   518             $this->_barWidth = $barWidth - (count($this->_elements) - 1);
       
   519         }
       
   520     }
       
   521 
       
   522     /**
       
   523      * Outputs given data to STDOUT.
       
   524      *
       
   525      * This split-off is required for unit-testing.
       
   526      *
       
   527      * @param  string $data
       
   528      * @return void
       
   529      */
       
   530     protected function _outputData($data)
       
   531     {
       
   532         fwrite($this->getOutputStream(), $data);
       
   533     }
       
   534 }