1<?php
2/**
3 * PHP Command Line Tools
4 *
5 * This source file is subject to the MIT license that is bundled
6 * with this package in the file LICENSE.
7 *
8 * @author    James Logsdon <dwarf@girsbrain.org>
9 * @copyright 2010 James Logsdom (http://girsbrain.org)
10 * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
11 */
12
13namespace cli;
14
15/**
16 * A more complex type of Notifier, `Progress` Notifiers always have a maxim
17 * value and generally show some form of percent complete or estimated time
18 * to completion along with the standard Notifier displays.
19 *
20 * @see cli\Notify
21 */
22abstract class Progress extends \cli\Notify {
23	protected $_total = 0;
24
25	/**
26	 * Instantiates a Progress Notifier.
27	 *
28	 * @param string  $msg       The text to display next to the Notifier.
29	 * @param int     $total     The total number of ticks we will be performing.
30	 * @param int     $interval  The interval in milliseconds between updates.
31	 * @see cli\Progress::setTotal()
32	 */
33	public function __construct($msg, $total, $interval = 100) {
34		parent::__construct($msg, $interval);
35		$this->setTotal($total);
36	}
37
38	/**
39	 * Set the max increments for this progress notifier.
40	 *
41	 * @param int  $total  The total number of times this indicator should be `tick`ed.
42	 * @throws \InvalidArgumentException  Thrown if the `$total` is less than 0.
43	 */
44	public function setTotal($total) {
45		$this->_total = (int)$total;
46
47		if ($this->_total < 0) {
48			throw new \InvalidArgumentException('Maximum value out of range, must be positive.');
49		}
50	}
51
52	/**
53	 * Reset the progress state so the same instance can be used in multiple loops.
54	 */
55	public function reset($total = null) {
56		parent::reset();
57
58		if ($total) {
59			$this->setTotal($total);
60		}
61	}
62
63	/**
64	 * Behaves in a similar manner to `cli\Notify::current()`, but the output
65	 * is padded to match the length of `cli\Progress::total()`.
66	 *
67	 * @return string  The formatted and padded tick count.
68	 * @see cli\Progress::total()
69	 */
70	public function current() {
71		$size = strlen($this->total());
72		return str_pad(parent::current(), $size);
73	}
74
75	/**
76	 * Returns the formatted total expected ticks.
77	 *
78	 * @return string  The formatted total ticks.
79	 */
80	public function total() {
81		return number_format($this->_total);
82	}
83
84	/**
85	 * Calculates the estimated total time for the tick count to reach the
86	 * total ticks given.
87	 *
88	 * @return int  The estimated total number of seconds for all ticks to be
89	 *              completed. This is not the estimated time left, but total.
90	 * @see cli\Notify::speed()
91	 * @see cli\Notify::elapsed()
92	 */
93	public function estimated() {
94		$speed = $this->speed();
95		if (!$speed || !$this->elapsed()) {
96			return 0;
97		}
98
99		$estimated = round($this->_total / $speed);
100		return $estimated;
101	}
102
103	/**
104	 * Forces the current tick count to the total ticks given at instatiation
105	 * time before passing on to `cli\Notify::finish()`.
106	 */
107	public function finish() {
108		$this->_current = $this->_total;
109		parent::finish();
110	}
111
112	/**
113	 * Increments are tick counter by the given amount. If no amount is provided,
114	 * the ticker is incremented by 1.
115	 *
116	 * @param int  $increment  The amount to increment by.
117	 */
118	public function increment($increment = 1) {
119		$this->_current = min($this->_total, $this->_current + $increment);
120	}
121
122	/**
123	 * Calculate the percentage completed.
124	 *
125	 * @return float  The percent completed.
126	 */
127	public function percent() {
128		if ($this->_total == 0) {
129			return 1;
130		}
131
132		return ($this->_current / $this->_total);
133	}
134}
135