Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 157 additions & 34 deletions lib/cli/Arguments.php

Large diffs are not rendered by default.

36 changes: 31 additions & 5 deletions lib/cli/Colors.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Reference: http://graphcomp.com/info/specs/ansi_col.html#colors
*/
class Colors {
/** @var array<string, array<string, int>> */
static protected $_colors = array(
'color' => array(
'black' => 30,
Expand Down Expand Up @@ -48,14 +49,28 @@ class Colors {
'white' => 47
)
);
/** @var bool|null */
static protected $_enabled = null;

/** @var array<string, array<string, string>> */
static protected $_string_cache = array();

/**
* Enable colorized output.
*
* @param bool $force Force enable.
* @return void
*/
static public function enable($force = true) {
self::$_enabled = $force === true ? true : null;
}

/**
* Disable colorized output.
*
* @param bool $force Force disable.
* @return void
*/
static public function disable($force = true) {
self::$_enabled = $force === true ? false : null;
}
Expand All @@ -64,6 +79,9 @@ static public function disable($force = true) {
* Check if we should colorize output based on local flags and shell type.
*
* Only check the shell type if `Colors::$_enabled` is null and `$colored` is null.
*
* @param bool|null $colored Force enable or disable the colorized output.
* @return bool
*/
static public function shouldColorize($colored = null) {
return self::$_enabled === true ||
Expand All @@ -75,8 +93,8 @@ static public function shouldColorize($colored = null) {
/**
* Set the color.
*
* @param string $color The name of the color or style to set.
* @return string
* @param string|array<string, string|int> $color The name of the color or style to set, or an array of options.
* @return string
*/
static public function color($color) {
if (!is_array($color)) {
Expand Down Expand Up @@ -171,6 +189,7 @@ static public function decolorize( $string, $keep = 0 ) {
* @param string $passed The original string before colorization.
* @param string $colorized The string after running through self::colorize.
* @param string $deprecated Optional. Not used. Default null.
* @return void
*/
static public function cacheString( $passed, $colorized, $deprecated = null ) {
self::$_string_cache[md5($passed)] = array(
Expand Down Expand Up @@ -225,7 +244,7 @@ static public function pad( $string, $length, $pre_colorized = false, $encoding
/**
* Get the color mapping array.
*
* @return array Array of color tokens mapped to colors and styles.
* @return array<string, array<string, string|int>> Array of color tokens mapped to colors and styles.
*/
static public function getColors() {
return array(
Expand Down Expand Up @@ -268,14 +287,16 @@ static public function getColors() {
/**
* Get the cached string values.
*
* @return array The cached string values.
* @return array<string, array<string, string>> The cached string values.
*/
static public function getStringCache() {
return self::$_string_cache;
}

/**
* Clear the string cache.
*
* @return void
*/
static public function clearStringCache() {
self::$_string_cache = array();
Expand Down Expand Up @@ -305,7 +326,7 @@ static public function getResetCode() {
* @param string $string The string to wrap (with ANSI codes).
* @param int $width The maximum display width per line.
* @param string|bool $encoding Optional. The encoding of the string. Default false.
* @return array Array of wrapped string segments.
* @return array<int, string> Array of wrapped string segments.
*/
static public function wrapPreColorized( $string, $width, $encoding = false ) {
$wrapped = array();
Expand All @@ -319,6 +340,10 @@ static public function wrapPreColorized( $string, $width, $encoding = false ) {
// Split the string into parts: ANSI codes and text
$parts = preg_split( $ansi_pattern, $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );

if ( false === $parts ) {
$parts = array( $string );
}

foreach ( $parts as $part ) {
// Check if this part is an ANSI code
if ( preg_match( $ansi_pattern, $part ) ) {
Expand All @@ -340,6 +365,7 @@ static public function wrapPreColorized( $string, $width, $encoding = false ) {

while ( $offset < $text_length ) {
$char = \cli\safe_substr( $part, $offset, 1, false, $encoding );
assert( is_string( $char ) );
$char_width = \cli\strwidth( $char, $encoding );

// Check if adding this character would exceed the width
Expand Down
16 changes: 14 additions & 2 deletions lib/cli/Memoize.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@
namespace cli;

abstract class Memoize {
/** @var array<string, mixed> */
protected $_memoCache = array();

/**
* Magic getter to retrieve memoized properties.
*
* @param string $name Property name.
* @return mixed
*/
public function __get($name) {
if (isset($this->_memoCache[$name])) {
return $this->_memoCache[$name];
Expand All @@ -29,11 +36,16 @@ public function __get($name) {
return ($this->_memoCache[$name] = null);
}

$method = array($this, $name);
($this->_memoCache[$name] = call_user_func($method));
($this->_memoCache[$name] = $this->$name());
return $this->_memoCache[$name];
}

/**
* Unmemoize a property or all properties.
*
* @param string|bool $name Property name to unmemoize, or true to unmemoize all.
* @return void
*/
protected function _unmemo($name) {
if ($name === true) {
$this->_memoCache = array();
Expand Down
19 changes: 17 additions & 2 deletions lib/cli/Notify.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,23 @@
* of characters to indicate progress is being made.
*/
abstract class Notify {
/** @var int */
protected $_current = 0;
/** @var bool */
protected $_first = true;
/** @var int */
protected $_interval;
/** @var string */
protected $_message;
/** @var int|null */
protected $_start;
/** @var float|null */
protected $_timer;
/** @var float|int|null */
protected $_tick;
/** @var int */
protected $_iteration = 0;
/** @var float|int */
protected $_speed = 0;

/**
Expand All @@ -52,11 +61,14 @@ public function __construct($msg, $interval = 100) {
* @abstract
* @param boolean $finish
* @see cli\Notify::tick()
* @return void
*/
abstract public function display($finish = false);

/**
* Reset the notifier state so the same instance can be used in multiple loops.
*
* @return void
*/
public function reset() {
$this->_current = 0;
Expand Down Expand Up @@ -109,7 +121,7 @@ public function speed() {
$this->_speed = ($this->_current / $this->_iteration) / $span;
}

return $this->_speed;
return (int) $this->_speed;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Casting the calculated speed to an (int) results in a loss of precision. The $_speed property is typed as float|int, and a float value would be more accurate for representing speed (e.g., items per second). Was this truncation intentional? If so, it would be good to document it. Otherwise, I'd suggest removing the cast to return the more precise float value.

return $this->_speed;

}

/**
Expand All @@ -120,14 +132,15 @@ public function speed() {
* @return string The formatted time span.
*/
public function formatTime($time) {
return floor($time / 60) . ':' . str_pad($time % 60, 2, 0, STR_PAD_LEFT);
return sprintf('%02d:%02d', (int)floor($time / 60), $time % 60);
}

/**
* Finish our Notification display. Should be called after the Notifier is
* no longer needed.
*
* @see cli\Notify::display()
* @return void
*/
public function finish() {
Streams::out("\r");
Expand All @@ -140,6 +153,7 @@ public function finish() {
* the ticker is incremented by 1.
*
* @param int $increment The amount to increment by.
* @return void
*/
public function increment($increment = 1) {
$this->_current += $increment;
Expand Down Expand Up @@ -174,6 +188,7 @@ public function shouldUpdate() {
* @see cli\Notify::increment()
* @see cli\Notify::shouldUpdate()
* @see cli\Notify::display()
* @return void
*/
public function tick($increment = 1) {
$this->increment($increment);
Expand Down
10 changes: 9 additions & 1 deletion lib/cli/Progress.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* @see cli\Notify
*/
abstract class Progress extends \cli\Notify {
/** @var int */
protected $_total = 0;

/**
Expand All @@ -40,6 +41,7 @@ public function __construct($msg, $total, $interval = 100) {
*
* @param int $total The total number of times this indicator should be `tick`ed.
* @throws \InvalidArgumentException Thrown if the `$total` is less than 0.
* @return void
*/
public function setTotal($total) {
$this->_total = (int)$total;
Expand All @@ -51,6 +53,9 @@ public function setTotal($total) {

/**
* Reset the progress state so the same instance can be used in multiple loops.
*
* @param int|null $total Optional new total.
* @return void
*/
public function reset($total = null) {
parent::reset();
Expand Down Expand Up @@ -97,12 +102,14 @@ public function estimated() {
}

$estimated = round($this->_total / $speed);
return $estimated;
return (int)$estimated;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Casting the estimated time to an (int) truncates the value, which could be misleading. The round() function already returns a float, which provides a more accurate estimation. I'd recommend removing the (int) cast to avoid this loss of precision.

return $estimated;

}

/**
* Forces the current tick count to the total ticks given at instantiation
* time before passing on to `cli\Notify::finish()`.
*
* @return void
*/
public function finish() {
$this->_current = $this->_total;
Expand All @@ -114,6 +121,7 @@ public function finish() {
* the ticker is incremented by 1.
*
* @param int $increment The amount to increment by.
* @return void
*/
public function increment($increment = 1) {
$this->_current = min($this->_total, $this->_current + $increment);
Expand Down
4 changes: 3 additions & 1 deletion lib/cli/Shell.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ static public function columns() {
}
} else {
$size = exec( '/usr/bin/env stty size 2>/dev/null' );
if ( '' !== $size && preg_match( '/[0-9]+ ([0-9]+)/', $size, $matches ) ) {
if ( $size && preg_match( '/[0-9]+ ([0-9]+)/', $size, $matches ) ) {
$columns = (int) $matches[1];
}
if ( ! $columns ) {
Expand Down Expand Up @@ -99,7 +99,9 @@ static public function isPiped() {

/**
* Uses `stty` to hide input/output completely.
*
* @param boolean $hidden Will hide/show the next data. Defaults to true.
* @return void
*/
static public function hide($hidden = true) {
system( 'stty ' . ( $hidden? '-echo' : 'echo' ) );
Expand Down
Loading