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.
14 * @package Zend_ProgressBar
15 * @copyright Copyright (c) 2005-2009 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 16215 2009-06-21 19:36:07Z thomas $
21 * @see Zend_ProgressBar_Adapter
23 require_once 'Zend/ProgressBar/Adapter.php';
26 * @see Zend_Text_MultiByte
28 require_once 'Zend/Text/MultiByte.php';
31 * Zend_ProgressBar_Adapter_Console offers a text-based progressbar for console
35 * @package Zend_ProgressBar
36 * @uses Zend_ProgressBar_Adapter_Interface
37 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
38 * @license http://framework.zend.com/license/new-bsd New BSD License
40 class Zend_ProgressBar_Adapter_Console
extends Zend_ProgressBar_Adapter
43 * Percentage value of the progress
45 const ELEMENT_PERCENT
= 'ELEMENT_PERCENT';
48 * Visual value of the progress
50 const ELEMENT_BAR
= 'ELEMENT_BAR';
55 const ELEMENT_ETA
= 'ELEMENT_ETA';
58 * Text part of the progress
60 const ELEMENT_TEXT
= 'ELEMENT_TEXT';
63 * Finish action: End of Line
65 const FINISH_ACTION_EOL
= 'FINISH_ACTION_EOL';
68 * Finish action: Clear Line
70 const FINISH_ACTION_CLEAR_LINE
= 'FINISH_ACTION_CLEAR_LINE';
75 const FINISH_ACTION_NONE
= 'FINISH_ACTION_NONE';
78 * Width of the progressbar
82 protected $_width = null;
89 protected $_elements = array(self
::ELEMENT_PERCENT
,
94 * Which action to do at finish call
98 protected $_finishAction = self
::FINISH_ACTION_EOL
;
101 * Width of the bar element
105 protected $_barWidth;
108 * Left character(s) within the bar
112 protected $_barLeftChar = '#';
115 * Indicator character(s) within the bar
119 protected $_barIndicatorChar = '';
122 * Right character(s) within the bar
126 protected $_barRightChar = '-';
129 * Output-stream, when STDOUT is not defined (e.g. in CGI) or set manually
133 protected $_outputStream = null;
136 * Width of the text element
140 protected $_textWidth = 20;
143 * Wether the output started yet or not
147 protected $_outputStarted = false;
150 * Charset of text element
154 protected $_charset = 'utf-8';
157 * Defined by Zend_ProgressBar_Adapter
159 * @param null|array|Zend_Config $options
161 public function __construct($options = null)
163 // Call parent constructor with options
164 parent
::__construct($options);
166 // Check if a width was set, else use auto width
167 if ($this->_width
=== null) {
173 * Close local stdout, when open
175 public function __destruct()
177 if ($this->_outputStream
!== null) {
178 fclose($this->_outputStream
);
183 * Set a different output-stream
185 * @param string $resource
186 * @return Zend_ProgressBar_Adapter_Console
188 public function setOutputStream($resource)
190 $stream = @fopen
($resource, 'w');
192 if ($stream === false) {
193 require_once 'Zend/ProgressBar/Adapter/Exception.php';
194 throw new Zend_ProgressBar_Adapter_Exception('Unable to open stream');
197 if ($this->_outputStream
!== null) {
198 fclose($this->_outputStream
);
201 $this->_outputStream
= $stream;
205 * Get the current output stream
209 public function getOutputStream()
211 if ($this->_outputStream
=== null) {
212 if (!defined('STDOUT')) {
213 $this->_outputStream
= fopen('php://stdout', 'w');
219 return $this->_outputStream
;
223 * Set the width of the progressbar
225 * @param integer $width
226 * @return Zend_ProgressBar_Adapter_Console
228 public function setWidth($width = null)
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.
238 // Set the default width of 80
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];
249 $this->_width
= (int) $width;
252 $this->_calculateBarWidth();
258 * Set the elements to display with the progressbar
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
264 public function setElements(array $elements)
266 $allowedElements = array(self
::ELEMENT_PERCENT
,
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');
276 $this->_elements
= $elements;
278 $this->_calculateBarWidth();
284 * Set the left-hand character for the bar
286 * @param string $char
287 * @throws Zend_ProgressBar_Adapter_Exception When character is empty
288 * @return Zend_ProgressBar_Adapter_Console
290 public function setBarLeftChar($char)
293 require_once 'Zend/ProgressBar/Adapter/Exception.php';
294 throw new Zend_ProgressBar_Adapter_Exception('Character may not be empty');
297 $this->_barLeftChar
= (string) $char;
303 * Set the right-hand character for the bar
305 * @param string $char
306 * @throws Zend_ProgressBar_Adapter_Exception When character is empty
307 * @return Zend_ProgressBar_Adapter_Console
309 public function setBarRightChar($char)
312 require_once 'Zend/ProgressBar/Adapter/Exception.php';
313 throw new Zend_ProgressBar_Adapter_Exception('Character may not be empty');
316 $this->_barRightChar
= (string) $char;
322 * Set the indicator character for the bar
324 * @param string $char
325 * @return Zend_ProgressBar_Adapter_Console
327 public function setBarIndicatorChar($char)
329 $this->_barIndicatorChar
= (string) $char;
335 * Set the width of the text element
337 * @param integer $width
338 * @return Zend_ProgressBar_Adapter_Console
340 public function setTextWidth($width)
342 $this->_textWidth
= (int) $width;
344 $this->_calculateBarWidth();
350 * Set the charset of the text element
352 * @param string $charset
354 public function setCharset($charset)
356 $this->_charset
= $charset;
360 * Set the finish action
362 * @param string $action
363 * @throws Zend_ProgressBar_Adapter_Exception When an invalid action is specified
364 * @return Zend_ProgressBar_Adapter_Console
366 public function setFinishAction($action)
368 $allowedActions = array(self
::FINISH_ACTION_CLEAR_LINE
,
369 self
::FINISH_ACTION_EOL
,
370 self
::FINISH_ACTION_NONE
);
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');
377 $this->_finishAction
= $action;
383 * Defined by Zend_ProgressBar_Adapter_Interface
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
393 public function notify($current, $max, $percent, $timeTaken, $timeRemaining, $text)
395 // See if we must clear the line
396 if ($this->_outputStarted
) {
397 $data = str_repeat("\x08", $this->_width
);
400 $this->_outputStarted
= true;
403 // Build all elements
404 $renderedElements = array();
406 foreach ($this->_elements
as $element) {
408 case self
::ELEMENT_BAR
:
409 $visualWidth = $this->_barWidth
- 2;
412 $indicatorWidth = strlen($this->_barIndicatorChar
);
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);
419 $bar .= $this->_barIndicatorChar
;
421 $leftWidth = $visualWidth - $doneWidth - $indicatorWidth;
422 if ($leftWidth > 0) {
423 $bar .= substr(str_repeat($this->_barRightChar
, ceil($leftWidth / strlen($this->_barRightChar
))), 0, $leftWidth);
428 $renderedElements[] = $bar;
431 case self
::ELEMENT_PERCENT
:
432 $renderedElements[] = str_pad(round($percent * 100), 3, ' ', STR_PAD_LEFT
) . '%';
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
439 if ($timeTaken < 5) {
440 $renderedElements[] = str_repeat(' ', 12);
444 if ($timeRemaining === null ||
$timeRemaining > 86400) {
445 $etaFormatted = '??:??:??';
447 $hours = floor($timeRemaining / 3600);
448 $minutes = floor(($timeRemaining %
3600) / 60);
449 $seconds = ($timeRemaining %
3600 %
60);
451 $etaFormatted = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
454 $renderedElements[] = 'ETA ' . $etaFormatted;
457 case self
::ELEMENT_TEXT
:
458 $renderedElements[] = Zend_Text_MultiByte
::strPad(substr($text, 0, $this->_textWidth
), $this->_textWidth
, ' ', STR_PAD_RIGHT
, $this->_charset
);
463 $data .= implode(' ', $renderedElements);
466 $this->_outputData($data);
470 * Defined by Zend_ProgressBar_Adapter_Interface
474 public function finish()
476 switch ($this->_finishAction
) {
477 case self
::FINISH_ACTION_EOL
:
478 $this->_outputData(PHP_EOL
);
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
);
487 $this->_outputData($data);
491 case self
::FINISH_ACTION_NONE
:
497 * Calculate the bar width when other elements changed
501 protected function _calculateBarWidth()
503 if (in_array(self
::ELEMENT_BAR
, $this->_elements
)) {
504 $barWidth = $this->_width
;
506 if (in_array(self
::ELEMENT_PERCENT
, $this->_elements
)) {
510 if (in_array(self
::ELEMENT_ETA
, $this->_elements
)) {
514 if (in_array(self
::ELEMENT_TEXT
, $this->_elements
)) {
515 $barWidth -= $this->_textWidth
;
518 $this->_barWidth
= $barWidth - (count($this->_elements
) - 1);
523 * Outputs given data to STDOUT.
525 * This split-off is required for unit-testing.
527 * @param string $data
530 protected function _outputData($data)
532 fwrite($this->getOutputStream(), $data);