2015-08-17 17:00:26 -07:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\Console\Style ;
use Symfony\Component\Console\Application ;
use Symfony\Component\Console\Formatter\OutputFormatter ;
use Symfony\Component\Console\Helper\Helper ;
use Symfony\Component\Console\Helper\ProgressBar ;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper ;
use Symfony\Component\Console\Helper\Table ;
use Symfony\Component\Console\Input\InputInterface ;
2015-08-27 12:03:05 -07:00
use Symfony\Component\Console\Output\BufferedOutput ;
2015-08-17 17:00:26 -07:00
use Symfony\Component\Console\Output\OutputInterface ;
use Symfony\Component\Console\Question\ChoiceQuestion ;
use Symfony\Component\Console\Question\ConfirmationQuestion ;
use Symfony\Component\Console\Question\Question ;
/**
* Output decorator helpers for the Symfony Style Guide .
*
* @ author Kevin Bond < kevinbond @ gmail . com >
*/
class SymfonyStyle extends OutputStyle
{
const MAX_LINE_LENGTH = 120 ;
private $input ;
private $questionHelper ;
private $progressBar ;
private $lineLength ;
2015-08-27 12:03:05 -07:00
private $bufferedOutput ;
2015-08-17 17:00:26 -07:00
/**
* @ param InputInterface $input
* @ param OutputInterface $output
*/
public function __construct ( InputInterface $input , OutputInterface $output )
{
$this -> input = $input ;
2015-08-27 12:03:05 -07:00
$this -> bufferedOutput = new BufferedOutput ( $output -> getVerbosity (), false , clone $output -> getFormatter ());
// Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
$this -> lineLength = min ( $this -> getTerminalWidth () - ( int ) ( DIRECTORY_SEPARATOR === '\\' ), self :: MAX_LINE_LENGTH );
2015-08-17 17:00:26 -07:00
parent :: __construct ( $output );
}
/**
* Formats a message as a block of text .
*
* @ param string | array $messages The message to write in the block
* @ param string | null $type The block type ( added in [] on first line )
* @ param string | null $style The style to apply to the whole block
* @ param string $prefix The prefix for the block
* @ param bool $padding Whether to add vertical padding
*/
public function block ( $messages , $type = null , $style = null , $prefix = ' ' , $padding = false )
{
2015-08-27 12:03:05 -07:00
$this -> autoPrependBlock ();
2015-08-17 17:00:26 -07:00
$messages = is_array ( $messages ) ? array_values ( $messages ) : array ( $messages );
$lines = array ();
// add type
if ( null !== $type ) {
$messages [ 0 ] = sprintf ( '[%s] %s' , $type , $messages [ 0 ]);
}
// wrap and add newlines for each element
foreach ( $messages as $key => $message ) {
$message = OutputFormatter :: escape ( $message );
2015-08-27 12:03:05 -07:00
$lines = array_merge ( $lines , explode ( PHP_EOL , wordwrap ( $message , $this -> lineLength - Helper :: strlen ( $prefix ), PHP_EOL , true )));
2015-08-17 17:00:26 -07:00
if ( count ( $messages ) > 1 && $key < count ( $messages ) - 1 ) {
$lines [] = '' ;
}
}
if ( $padding && $this -> isDecorated ()) {
array_unshift ( $lines , '' );
$lines [] = '' ;
}
foreach ( $lines as & $line ) {
$line = sprintf ( '%s%s' , $prefix , $line );
$line .= str_repeat ( ' ' , $this -> lineLength - Helper :: strlenWithoutDecoration ( $this -> getFormatter (), $line ));
if ( $style ) {
$line = sprintf ( '<%s>%s</>' , $style , $line );
}
}
2015-08-27 12:03:05 -07:00
$this -> writeln ( $lines );
$this -> newLine ();
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function title ( $message )
{
2015-08-27 12:03:05 -07:00
$this -> autoPrependBlock ();
$this -> writeln ( array (
sprintf ( '<comment>%s</>' , $message ),
sprintf ( '<comment>%s</>' , str_repeat ( '=' , strlen ( $message ))),
));
$this -> newLine ();
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function section ( $message )
{
2015-08-27 12:03:05 -07:00
$this -> autoPrependBlock ();
$this -> writeln ( array (
sprintf ( '<comment>%s</>' , $message ),
sprintf ( '<comment>%s</>' , str_repeat ( '-' , strlen ( $message ))),
));
$this -> newLine ();
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function listing ( array $elements )
{
2015-08-27 12:03:05 -07:00
$this -> autoPrependText ();
2015-08-17 17:00:26 -07:00
$elements = array_map ( function ( $element ) {
2015-08-27 12:03:05 -07:00
return sprintf ( ' * %s' , $element );
}, $elements );
2015-08-17 17:00:26 -07:00
2015-08-27 12:03:05 -07:00
$this -> writeln ( $elements );
$this -> newLine ();
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function text ( $message )
{
2015-08-27 12:03:05 -07:00
$this -> autoPrependText ();
2015-08-17 17:00:26 -07:00
if ( ! is_array ( $message )) {
$this -> writeln ( sprintf ( ' // %s' , $message ));
return ;
}
foreach ( $message as $element ) {
$this -> text ( $element );
}
}
/**
* { @ inheritdoc }
*/
public function success ( $message )
{
2015-11-17 13:42:33 -08:00
$this -> block ( $message , 'OK' , 'fg=black;bg=green' , ' ' , true );
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function error ( $message )
{
$this -> block ( $message , 'ERROR' , 'fg=white;bg=red' , ' ' , true );
}
/**
* { @ inheritdoc }
*/
public function warning ( $message )
{
$this -> block ( $message , 'WARNING' , 'fg=white;bg=red' , ' ' , true );
}
/**
* { @ inheritdoc }
*/
public function note ( $message )
{
$this -> block ( $message , 'NOTE' , 'fg=yellow' , ' ! ' );
}
/**
* { @ inheritdoc }
*/
public function caution ( $message )
{
$this -> block ( $message , 'CAUTION' , 'fg=white;bg=red' , ' ! ' , true );
}
/**
* { @ inheritdoc }
*/
public function table ( array $headers , array $rows )
{
$headers = array_map ( function ( $value ) { return sprintf ( '<info>%s</>' , $value ); }, $headers );
$table = new Table ( $this );
$table -> setHeaders ( $headers );
$table -> setRows ( $rows );
$table -> setStyle ( 'symfony-style-guide' );
$table -> render ();
$this -> newLine ();
}
/**
* { @ inheritdoc }
*/
public function ask ( $question , $default = null , $validator = null )
{
$question = new Question ( $question , $default );
$question -> setValidator ( $validator );
2015-08-27 12:03:05 -07:00
return $this -> askQuestion ( $question );
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function askHidden ( $question , $validator = null )
{
$question = new Question ( $question );
2015-08-27 12:03:05 -07:00
2015-08-17 17:00:26 -07:00
$question -> setHidden ( true );
2015-08-27 12:03:05 -07:00
$question -> setValidator ( $validator );
2015-08-17 17:00:26 -07:00
2015-08-27 12:03:05 -07:00
return $this -> askQuestion ( $question );
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function confirm ( $question , $default = true )
{
return $this -> askQuestion ( new ConfirmationQuestion ( $question , $default ));
}
/**
* { @ inheritdoc }
*/
public function choice ( $question , array $choices , $default = null )
{
if ( null !== $default ) {
$values = array_flip ( $choices );
$default = $values [ $default ];
}
return $this -> askQuestion ( new ChoiceQuestion ( $question , $choices , $default ));
}
/**
* { @ inheritdoc }
*/
public function progressStart ( $max = 0 )
{
$this -> progressBar = $this -> createProgressBar ( $max );
$this -> progressBar -> start ();
}
/**
* { @ inheritdoc }
*/
public function progressAdvance ( $step = 1 )
{
$this -> getProgressBar () -> advance ( $step );
}
/**
* { @ inheritdoc }
*/
public function progressFinish ()
{
$this -> getProgressBar () -> finish ();
$this -> newLine ( 2 );
$this -> progressBar = null ;
}
/**
* { @ inheritdoc }
*/
public function createProgressBar ( $max = 0 )
{
$progressBar = parent :: createProgressBar ( $max );
if ( '\\' === DIRECTORY_SEPARATOR ) {
$progressBar -> setEmptyBarCharacter ( '░' ); // light shade character \u2591
$progressBar -> setProgressCharacter ( '' );
$progressBar -> setBarCharacter ( '▓' ); // dark shade character \u2593
}
return $progressBar ;
}
/**
* @ param Question $question
*
* @ return string
*/
public function askQuestion ( Question $question )
{
2015-08-27 12:03:05 -07:00
if ( $this -> input -> isInteractive ()) {
$this -> autoPrependBlock ();
}
2015-08-17 17:00:26 -07:00
if ( ! $this -> questionHelper ) {
$this -> questionHelper = new SymfonyQuestionHelper ();
}
$answer = $this -> questionHelper -> ask ( $this -> input , $this , $question );
2015-08-27 12:03:05 -07:00
if ( $this -> input -> isInteractive ()) {
$this -> newLine ();
$this -> bufferedOutput -> write ( " \n " );
}
2015-08-17 17:00:26 -07:00
return $answer ;
}
2015-08-27 12:03:05 -07:00
/**
* { @ inheritdoc }
*/
public function writeln ( $messages , $type = self :: OUTPUT_NORMAL )
{
parent :: writeln ( $messages , $type );
$this -> bufferedOutput -> writeln ( $this -> reduceBuffer ( $messages ), $type );
}
/**
* { @ inheritdoc }
*/
public function write ( $messages , $newline = false , $type = self :: OUTPUT_NORMAL )
{
parent :: write ( $messages , $newline , $type );
$this -> bufferedOutput -> write ( $this -> reduceBuffer ( $messages ), $newline , $type );
}
/**
* { @ inheritdoc }
*/
public function newLine ( $count = 1 )
{
parent :: newLine ( $count );
$this -> bufferedOutput -> write ( str_repeat ( " \n " , $count ));
}
2015-08-17 17:00:26 -07:00
/**
* @ return ProgressBar
*/
private function getProgressBar ()
{
if ( ! $this -> progressBar ) {
throw new \RuntimeException ( 'The ProgressBar is not started.' );
}
return $this -> progressBar ;
}
private function getTerminalWidth ()
{
$application = new Application ();
$dimensions = $application -> getTerminalDimensions ();
return $dimensions [ 0 ] ? : self :: MAX_LINE_LENGTH ;
}
2015-08-27 12:03:05 -07:00
private function autoPrependBlock ()
{
$chars = substr ( str_replace ( PHP_EOL , " \n " , $this -> bufferedOutput -> fetch ()), - 2 );
2015-09-04 13:20:09 -07:00
if ( ! isset ( $chars [ 0 ])) {
2015-08-27 12:03:05 -07:00
return $this -> newLine (); //empty history, so we should start with a new line.
}
//Prepend new line for each non LF chars (This means no blank line was output before)
$this -> newLine ( 2 - substr_count ( $chars , " \n " ));
}
private function autoPrependText ()
{
$fetched = $this -> bufferedOutput -> fetch ();
//Prepend new line if last char isn't EOL:
if ( " \n " !== substr ( $fetched , - 1 )) {
$this -> newLine ();
}
}
private function reduceBuffer ( $messages )
{
// We need to know if the two last chars are PHP_EOL
// Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer
return array_map ( function ( $value ) {
return substr ( $value , - 4 );
}, array_merge ( array ( $this -> bufferedOutput -> fetch ()), ( array ) $messages ));
}
2015-08-17 17:00:26 -07:00
}