6ece9e90684e15eb85f2491ba3d8b1b439169ae3
[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_replace("\0", '', $text);
56             $text .= str_repeat("\0", $len - strlen($text));
57         }
58
59         return $text;
60     }
61
62     /**
63      * Initializes console output formatter.
64      *
65      * @param bool                            $decorated Whether this formatter should actually decorate strings
66      * @param OutputFormatterStyleInterface[] $styles    Array of "name => FormatterStyle" instances
67      */
68     public function __construct($decorated = false, array $styles = array())
69     {
70         $this->decorated = (bool) $decorated;
71
72         $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
73         $this->setStyle('info', new OutputFormatterStyle('green'));
74         $this->setStyle('comment', new OutputFormatterStyle('yellow'));
75         $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
76
77         foreach ($styles as $name => $style) {
78             $this->setStyle($name, $style);
79         }
80
81         $this->styleStack = new OutputFormatterStyleStack();
82     }
83
84     /**
85      * {@inheritdoc}
86      */
87     public function setDecorated($decorated)
88     {
89         $this->decorated = (bool) $decorated;
90     }
91
92     /**
93      * {@inheritdoc}
94      */
95     public function isDecorated()
96     {
97         return $this->decorated;
98     }
99
100     /**
101      * {@inheritdoc}
102      */
103     public function setStyle($name, OutputFormatterStyleInterface $style)
104     {
105         $this->styles[strtolower($name)] = $style;
106     }
107
108     /**
109      * {@inheritdoc}
110      */
111     public function hasStyle($name)
112     {
113         return isset($this->styles[strtolower($name)]);
114     }
115
116     /**
117      * {@inheritdoc}
118      */
119     public function getStyle($name)
120     {
121         if (!$this->hasStyle($name)) {
122             throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
123         }
124
125         return $this->styles[strtolower($name)];
126     }
127
128     /**
129      * {@inheritdoc}
130      */
131     public function format($message)
132     {
133         $message = (string) $message;
134         $offset = 0;
135         $output = '';
136         $tagRegex = '[a-z][a-z0-9,_=;-]*+';
137         preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
138         foreach ($matches[0] as $i => $match) {
139             $pos = $match[1];
140             $text = $match[0];
141
142             if (0 != $pos && '\\' == $message[$pos - 1]) {
143                 continue;
144             }
145
146             // add the text up to the next tag
147             $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
148             $offset = $pos + strlen($text);
149
150             // opening tag?
151             if ($open = '/' != $text[1]) {
152                 $tag = $matches[1][$i][0];
153             } else {
154                 $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
155             }
156
157             if (!$open && !$tag) {
158                 // </>
159                 $this->styleStack->pop();
160             } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
161                 $output .= $this->applyCurrentStyle($text);
162             } elseif ($open) {
163                 $this->styleStack->push($style);
164             } else {
165                 $this->styleStack->pop($style);
166             }
167         }
168
169         $output .= $this->applyCurrentStyle(substr($message, $offset));
170
171         if (false !== strpos($output, "\0")) {
172             return strtr($output, array("\0" => '\\', '\\<' => '<'));
173         }
174
175         return str_replace('\\<', '<', $output);
176     }
177
178     /**
179      * @return OutputFormatterStyleStack
180      */
181     public function getStyleStack()
182     {
183         return $this->styleStack;
184     }
185
186     /**
187      * Tries to create new style instance from string.
188      *
189      * @param string $string
190      *
191      * @return OutputFormatterStyle|false false if string is not format string
192      */
193     private function createStyleFromString($string)
194     {
195         if (isset($this->styles[$string])) {
196             return $this->styles[$string];
197         }
198
199         if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, PREG_SET_ORDER)) {
200             return false;
201         }
202
203         $style = new OutputFormatterStyle();
204         foreach ($matches as $match) {
205             array_shift($match);
206
207             if ('fg' == $match[0]) {
208                 $style->setForeground($match[1]);
209             } elseif ('bg' == $match[0]) {
210                 $style->setBackground($match[1]);
211             } elseif ('options' === $match[0]) {
212                 preg_match_all('([^,;]+)', $match[1], $options);
213                 $options = array_shift($options);
214                 foreach ($options as $option) {
215                     try {
216                         $style->setOption($option);
217                     } catch (\InvalidArgumentException $e) {
218                         @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);
219
220                         return false;
221                     }
222                 }
223             } else {
224                 return false;
225             }
226         }
227
228         return $style;
229     }
230
231     /**
232      * Applies current style from stack to text, if must be applied.
233      *
234      * @param string $text Input text
235      *
236      * @return string Styled text
237      */
238     private function applyCurrentStyle($text)
239     {
240         return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
241     }
242 }