dee6967594b36d630b21bdcec28c22c2c27872d1
[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      * Sets the decorated flag.
85      *
86      * @param bool $decorated Whether to decorate the messages or not
87      */
88     public function setDecorated($decorated)
89     {
90         $this->decorated = (bool) $decorated;
91     }
92
93     /**
94      * Gets the decorated flag.
95      *
96      * @return bool true if the output will decorate messages, false otherwise
97      */
98     public function isDecorated()
99     {
100         return $this->decorated;
101     }
102
103     /**
104      * Sets a new style.
105      *
106      * @param string                        $name  The style name
107      * @param OutputFormatterStyleInterface $style The style instance
108      */
109     public function setStyle($name, OutputFormatterStyleInterface $style)
110     {
111         $this->styles[strtolower($name)] = $style;
112     }
113
114     /**
115      * Checks if output formatter has style with specified name.
116      *
117      * @param string $name
118      *
119      * @return bool
120      */
121     public function hasStyle($name)
122     {
123         return isset($this->styles[strtolower($name)]);
124     }
125
126     /**
127      * Gets style options from style with specified name.
128      *
129      * @param string $name
130      *
131      * @return OutputFormatterStyleInterface
132      *
133      * @throws InvalidArgumentException When style isn't defined
134      */
135     public function getStyle($name)
136     {
137         if (!$this->hasStyle($name)) {
138             throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
139         }
140
141         return $this->styles[strtolower($name)];
142     }
143
144     /**
145      * Formats a message according to the given styles.
146      *
147      * @param string $message The message to style
148      *
149      * @return string The styled message
150      */
151     public function format($message)
152     {
153         $message = (string) $message;
154         $offset = 0;
155         $output = '';
156         $tagRegex = '[a-z][a-z0-9_=;-]*+';
157         preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
158         foreach ($matches[0] as $i => $match) {
159             $pos = $match[1];
160             $text = $match[0];
161
162             if (0 != $pos && '\\' == $message[$pos - 1]) {
163                 continue;
164             }
165
166             // add the text up to the next tag
167             $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
168             $offset = $pos + strlen($text);
169
170             // opening tag?
171             if ($open = '/' != $text[1]) {
172                 $tag = $matches[1][$i][0];
173             } else {
174                 $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
175             }
176
177             if (!$open && !$tag) {
178                 // </>
179                 $this->styleStack->pop();
180             } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
181                 $output .= $this->applyCurrentStyle($text);
182             } elseif ($open) {
183                 $this->styleStack->push($style);
184             } else {
185                 $this->styleStack->pop($style);
186             }
187         }
188
189         $output .= $this->applyCurrentStyle(substr($message, $offset));
190
191         if (false !== strpos($output, '<<')) {
192             return strtr($output, array('\\<' => '<', '<<' => '\\'));
193         }
194
195         return str_replace('\\<', '<', $output);
196     }
197
198     /**
199      * @return OutputFormatterStyleStack
200      */
201     public function getStyleStack()
202     {
203         return $this->styleStack;
204     }
205
206     /**
207      * Tries to create new style instance from string.
208      *
209      * @param string $string
210      *
211      * @return OutputFormatterStyle|false false if string is not format string
212      */
213     private function createStyleFromString($string)
214     {
215         if (isset($this->styles[$string])) {
216             return $this->styles[$string];
217         }
218
219         if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
220             return false;
221         }
222
223         $style = new OutputFormatterStyle();
224         foreach ($matches as $match) {
225             array_shift($match);
226
227             if ('fg' == $match[0]) {
228                 $style->setForeground($match[1]);
229             } elseif ('bg' == $match[0]) {
230                 $style->setBackground($match[1]);
231             } else {
232                 try {
233                     $style->setOption($match[1]);
234                 } catch (\InvalidArgumentException $e) {
235                     return false;
236                 }
237             }
238         }
239
240         return $style;
241     }
242
243     /**
244      * Applies current style from stack to text, if must be applied.
245      *
246      * @param string $text Input text
247      *
248      * @return string Styled text
249      */
250     private function applyCurrentStyle($text)
251     {
252         return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
253     }
254 }