Yaffs site version 1.1
[yaffs-website] / vendor / symfony / console / Formatter / OutputFormatter.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Console\Formatter;
13
14 use Symfony\Component\Console\Exception\InvalidArgumentException;
15
16 /**
17  * Formatter class for console output.
18  *
19  * @author Konstantin Kudryashov <ever.zet@gmail.com>
20  */
21 class OutputFormatter implements OutputFormatterInterface
22 {
23     private $decorated;
24     private $styles = array();
25     private $styleStack;
26
27     /**
28      * Escapes "<" special char in given text.
29      *
30      * @param string $text Text to escape
31      *
32      * @return string Escaped text
33      */
34     public static function escape($text)
35     {
36         $text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
37
38         return self::escapeTrailingBackslash($text);
39     }
40
41     /**
42      * Escapes trailing "\" in given text.
43      *
44      * @param string $text Text to escape
45      *
46      * @return string Escaped text
47      *
48      * @internal
49      */
50     public static function escapeTrailingBackslash($text)
51     {
52         if ('\\' === substr($text, -1)) {
53             $len = strlen($text);
54             $text = rtrim($text, '\\');
55             $text .= str_repeat('<<', $len - strlen($text));
56         }
57
58         return $text;
59     }
60
61     /**
62      * Initializes console output formatter.
63      *
64      * @param bool                            $decorated Whether this formatter should actually decorate strings
65      * @param OutputFormatterStyleInterface[] $styles    Array of "name => FormatterStyle" instances
66      */
67     public function __construct($decorated = false, array $styles = array())
68     {
69         $this->decorated = (bool) $decorated;
70
71         $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
72         $this->setStyle('info', new OutputFormatterStyle('green'));
73         $this->setStyle('comment', new OutputFormatterStyle('yellow'));
74         $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
75
76         foreach ($styles as $name => $style) {
77             $this->setStyle($name, $style);
78         }
79
80         $this->styleStack = new OutputFormatterStyleStack();
81     }
82
83     /**
84      * {@inheritdoc}
85      */
86     public function setDecorated($decorated)
87     {
88         $this->decorated = (bool) $decorated;
89     }
90
91     /**
92      * {@inheritdoc}
93      */
94     public function isDecorated()
95     {
96         return $this->decorated;
97     }
98
99     /**
100      * {@inheritdoc}
101      */
102     public function setStyle($name, OutputFormatterStyleInterface $style)
103     {
104         $this->styles[strtolower($name)] = $style;
105     }
106
107     /**
108      * {@inheritdoc}
109      */
110     public function hasStyle($name)
111     {
112         return isset($this->styles[strtolower($name)]);
113     }
114
115     /**
116      * {@inheritdoc}
117      */
118     public function getStyle($name)
119     {
120         if (!$this->hasStyle($name)) {
121             throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
122         }
123
124         return $this->styles[strtolower($name)];
125     }
126
127     /**
128      * {@inheritdoc}
129      */
130     public function format($message)
131     {
132         $message = (string) $message;
133         $offset = 0;
134         $output = '';
135         $tagRegex = '[a-z][a-z0-9_=;-]*+';
136         preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
137         foreach ($matches[0] as $i => $match) {
138             $pos = $match[1];
139             $text = $match[0];
140
141             if (0 != $pos && '\\' == $message[$pos - 1]) {
142                 continue;
143             }
144
145             // add the text up to the next tag
146             $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
147             $offset = $pos + strlen($text);
148
149             // opening tag?
150             if ($open = '/' != $text[1]) {
151                 $tag = $matches[1][$i][0];
152             } else {
153                 $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
154             }
155
156             if (!$open && !$tag) {
157                 // </>
158                 $this->styleStack->pop();
159             } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
160                 $output .= $this->applyCurrentStyle($text);
161             } elseif ($open) {
162                 $this->styleStack->push($style);
163             } else {
164                 $this->styleStack->pop($style);
165             }
166         }
167
168         $output .= $this->applyCurrentStyle(substr($message, $offset));
169
170         if (false !== strpos($output, '<<')) {
171             return strtr($output, array('\\<' => '<', '<<' => '\\'));
172         }
173
174         return str_replace('\\<', '<', $output);
175     }
176
177     /**
178      * @return OutputFormatterStyleStack
179      */
180     public function getStyleStack()
181     {
182         return $this->styleStack;
183     }
184
185     /**
186      * Tries to create new style instance from string.
187      *
188      * @param string $string
189      *
190      * @return OutputFormatterStyle|false false if string is not format string
191      */
192     private function createStyleFromString($string)
193     {
194         if (isset($this->styles[$string])) {
195             return $this->styles[$string];
196         }
197
198         if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
199             return false;
200         }
201
202         $style = new OutputFormatterStyle();
203         foreach ($matches as $match) {
204             array_shift($match);
205
206             if ('fg' == $match[0]) {
207                 $style->setForeground($match[1]);
208             } elseif ('bg' == $match[0]) {
209                 $style->setBackground($match[1]);
210             } else {
211                 try {
212                     $style->setOption($match[1]);
213                 } catch (\InvalidArgumentException $e) {
214                     return false;
215                 }
216             }
217         }
218
219         return $style;
220     }
221
222     /**
223      * Applies current style from stack to text, if must be applied.
224      *
225      * @param string $text Input text
226      *
227      * @return string Styled text
228      */
229     private function applyCurrentStyle($text)
230     {
231         return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
232     }
233 }