xarxaprod-wp-theme/vendor/wp-cli/php-cli-tools/lib/cli/Streams.php

284 lines
8.8 KiB
PHP
Raw Permalink Normal View History

2024-01-09 16:13:20 +01:00
<?php
namespace cli;
class Streams {
protected static $out = STDOUT;
protected static $in = STDIN;
protected static $err = STDERR;
static function _call( $func, $args ) {
$method = __CLASS__ . '::' . $func;
return call_user_func_array( $method, $args );
}
static public function isTty() {
if ( function_exists('stream_isatty') ) {
return stream_isatty(static::$out);
} else {
return (function_exists('posix_isatty') && posix_isatty(static::$out));
}
}
/**
* Handles rendering strings. If extra scalar arguments are given after the `$msg`
* the string will be rendered with `sprintf`. If the second argument is an `array`
* then each key in the array will be the placeholder name. Placeholders are of the
* format {:key}.
*
* @param string $msg The message to render.
* @param mixed ... Either scalar arguments or a single array argument.
* @return string The rendered string.
*/
public static function render( $msg ) {
$args = func_get_args();
// No string replacement is needed
if( count( $args ) == 1 || ( is_string( $args[1] ) && '' === $args[1] ) ) {
return Colors::shouldColorize() ? Colors::colorize( $msg ) : $msg;
}
// If the first argument is not an array just pass to sprintf
if( !is_array( $args[1] ) ) {
// Colorize the message first so sprintf doesn't bitch at us
if ( Colors::shouldColorize() ) {
$args[0] = Colors::colorize( $args[0] );
}
// Escape percent characters for sprintf
$args[0] = preg_replace('/(%([^\w]|$))/', "%$1", $args[0]);
return call_user_func_array( 'sprintf', $args );
}
// Here we do named replacement so formatting strings are more understandable
foreach( $args[1] as $key => $value ) {
$msg = str_replace( '{:' . $key . '}', $value, $msg );
}
return Colors::shouldColorize() ? Colors::colorize( $msg ) : $msg;
}
/**
* Shortcut for printing to `STDOUT`. The message and parameters are passed
* through `sprintf` before output.
*
* @param string $msg The message to output in `printf` format.
* @param mixed ... Either scalar arguments or a single array argument.
* @return void
* @see \cli\render()
*/
public static function out( $msg ) {
fwrite( static::$out, self::_call( 'render', func_get_args() ) );
}
/**
* Pads `$msg` to the width of the shell before passing to `cli\out`.
*
* @param string $msg The message to pad and pass on.
* @param mixed ... Either scalar arguments or a single array argument.
* @return void
* @see cli\out()
*/
public static function out_padded( $msg ) {
$msg = self::_call( 'render', func_get_args() );
self::out( str_pad( $msg, \cli\Shell::columns() ) );
}
/**
* Prints a message to `STDOUT` with a newline appended. See `\cli\out` for
* more documentation.
*
* @see cli\out()
*/
public static function line( $msg = '' ) {
// func_get_args is empty if no args are passed even with the default above.
$args = array_merge( func_get_args(), array( '' ) );
$args[0] .= "\n";
self::_call( 'out', $args );
}
/**
* Shortcut for printing to `STDERR`. The message and parameters are passed
* through `sprintf` before output.
*
* @param string $msg The message to output in `printf` format. With no string,
* a newline is printed.
* @param mixed ... Either scalar arguments or a single array argument.
* @return void
*/
public static function err( $msg = '' ) {
// func_get_args is empty if no args are passed even with the default above.
$args = array_merge( func_get_args(), array( '' ) );
$args[0] .= "\n";
fwrite( static::$err, self::_call( 'render', $args ) );
}
/**
* Takes input from `STDIN` in the given format. If an end of transmission
* character is sent (^D), an exception is thrown.
*
* @param string $format A valid input format. See `fscanf` for documentation.
* If none is given, all input up to the first newline
* is accepted.
* @param boolean $hide If true will hide what the user types in.
* @return string The input with whitespace trimmed.
* @throws \Exception Thrown if ctrl-D (EOT) is sent as input.
*/
public static function input( $format = null, $hide = false ) {
if ( $hide )
Shell::hide();
if( $format ) {
fscanf( static::$in, $format . "\n", $line );
} else {
$line = fgets( static::$in );
}
if ( $hide ) {
Shell::hide( false );
echo "\n";
}
if( $line === false ) {
throw new \Exception( 'Caught ^D during input' );
}
return trim( $line );
}
/**
* Displays an input prompt. If no default value is provided the prompt will
* continue displaying until input is received.
*
* @param string $question The question to ask the user.
* @param bool|string $default A default value if the user provides no input.
* @param string $marker A string to append to the question and default value
* on display.
* @param boolean $hide Optionally hides what the user types in.
* @return string The users input.
* @see cli\input()
*/
public static function prompt( $question, $default = null, $marker = ': ', $hide = false ) {
if( $default && strpos( $question, '[' ) === false ) {
$question .= ' [' . $default . ']';
}
while( true ) {
self::out( $question . $marker );
$line = self::input( null, $hide );
if ( trim( $line ) !== '' )
return $line;
if( $default !== false )
return $default;
}
}
/**
* Presents a user with a multiple choice question, useful for 'yes/no' type
* questions (which this public static function defaults too).
*
* @param string $question The question to ask the user.
* @param string $choice A string of characters allowed as a response. Case is ignored.
* @param string $default The default choice. NULL if a default is not allowed.
* @return string The users choice.
* @see cli\prompt()
*/
public static function choose( $question, $choice = 'yn', $default = 'n' ) {
if( !is_string( $choice ) ) {
$choice = join( '', $choice );
}
// Make every choice character lowercase except the default
$choice = str_ireplace( $default, strtoupper( $default ), strtolower( $choice ) );
// Seperate each choice with a forward-slash
$choices = trim( join( '/', preg_split( '//', $choice ) ), '/' );
while( true ) {
$line = self::prompt( sprintf( '%s? [%s]', $question, $choices ), $default, '' );
if( stripos( $choice, $line ) !== false ) {
return strtolower( $line );
}
if( !empty( $default ) ) {
return strtolower( $default );
}
}
}
/**
* Displays an array of strings as a menu where a user can enter a number to
* choose an option. The array must be a single dimension with either strings
* or objects with a `__toString()` method.
*
* @param array $items The list of items the user can choose from.
* @param string $default The index of the default item.
* @param string $title The message displayed to the user when prompted.
* @return string The index of the chosen item.
* @see cli\line()
* @see cli\input()
* @see cli\err()
*/
public static function menu( $items, $default = null, $title = 'Choose an item' ) {
$map = array_values( $items );
if( $default && strpos( $title, '[' ) === false && isset( $items[$default] ) ) {
$title .= ' [' . $items[$default] . ']';
}
foreach( $map as $idx => $item ) {
self::line( ' %d. %s', $idx + 1, (string)$item );
}
self::line();
while( true ) {
fwrite( static::$out, sprintf( '%s: ', $title ) );
$line = self::input();
if( is_numeric( $line ) ) {
$line--;
if( isset( $map[$line] ) ) {
return array_search( $map[$line], $items );
}
if( $line < 0 || $line >= count( $map ) ) {
self::err( 'Invalid menu selection: out of range' );
}
} else if( isset( $default ) ) {
return $default;
}
}
}
/**
* Sets one of the streams (input, output, or error) to a `stream` type resource.
*
* Valid $whichStream values are:
* - 'in' (default: STDIN)
* - 'out' (default: STDOUT)
* - 'err' (default: STDERR)
*
* Any custom streams will be closed for you on shutdown, so please don't close stream
* resources used with this method.
*
* @param string $whichStream The stream property to update
* @param resource $stream The new stream resource to use
* @return void
* @throws \Exception Thrown if $stream is not a resource of the 'stream' type.
*/
public static function setStream( $whichStream, $stream ) {
if( !is_resource( $stream ) || get_resource_type( $stream ) !== 'stream' ) {
throw new \Exception( 'Invalid resource type!' );
}
if( property_exists( __CLASS__, $whichStream ) ) {
static::${$whichStream} = $stream;
}
register_shutdown_function( function() use ($stream) {
fclose( $stream );
} );
}
}