-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Console] implemented output formatter to decorate and format output …
…messages
- Loading branch information
Showing
2 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
223 changes: 223 additions & 0 deletions
223
src/Symfony/Component/Console/Formatter/OutputFormatter.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
<?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; | ||
|
||
/** | ||
* Formatter class for console output. | ||
* | ||
* @author Konstantin Kudryashov <ever.zet@gmail.com> | ||
*/ | ||
class OutputFormatter implements OutputFormatterInterface | ||
{ | ||
private $decorated; | ||
private $styles = array(); | ||
|
||
/** | ||
* Initializes console output formatter. | ||
* | ||
* @param boolean $decorated Whether this formatter should actually decorate strings | ||
* @param array $styles Array of "name => FormatterStyle" instance | ||
*/ | ||
public function __construct($decorated = null, array $styles = array()) | ||
{ | ||
$this->decorated = (Boolean) $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); | ||
} | ||
} | ||
|
||
/** | ||
* Sets the decorated flag. | ||
* | ||
* @param Boolean $decorated Whether to decorated the messages or not | ||
*/ | ||
public function setDecorated($decorated) | ||
{ | ||
$this->decorated = (Boolean) $decorated; | ||
} | ||
|
||
/** | ||
* Gets the decorated flag. | ||
* | ||
* @return Boolean true if the output will decorate messages, false otherwise | ||
*/ | ||
public function isDecorated() | ||
{ | ||
return $this->decorated; | ||
} | ||
|
||
/** | ||
* Sets a new style. | ||
* | ||
* @param string $name The style name | ||
* @param OutputFormatterStyleInterface $options The style instance | ||
*/ | ||
public function setStyle($name, OutputFormatterStyleInterface $style) | ||
{ | ||
$this->styles[strtolower($name)] = $style; | ||
} | ||
|
||
/** | ||
* Checks if output formatter has style with specified name. | ||
* | ||
* @param string $name | ||
* | ||
* @return boolean | ||
*/ | ||
public function hasStyle($name) | ||
{ | ||
return isset($this->styles[strtolower($name)]); | ||
} | ||
|
||
/** | ||
* Gets style options from style with specified name. | ||
* | ||
* @param string $name | ||
* | ||
* @return OutputFormatterStyleInterface | ||
*/ | ||
public function getStyle($name) | ||
{ | ||
if (!$this->hasStyle($name)) { | ||
throw new \InvalidArgumentException('Undefined style: ' . $name); | ||
} | ||
|
||
return $this->styles[strtolower($name)]; | ||
} | ||
|
||
/** | ||
* Formats a message according to the given styles. | ||
* | ||
* @param string $message The message to style | ||
* | ||
* @return string The styled message | ||
*/ | ||
public function format($message) | ||
{ | ||
$message = preg_replace_callback( | ||
$this->getBeginStyleRegex(), array($this, 'replaceBeginStyle'), $message | ||
); | ||
|
||
return preg_replace_callback( | ||
$this->getEndStyleRegex(), array($this, 'replaceEndStyle'), $message | ||
); | ||
} | ||
|
||
/** | ||
* Gets regex for a style start. | ||
* | ||
* @return string | ||
*/ | ||
protected function getBeginStyleRegex() | ||
{ | ||
return '#<([a-z][a-z0-9\-_=;]+)>#i'; | ||
} | ||
|
||
/** | ||
* Gets regex for a style end. | ||
* | ||
* @return string | ||
*/ | ||
protected function getEndStyleRegex() | ||
{ | ||
return '#</([a-z][a-z0-9\-_]*)?>#i'; | ||
} | ||
|
||
/** | ||
* Replaces the starting style of the output. | ||
* | ||
* @param array $match | ||
* | ||
* @return string The replaced style | ||
* | ||
* @throws \InvalidArgumentException When style is unknown | ||
*/ | ||
private function replaceBeginStyle($match) | ||
{ | ||
if (!$this->isDecorated()) { | ||
return ''; | ||
} | ||
|
||
if (isset($this->styles[strtolower($match[1])])) { | ||
$style = $this->styles[strtolower($match[1])]; | ||
} else { | ||
$style = $this->createStyleFromString($match[1]); | ||
|
||
if (false === $style) { | ||
return $match[0]; | ||
} | ||
} | ||
|
||
return $style->getBeginStyle(); | ||
} | ||
|
||
/** | ||
* Replaces the end style. | ||
* | ||
* @param string $match The text to match | ||
* | ||
* @return string The end style | ||
*/ | ||
private function replaceEndStyle($match) | ||
{ | ||
if (!$this->isDecorated()) { | ||
return ''; | ||
} | ||
|
||
if ('style' == $match[1]) { | ||
$style = new OutputFormatterStyle(); | ||
} else { | ||
if (!isset($this->styles[strtolower($match[1])])) { | ||
return $match[0]; | ||
} | ||
|
||
$style = $this->styles[strtolower($match[1])]; | ||
} | ||
|
||
return $style->getEndStyle(); | ||
} | ||
|
||
/** | ||
* Tryes to create new style instance from string. | ||
* | ||
* @param string $string | ||
* | ||
* @return Symfony\Component\Console\Format\FormatterStyle|boolean false if string is not format string | ||
*/ | ||
private function createStyleFromString($string) | ||
{ | ||
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { | ||
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]); | ||
} else { | ||
$style->setOption($match[1]); | ||
} | ||
} | ||
|
||
return $style; | ||
} | ||
} |
137 changes: 137 additions & 0 deletions
137
tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<?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\Tests\Component\Console\Formatter; | ||
|
||
use Symfony\Component\Console\Formatter\OutputFormatter; | ||
use Symfony\Component\Console\Formatter\OutputFormatterStyle; | ||
|
||
class FormatterStyleTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
public function testBundledStyles() | ||
{ | ||
$formatter = new OutputFormatter(true); | ||
|
||
$this->assertTrue($formatter->hasStyle('error')); | ||
$this->assertTrue($formatter->hasStyle('info')); | ||
$this->assertTrue($formatter->hasStyle('comment')); | ||
$this->assertTrue($formatter->hasStyle('question')); | ||
|
||
$this->assertEquals( | ||
"\033[37;41msome error\033[0m", $formatter->format('<error>some error</error>') | ||
); | ||
$this->assertEquals( | ||
"\033[32msome info\033[0m", $formatter->format('<info>some info</info>') | ||
); | ||
$this->assertEquals( | ||
"\033[33msome comment\033[0m", $formatter->format('<comment>some comment</comment>') | ||
); | ||
$this->assertEquals( | ||
"\033[30;46msome question\033[0m", $formatter->format('<question>some question</question>') | ||
); | ||
} | ||
|
||
public function testNewStyle() | ||
{ | ||
$formatter = new OutputFormatter(true); | ||
|
||
$style = $this->getMockBuilder('Symfony\Component\Console\Formatter\OutputFormatterStyleInterface') | ||
->getMock(); | ||
$formatter->setStyle('test', $style); | ||
|
||
$this->assertEquals($style, $formatter->getStyle('test')); | ||
$this->assertNotEquals($style, $formatter->getStyle('info')); | ||
|
||
$style | ||
->expects($this->once()) | ||
->method('getBeginStyle') | ||
->will($this->returnValue('[STYLE_BEG]')); | ||
|
||
$style | ||
->expects($this->once()) | ||
->method('getEndStyle') | ||
->will($this->returnValue('[STYLE_END]')); | ||
|
||
$this->assertEquals( | ||
"[STYLE_BEG]some custom msg[STYLE_END]", $formatter->format('<test>some custom msg</test>') | ||
); | ||
} | ||
|
||
public function testRedefineStyle() | ||
{ | ||
$formatter = new OutputFormatter(true); | ||
|
||
$style = $this->getMockBuilder('Symfony\Component\Console\Formatter\OutputFormatterStyleInterface') | ||
->getMock(); | ||
$formatter->setStyle('info', $style); | ||
|
||
$style | ||
->expects($this->once()) | ||
->method('getBeginStyle') | ||
->will($this->returnValue('[STYLE_BEG]')); | ||
|
||
$style | ||
->expects($this->once()) | ||
->method('getEndStyle') | ||
->will($this->returnValue('[STYLE_END]')); | ||
|
||
$this->assertEquals( | ||
"[STYLE_BEG]some custom msg[STYLE_END]", $formatter->format('<info>some custom msg</info>') | ||
); | ||
} | ||
|
||
public function testInlineStyle() | ||
{ | ||
$formatter = new OutputFormatter(true); | ||
|
||
$this->assertEquals( | ||
"\033[34;41msome text\033[0m", $formatter->format('<fg=blue;bg=red>some text</style>') | ||
); | ||
} | ||
|
||
public function testNotDecoratedFormatter() | ||
{ | ||
$formatter = new OutputFormatter(false); | ||
|
||
$this->assertTrue($formatter->hasStyle('error')); | ||
$this->assertTrue($formatter->hasStyle('info')); | ||
$this->assertTrue($formatter->hasStyle('comment')); | ||
$this->assertTrue($formatter->hasStyle('question')); | ||
|
||
$this->assertEquals( | ||
"some error", $formatter->format('<error>some error</error>') | ||
); | ||
$this->assertEquals( | ||
"some info", $formatter->format('<info>some info</info>') | ||
); | ||
$this->assertEquals( | ||
"some comment", $formatter->format('<comment>some comment</comment>') | ||
); | ||
$this->assertEquals( | ||
"some question", $formatter->format('<question>some question</question>') | ||
); | ||
|
||
$formatter->setDecorated(true); | ||
|
||
$this->assertEquals( | ||
"\033[37;41msome error\033[0m", $formatter->format('<error>some error</error>') | ||
); | ||
$this->assertEquals( | ||
"\033[32msome info\033[0m", $formatter->format('<info>some info</info>') | ||
); | ||
$this->assertEquals( | ||
"\033[33msome comment\033[0m", $formatter->format('<comment>some comment</comment>') | ||
); | ||
$this->assertEquals( | ||
"\033[30;46msome question\033[0m", $formatter->format('<question>some question</question>') | ||
); | ||
} | ||
} |