2015-08-18 00:00:26 +00: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\Formatter ;
2016-04-20 16:56:34 +00:00
use Symfony\Component\Console\Exception\InvalidArgumentException ;
2015-08-18 00:00:26 +00:00
/**
* Formatter class for console output .
*
* @ author Konstantin Kudryashov < ever . zet @ gmail . com >
*/
class OutputFormatter implements OutputFormatterInterface
{
private $decorated ;
private $styles = array ();
private $styleStack ;
/**
* Escapes " < " special char in given text .
*
* @ param string $text Text to escape
*
* @ return string Escaped text
*/
public static function escape ( $text )
{
2016-04-20 16:56:34 +00:00
$text = preg_replace ( '/([^\\\\]?)</' , '$1\\<' , $text );
2017-02-03 00:28:38 +00:00
return self :: escapeTrailingBackslash ( $text );
}
/**
* Escapes trailing " \" in given text.
*
* @ param string $text Text to escape
*
* @ return string Escaped text
*
* @ internal
*/
public static function escapeTrailingBackslash ( $text )
{
2016-04-20 16:56:34 +00:00
if ( '\\' === substr ( $text , - 1 )) {
2018-11-23 12:29:20 +00:00
$len = \strlen ( $text );
2016-04-20 16:56:34 +00:00
$text = rtrim ( $text , '\\' );
2018-11-23 12:29:20 +00:00
$text = str_replace ( " \0 " , '' , $text );
$text .= str_repeat ( " \0 " , $len - \strlen ( $text ));
2016-04-20 16:56:34 +00:00
}
return $text ;
2015-08-18 00:00:26 +00:00
}
/**
* Initializes console output formatter .
*
* @ param bool $decorated Whether this formatter should actually decorate strings
* @ param OutputFormatterStyleInterface [] $styles Array of " name => FormatterStyle " instances
*/
public function __construct ( $decorated = false , array $styles = array ())
{
$this -> decorated = ( bool ) $decorated ;
$this -> setStyle ( 'error' , new OutputFormatterStyle ( 'white' , 'red' ));
$this -> setStyle ( 'info' , new OutputFormatterStyle ( 'green' ));
$this -> setStyle ( 'comment' , new OutputFormatterStyle ( 'yellow' ));
$this -> setStyle ( 'question' , new OutputFormatterStyle ( 'black' , 'cyan' ));
foreach ( $styles as $name => $style ) {
$this -> setStyle ( $name , $style );
}
$this -> styleStack = new OutputFormatterStyleStack ();
}
/**
2017-07-03 15:47:07 +00:00
* { @ inheritdoc }
2015-08-18 00:00:26 +00:00
*/
public function setDecorated ( $decorated )
{
$this -> decorated = ( bool ) $decorated ;
}
/**
2017-07-03 15:47:07 +00:00
* { @ inheritdoc }
2015-08-18 00:00:26 +00:00
*/
public function isDecorated ()
{
return $this -> decorated ;
}
/**
2017-07-03 15:47:07 +00:00
* { @ inheritdoc }
2015-08-18 00:00:26 +00:00
*/
public function setStyle ( $name , OutputFormatterStyleInterface $style )
{
$this -> styles [ strtolower ( $name )] = $style ;
}
/**
2017-07-03 15:47:07 +00:00
* { @ inheritdoc }
2015-08-18 00:00:26 +00:00
*/
public function hasStyle ( $name )
{
return isset ( $this -> styles [ strtolower ( $name )]);
}
/**
2017-07-03 15:47:07 +00:00
* { @ inheritdoc }
2015-08-18 00:00:26 +00:00
*/
public function getStyle ( $name )
{
if ( ! $this -> hasStyle ( $name )) {
2016-04-20 16:56:34 +00:00
throw new InvalidArgumentException ( sprintf ( 'Undefined style: %s' , $name ));
2015-08-18 00:00:26 +00:00
}
return $this -> styles [ strtolower ( $name )];
}
/**
2017-07-03 15:47:07 +00:00
* { @ inheritdoc }
2015-08-18 00:00:26 +00:00
*/
public function format ( $message )
{
$message = ( string ) $message ;
$offset = 0 ;
$output = '' ;
2018-11-23 12:29:20 +00:00
$tagRegex = '[a-z][a-z0-9,_=;-]*+' ;
2015-08-27 19:03:05 +00:00
preg_match_all ( " #<(( $tagRegex ) | /( $tagRegex )?)>#ix " , $message , $matches , PREG_OFFSET_CAPTURE );
2015-08-18 00:00:26 +00:00
foreach ( $matches [ 0 ] as $i => $match ) {
$pos = $match [ 1 ];
$text = $match [ 0 ];
if ( 0 != $pos && '\\' == $message [ $pos - 1 ]) {
continue ;
}
// add the text up to the next tag
$output .= $this -> applyCurrentStyle ( substr ( $message , $offset , $pos - $offset ));
2018-11-23 12:29:20 +00:00
$offset = $pos + \strlen ( $text );
2015-08-18 00:00:26 +00:00
// opening tag?
if ( $open = '/' != $text [ 1 ]) {
$tag = $matches [ 1 ][ $i ][ 0 ];
} else {
$tag = isset ( $matches [ 3 ][ $i ][ 0 ]) ? $matches [ 3 ][ $i ][ 0 ] : '' ;
}
if ( ! $open && ! $tag ) {
// </>
$this -> styleStack -> pop ();
} elseif ( false === $style = $this -> createStyleFromString ( strtolower ( $tag ))) {
$output .= $this -> applyCurrentStyle ( $text );
} elseif ( $open ) {
$this -> styleStack -> push ( $style );
} else {
$this -> styleStack -> pop ( $style );
}
}
$output .= $this -> applyCurrentStyle ( substr ( $message , $offset ));
2018-11-23 12:29:20 +00:00
if ( false !== strpos ( $output , " \0 " )) {
return strtr ( $output , array ( " \0 " => '\\' , '\\<' => '<' ));
2016-04-20 16:56:34 +00:00
}
2015-08-18 00:00:26 +00:00
return str_replace ( '\\<' , '<' , $output );
}
/**
* @ return OutputFormatterStyleStack
*/
public function getStyleStack ()
{
return $this -> styleStack ;
}
/**
* Tries to create new style instance from string .
*
* @ param string $string
*
2017-02-03 00:28:38 +00:00
* @ return OutputFormatterStyle | false false if string is not format string
2015-08-18 00:00:26 +00:00
*/
private function createStyleFromString ( $string )
{
if ( isset ( $this -> styles [ $string ])) {
return $this -> styles [ $string ];
}
2018-11-23 12:29:20 +00:00
if ( ! preg_match_all ( '/([^=]+)=([^;]+)(;|$)/' , $string , $matches , PREG_SET_ORDER )) {
2015-08-18 00:00:26 +00:00
return false ;
}
$style = new OutputFormatterStyle ();
foreach ( $matches as $match ) {
array_shift ( $match );
if ( 'fg' == $match [ 0 ]) {
$style -> setForeground ( $match [ 1 ]);
} elseif ( 'bg' == $match [ 0 ]) {
$style -> setBackground ( $match [ 1 ]);
2018-11-23 12:29:20 +00:00
} elseif ( 'options' === $match [ 0 ]) {
preg_match_all ( '([^,;]+)' , $match [ 1 ], $options );
$options = array_shift ( $options );
foreach ( $options as $option ) {
try {
$style -> setOption ( $option );
} catch ( \InvalidArgumentException $e ) {
@ trigger_error ( sprintf ( 'Unknown style options are deprecated since Symfony 3.2 and will be removed in 4.0. Exception "%s".' , $e -> getMessage ()), E_USER_DEPRECATED );
return false ;
}
2015-08-18 00:00:26 +00:00
}
2018-11-23 12:29:20 +00:00
} else {
return false ;
2015-08-18 00:00:26 +00:00
}
}
return $style ;
}
/**
* Applies current style from stack to text , if must be applied .
*
* @ param string $text Input text
*
* @ return string Styled text
*/
private function applyCurrentStyle ( $text )
{
2018-11-23 12:29:20 +00:00
return $this -> isDecorated () && \strlen ( $text ) > 0 ? $this -> styleStack -> getCurrent () -> apply ( $text ) : $text ;
2015-08-18 00:00:26 +00:00
}
}