diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index 62e075404f..3bec5f557e 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -23,12 +23,24 @@ class OutputFormatter implements OutputFormatterInterface /** * The pattern to phrase the format. */ - const FORMAT_PATTERN = '#<(/?)([a-z][a-z0-9_=;-]+)?>([^<]*)#is'; + const FORMAT_PATTERN = '#(\\\\?)<(/?)([a-z][a-z0-9_=;-]+)?>([^\\\\<]*)#is'; 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) + { + return preg_replace('/([^\\\\]?)" tag $this->styleStack->pop(); - return $this->applyStyle($this->styleStack->getCurrent(), $match[3]); + return $this->applyStyle($this->styleStack->getCurrent(), $match[4]); } // we got "<>" tag - return '<>'.$match[3]; + return '<>'.$match[4]; } - if (isset($this->styles[strtolower($match[2])])) { - $style = $this->styles[strtolower($match[2])]; + if (isset($this->styles[strtolower($match[3])])) { + $style = $this->styles[strtolower($match[3])]; } else { - $style = $this->createStyleFromString($match[2]); + $style = $this->createStyleFromString($match[3]); if (false === $style) { return $match[0]; } } - if ('/' === $match[1]) { + if ('/' === $match[2]) { $this->styleStack->pop($style); } else { $this->styleStack->push($style); } - return $this->applyStyle($this->styleStack->getCurrent(), $match[3]); + return $this->applyStyle($this->styleStack->getCurrent(), $match[4]); } /** diff --git a/src/Symfony/Component/Console/Helper/FormatterHelper.php b/src/Symfony/Component/Console/Helper/FormatterHelper.php index dd9615bc62..34ae394578 100644 --- a/src/Symfony/Component/Console/Helper/FormatterHelper.php +++ b/src/Symfony/Component/Console/Helper/FormatterHelper.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Console\Helper; +use Symfony\Component\Console\Formatter\OutputFormatter; + /** * The Formatter class provides helpers to format messages. * @@ -48,6 +50,7 @@ class FormatterHelper extends Helper $len = 0; $lines = array(); foreach ($messages as $message) { + $message = OutputFormatter::escape($message); $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); $len = max($this->strlen($message) + ($large ? 4 : 2), $len); } diff --git a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php index 0d1bf4104a..e53f5f5850 100644 --- a/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php +++ b/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php @@ -22,6 +22,14 @@ class FormatterStyleTest extends \PHPUnit_Framework_TestCase $this->assertEquals("foo<>bar", $formatter->format('foo<>bar')); } + public function testLGCharEscaping() + { + $formatter = new OutputFormatter(true); + $this->assertEquals("fooformat('foo\\assertEquals("some info", $formatter->format('\\some info\\')); + $this->assertEquals("\\some info\\", OutputFormatter::escape('some info')); + } + public function testBundledStyles() { $formatter = new OutputFormatter(true); diff --git a/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php index 430c077e82..5ab62a3933 100644 --- a/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php @@ -68,4 +68,17 @@ class FormatterHelperTest extends \PHPUnit_Framework_TestCase '::formatBlock() formats a message in a block' ); } + + public function testFormatBlockLGEscaping() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' ' . "\n" . + ' \some info\ ' . "\n" . + ' ', + $formatter->formatBlock('some info', 'error', true), + '::formatBlock() escapes \'<\' chars' + ); + } }