PluginProbe
Depicter — Popup & Slider Builder / 1.3.2
Depicter — Popup & Slider Builder v1.3.2
4.8.1 trunk 1.0.0 1.1.0 1.1.2 1.1.4 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.5 1.3.8 1.5.0 1.5.1 1.5.2 1.5.5 1.6.0 1.6.1 1.6.2 1.7.0 All 76 releases
depicter / vendor / symfony / console / Formatter / OutputFormatter.php

OutputFormatter.php in Depicter — Popup & Slider Builder 1.3.2, at vendor/symfony/console/Formatter/OutputFormatter.php

284 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 * @author Roland Franssen <franssen.roland@gmail.com>
21 */
22 class OutputFormatter implements WrappableOutputFormatterInterface
23 {
24 private $decorated;
25 private $styles = [];
26 private $styleStack;
27
28 public function __clone()
29 {
30 $this->styleStack = clone $this->styleStack;
31 foreach ($this->styles as $key => $value) {
32 $this->styles[$key] = clone $value;
33 }
34 }
35
36 /**
37 * Escapes "<" special char in given text.
38 *
39 * @return string Escaped text
40 */
41 public static function escape(string $text)
42 {
43 $text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
44
45 return self::escapeTrailingBackslash($text);
46 }
47
48 /**
49 * Escapes trailing "\" in given text.
50 *
51 * @internal
52 */
53 public static function escapeTrailingBackslash(string $text): string
54 {
55 if ('\\' === substr($text, -1)) {
56 $len = \strlen($text);
57 $text = rtrim($text, '\\');
58 $text = str_replace("\0", '', $text);
59 $text .= str_repeat("\0", $len - \strlen($text));
60 }
61
62 return $text;
63 }
64
65 /**
66 * Initializes console output formatter.
67 *
68 * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances
69 */
70 public function __construct(bool $decorated = false, array $styles = [])
71 {
72 $this->decorated = $decorated;
73
74 $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
75 $this->setStyle('info', new OutputFormatterStyle('green'));
76 $this->setStyle('comment', new OutputFormatterStyle('yellow'));
77 $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
78
79 foreach ($styles as $name => $style) {
80 $this->setStyle($name, $style);
81 }
82
83 $this->styleStack = new OutputFormatterStyleStack();
84 }
85
86 /**
87 * {@inheritdoc}
88 */
89 public function setDecorated(bool $decorated)
90 {
91 $this->decorated = $decorated;
92 }
93
94 /**
95 * {@inheritdoc}
96 */
97 public function isDecorated()
98 {
99 return $this->decorated;
100 }
101
102 /**
103 * {@inheritdoc}
104 */
105 public function setStyle(string $name, OutputFormatterStyleInterface $style)
106 {
107 $this->styles[strtolower($name)] = $style;
108 }
109
110 /**
111 * {@inheritdoc}
112 */
113 public function hasStyle(string $name)
114 {
115 return isset($this->styles[strtolower($name)]);
116 }
117
118 /**
119 * {@inheritdoc}
120 */
121 public function getStyle(string $name)
122 {
123 if (!$this->hasStyle($name)) {
124 throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name));
125 }
126
127 return $this->styles[strtolower($name)];
128 }
129
130 /**
131 * {@inheritdoc}
132 */
133 public function format(?string $message)
134 {
135 return $this->formatAndWrap($message, 0);
136 }
137
138 /**
139 * {@inheritdoc}
140 */
141 public function formatAndWrap(?string $message, int $width)
142 {
143 $offset = 0;
144 $output = '';
145 $tagRegex = '[a-z][^<>]*+';
146 $currentLineLength = 0;
147 preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, \PREG_OFFSET_CAPTURE);
148 foreach ($matches[0] as $i => $match) {
149 $pos = $match[1];
150 $text = $match[0];
151
152 if (0 != $pos && '\\' == $message[$pos - 1]) {
153 continue;
154 }
155
156 // add the text up to the next tag
157 $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
158 $offset = $pos + \strlen($text);
159
160 // opening tag?
161 if ($open = '/' != $text[1]) {
162 $tag = $matches[1][$i][0];
163 } else {
164 $tag = $matches[3][$i][0] ?? '';
165 }
166
167 if (!$open && !$tag) {
168 // </>
169 $this->styleStack->pop();
170 } elseif (null === $style = $this->createStyleFromString($tag)) {
171 $output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength);
172 } elseif ($open) {
173 $this->styleStack->push($style);
174 } else {
175 $this->styleStack->pop($style);
176 }
177 }
178
179 $output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
180
181 if (false !== strpos($output, "\0")) {
182 return strtr($output, ["\0" => '\\', '\\<' => '<']);
183 }
184
185 return str_replace('\\<', '<', $output);
186 }
187
188 /**
189 * @return OutputFormatterStyleStack
190 */
191 public function getStyleStack()
192 {
193 return $this->styleStack;
194 }
195
196 /**
197 * Tries to create new style instance from string.
198 */
199 private function createStyleFromString(string $string): ?OutputFormatterStyleInterface
200 {
201 if (isset($this->styles[$string])) {
202 return $this->styles[$string];
203 }
204
205 if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, \PREG_SET_ORDER)) {
206 return null;
207 }
208
209 $style = new OutputFormatterStyle();
210 foreach ($matches as $match) {
211 array_shift($match);
212 $match[0] = strtolower($match[0]);
213
214 if ('fg' == $match[0]) {
215 $style->setForeground(strtolower($match[1]));
216 } elseif ('bg' == $match[0]) {
217 $style->setBackground(strtolower($match[1]));
218 } elseif ('href' === $match[0]) {
219 $style->setHref($match[1]);
220 } elseif ('options' === $match[0]) {
221 preg_match_all('([^,;]+)', strtolower($match[1]), $options);
222 $options = array_shift($options);
223 foreach ($options as $option) {
224 $style->setOption($option);
225 }
226 } else {
227 return null;
228 }
229 }
230
231 return $style;
232 }
233
234 /**
235 * Applies current style from stack to text, if must be applied.
236 */
237 private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string
238 {
239 if ('' === $text) {
240 return '';
241 }
242
243 if (!$width) {
244 return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text;
245 }
246
247 if (!$currentLineLength && '' !== $current) {
248 $text = ltrim($text);
249 }
250
251 if ($currentLineLength) {
252 $prefix = substr($text, 0, $i = $width - $currentLineLength)."\n";
253 $text = substr($text, $i);
254 } else {
255 $prefix = '';
256 }
257
258 preg_match('~(\\n)$~', $text, $matches);
259 $text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text);
260 $text = rtrim($text, "\n").($matches[1] ?? '');
261
262 if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) {
263 $text = "\n".$text;
264 }
265
266 $lines = explode("\n", $text);
267
268 foreach ($lines as $line) {
269 $currentLineLength += \strlen($line);
270 if ($width <= $currentLineLength) {
271 $currentLineLength = 0;
272 }
273 }
274
275 if ($this->isDecorated()) {
276 foreach ($lines as $i => $line) {
277 $lines[$i] = $this->styleStack->getCurrent()->apply($line);
278 }
279 }
280
281 return implode("\n", $lines);
282 }
283 }
284