Complex classes like BaseConsole often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use BaseConsole, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class BaseConsole |
||
22 | { |
||
23 | // foreground color control codes |
||
24 | const FG_BLACK = 30; |
||
25 | const FG_RED = 31; |
||
26 | const FG_GREEN = 32; |
||
27 | const FG_YELLOW = 33; |
||
28 | const FG_BLUE = 34; |
||
29 | const FG_PURPLE = 35; |
||
30 | const FG_CYAN = 36; |
||
31 | const FG_GREY = 37; |
||
32 | // background color control codes |
||
33 | const BG_BLACK = 40; |
||
34 | const BG_RED = 41; |
||
35 | const BG_GREEN = 42; |
||
36 | const BG_YELLOW = 43; |
||
37 | const BG_BLUE = 44; |
||
38 | const BG_PURPLE = 45; |
||
39 | const BG_CYAN = 46; |
||
40 | const BG_GREY = 47; |
||
41 | // fonts style control codes |
||
42 | const RESET = 0; |
||
43 | const NORMAL = 0; |
||
44 | const BOLD = 1; |
||
45 | const ITALIC = 3; |
||
46 | const UNDERLINE = 4; |
||
47 | const BLINK = 5; |
||
48 | const NEGATIVE = 7; |
||
49 | const CONCEALED = 8; |
||
50 | const CROSSED_OUT = 9; |
||
51 | const FRAMED = 51; |
||
52 | const ENCIRCLED = 52; |
||
53 | const OVERLINED = 53; |
||
54 | |||
55 | |||
56 | /** |
||
57 | * Moves the terminal cursor up by sending ANSI control code CUU to the terminal. |
||
58 | * If the cursor is already at the edge of the screen, this has no effect. |
||
59 | * @param int $rows number of rows the cursor should be moved up |
||
60 | */ |
||
61 | 1 | public static function moveCursorUp($rows = 1) |
|
65 | |||
66 | /** |
||
67 | * Moves the terminal cursor down by sending ANSI control code CUD to the terminal. |
||
68 | * If the cursor is already at the edge of the screen, this has no effect. |
||
69 | * @param int $rows number of rows the cursor should be moved down |
||
70 | */ |
||
71 | 1 | public static function moveCursorDown($rows = 1) |
|
75 | |||
76 | /** |
||
77 | * Moves the terminal cursor forward by sending ANSI control code CUF to the terminal. |
||
78 | * If the cursor is already at the edge of the screen, this has no effect. |
||
79 | * @param int $steps number of steps the cursor should be moved forward |
||
80 | */ |
||
81 | 1 | public static function moveCursorForward($steps = 1) |
|
85 | |||
86 | /** |
||
87 | * Moves the terminal cursor backward by sending ANSI control code CUB to the terminal. |
||
88 | * If the cursor is already at the edge of the screen, this has no effect. |
||
89 | * @param int $steps number of steps the cursor should be moved backward |
||
90 | */ |
||
91 | 1 | public static function moveCursorBackward($steps = 1) |
|
95 | |||
96 | /** |
||
97 | * Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal. |
||
98 | * @param int $lines number of lines the cursor should be moved down |
||
99 | */ |
||
100 | 1 | public static function moveCursorNextLine($lines = 1) |
|
104 | |||
105 | /** |
||
106 | * Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal. |
||
107 | * @param int $lines number of lines the cursor should be moved up |
||
108 | */ |
||
109 | 1 | public static function moveCursorPrevLine($lines = 1) |
|
113 | |||
114 | /** |
||
115 | * Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal. |
||
116 | * @param int $column 1-based column number, 1 is the left edge of the screen. |
||
117 | * @param int|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line. |
||
118 | */ |
||
119 | 1 | public static function moveCursorTo($column, $row = null) |
|
120 | { |
||
121 | 1 | if ($row === null) { |
|
122 | 1 | echo "\033[" . (int) $column . 'G'; |
|
123 | } else { |
||
124 | 1 | echo "\033[" . (int) $row . ';' . (int) $column . 'H'; |
|
125 | } |
||
126 | 1 | } |
|
127 | |||
128 | /** |
||
129 | * Scrolls whole page up by sending ANSI control code SU to the terminal. |
||
130 | * New lines are added at the bottom. This is not supported by ANSI.SYS used in windows. |
||
131 | * @param int $lines number of lines to scroll up |
||
132 | */ |
||
133 | 1 | public static function scrollUp($lines = 1) |
|
137 | |||
138 | /** |
||
139 | * Scrolls whole page down by sending ANSI control code SD to the terminal. |
||
140 | * New lines are added at the top. This is not supported by ANSI.SYS used in windows. |
||
141 | * @param int $lines number of lines to scroll down |
||
142 | */ |
||
143 | 1 | public static function scrollDown($lines = 1) |
|
147 | |||
148 | /** |
||
149 | * Saves the current cursor position by sending ANSI control code SCP to the terminal. |
||
150 | * Position can then be restored with [[restoreCursorPosition()]]. |
||
151 | */ |
||
152 | 1 | public static function saveCursorPosition() |
|
156 | |||
157 | /** |
||
158 | * Restores the cursor position saved with [[saveCursorPosition()]] by sending ANSI control code RCP to the terminal. |
||
159 | */ |
||
160 | 1 | public static function restoreCursorPosition() |
|
164 | |||
165 | /** |
||
166 | * Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal. |
||
167 | * Use [[showCursor()]] to bring it back. |
||
168 | * Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit. |
||
169 | */ |
||
170 | 1 | public static function hideCursor() |
|
174 | |||
175 | /** |
||
176 | * Will show a cursor again when it has been hidden by [[hideCursor()]] by sending ANSI DECTCEM code ?25h to the terminal. |
||
177 | */ |
||
178 | 1 | public static function showCursor() |
|
182 | |||
183 | /** |
||
184 | * Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal. |
||
185 | * Cursor position will not be changed. |
||
186 | * **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen. |
||
187 | */ |
||
188 | 1 | public static function clearScreen() |
|
192 | |||
193 | /** |
||
194 | * Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal. |
||
195 | * Cursor position will not be changed. |
||
196 | */ |
||
197 | 1 | public static function clearScreenBeforeCursor() |
|
201 | |||
202 | /** |
||
203 | * Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal. |
||
204 | * Cursor position will not be changed. |
||
205 | */ |
||
206 | 1 | public static function clearScreenAfterCursor() |
|
210 | |||
211 | /** |
||
212 | * Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal. |
||
213 | * Cursor position will not be changed. |
||
214 | */ |
||
215 | 1 | public static function clearLine() |
|
219 | |||
220 | /** |
||
221 | * Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal. |
||
222 | * Cursor position will not be changed. |
||
223 | */ |
||
224 | 1 | public static function clearLineBeforeCursor() |
|
228 | |||
229 | /** |
||
230 | * Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal. |
||
231 | * Cursor position will not be changed. |
||
232 | */ |
||
233 | 1 | public static function clearLineAfterCursor() |
|
237 | |||
238 | /** |
||
239 | * Returns the ANSI format code. |
||
240 | * |
||
241 | * @param array $format An array containing formatting values. |
||
242 | * You can pass any of the `FG_*`, `BG_*` and `TEXT_*` constants |
||
243 | * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. |
||
244 | * @return string The ANSI format code according to the given formatting constants. |
||
245 | */ |
||
246 | 3 | public static function ansiFormatCode($format) |
|
250 | |||
251 | /** |
||
252 | * Echoes an ANSI format code that affects the formatting of any text that is printed afterwards. |
||
253 | * |
||
254 | * @param array $format An array containing formatting values. |
||
255 | * You can pass any of the `FG_*`, `BG_*` and `TEXT_*` constants |
||
256 | * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. |
||
257 | * @see ansiFormatCode() |
||
258 | * @see endAnsiFormat() |
||
259 | */ |
||
260 | 1 | public static function beginAnsiFormat($format) |
|
264 | |||
265 | /** |
||
266 | * Resets any ANSI format set by previous method [[beginAnsiFormat()]] |
||
267 | * Any output after this will have default text format. |
||
268 | * This is equal to calling. |
||
269 | * |
||
270 | * ```php |
||
271 | * echo Console::ansiFormatCode([Console::RESET]) |
||
272 | * ``` |
||
273 | */ |
||
274 | 1 | public static function endAnsiFormat() |
|
278 | |||
279 | /** |
||
280 | * Will return a string formatted with the given ANSI style. |
||
281 | * |
||
282 | * @param string $string the string to be formatted |
||
283 | * @param array $format An array containing formatting values. |
||
284 | * You can pass any of the `FG_*`, `BG_*` and `TEXT_*` constants |
||
285 | * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format. |
||
286 | * @return string |
||
287 | */ |
||
288 | 18 | public static function ansiFormat($string, $format = []) |
|
294 | |||
295 | /** |
||
296 | * Returns the ansi format code for xterm foreground color. |
||
297 | * |
||
298 | * You can pass the return value of this to one of the formatting methods: |
||
299 | * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]. |
||
300 | * |
||
301 | * @param int $colorCode xterm color code |
||
302 | * @return string |
||
303 | * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors |
||
304 | */ |
||
305 | 1 | public static function xtermFgColor($colorCode) |
|
306 | { |
||
307 | 1 | return '38;5;' . $colorCode; |
|
308 | } |
||
309 | |||
310 | /** |
||
311 | * Returns the ansi format code for xterm background color. |
||
312 | * |
||
313 | * You can pass the return value of this to one of the formatting methods: |
||
314 | * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]. |
||
315 | * |
||
316 | * @param int $colorCode xterm color code |
||
317 | * @return string |
||
318 | * @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors |
||
319 | */ |
||
320 | 1 | public static function xtermBgColor($colorCode) |
|
321 | { |
||
322 | 1 | return '48;5;' . $colorCode; |
|
323 | } |
||
324 | |||
325 | /** |
||
326 | * Strips ANSI control codes from a string. |
||
327 | * |
||
328 | * @param string $string String to strip |
||
329 | * @return string |
||
330 | */ |
||
331 | 8 | public static function stripAnsiFormat($string) |
|
332 | { |
||
333 | 8 | return preg_replace('/\033\[[\d;?]*\w/', '', $string); |
|
334 | } |
||
335 | |||
336 | /** |
||
337 | * Returns the length of the string without ANSI color codes. |
||
338 | * @param string $string the string to measure |
||
339 | * @return int the length of the string not counting ANSI format characters |
||
340 | */ |
||
341 | public static function ansiStrlen($string) |
||
342 | { |
||
343 | return mb_strlen(static::stripAnsiFormat($string)); |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * Converts an ANSI formatted string to HTML. |
||
348 | * |
||
349 | * Note: xTerm 256 bit colors are currently not supported. |
||
350 | * |
||
351 | * @param string $string the string to convert. |
||
352 | * @param array $styleMap an optional mapping of ANSI control codes such as |
||
353 | * FG\_*COLOR* or [[BOLD]] to a set of css style definitions. |
||
354 | * The CSS style definitions are represented as an array where the array keys correspond |
||
355 | * to the css style attribute names and the values are the css values. |
||
356 | * values may be arrays that will be merged and imploded with `' '` when rendered. |
||
357 | * @return string HTML representation of the ANSI formatted string |
||
358 | */ |
||
359 | 15 | public static function ansiToHtml($string, $styleMap = []) |
|
360 | { |
||
361 | $styleMap = [ |
||
362 | // http://www.w3.org/TR/CSS2/syndata.html#value-def-color |
||
363 | 15 | self::FG_BLACK => ['color' => 'black'], |
|
364 | 15 | self::FG_BLUE => ['color' => 'blue'], |
|
365 | 15 | self::FG_CYAN => ['color' => 'aqua'], |
|
366 | 15 | self::FG_GREEN => ['color' => 'lime'], |
|
367 | 15 | self::FG_GREY => ['color' => 'silver'], |
|
368 | // http://meyerweb.com/eric/thoughts/2014/06/19/rebeccapurple/ |
||
369 | // http://dev.w3.org/csswg/css-color/#valuedef-rebeccapurple |
||
370 | 15 | self::FG_PURPLE => ['color' => 'rebeccapurple'], |
|
371 | 15 | self::FG_RED => ['color' => 'red'], |
|
372 | 15 | self::FG_YELLOW => ['color' => 'yellow'], |
|
373 | 15 | self::BG_BLACK => ['background-color' => 'black'], |
|
374 | 15 | self::BG_BLUE => ['background-color' => 'blue'], |
|
375 | 15 | self::BG_CYAN => ['background-color' => 'aqua'], |
|
376 | 15 | self::BG_GREEN => ['background-color' => 'lime'], |
|
377 | 15 | self::BG_GREY => ['background-color' => 'silver'], |
|
378 | 15 | self::BG_PURPLE => ['background-color' => 'rebeccapurple'], |
|
379 | 15 | self::BG_RED => ['background-color' => 'red'], |
|
380 | 15 | self::BG_YELLOW => ['background-color' => 'yellow'], |
|
381 | 15 | self::BOLD => ['font-weight' => 'bold'], |
|
382 | 15 | self::ITALIC => ['font-style' => 'italic'], |
|
383 | 15 | self::UNDERLINE => ['text-decoration' => ['underline']], |
|
384 | 15 | self::OVERLINED => ['text-decoration' => ['overline']], |
|
385 | 15 | self::CROSSED_OUT => ['text-decoration' => ['line-through']], |
|
386 | 15 | self::BLINK => ['text-decoration' => ['blink']], |
|
387 | 15 | self::CONCEALED => ['visibility' => 'hidden'], |
|
388 | 15 | ] + $styleMap; |
|
389 | |||
390 | 15 | $tags = 0; |
|
391 | 15 | $result = preg_replace_callback( |
|
392 | 15 | '/\033\[([\d;]+)m/', |
|
393 | 15 | function ($ansi) use (&$tags, $styleMap) { |
|
394 | 14 | $style = []; |
|
395 | 14 | $reset = false; |
|
396 | 14 | $negative = false; |
|
397 | 14 | foreach (explode(';', $ansi[1]) as $controlCode) { |
|
398 | 14 | if ($controlCode == 0) { |
|
399 | 14 | $style = []; |
|
400 | 14 | $reset = true; |
|
401 | 11 | } elseif ($controlCode == self::NEGATIVE) { |
|
402 | 2 | $negative = true; |
|
403 | 10 | } elseif (isset($styleMap[$controlCode])) { |
|
404 | 14 | $style[] = $styleMap[$controlCode]; |
|
405 | } |
||
406 | } |
||
407 | |||
408 | 14 | $return = ''; |
|
409 | 14 | while ($reset && $tags > 0) { |
|
410 | 10 | $return .= '</span>'; |
|
411 | 10 | $tags--; |
|
412 | } |
||
413 | 14 | if (empty($style)) { |
|
414 | 14 | return $return; |
|
415 | } |
||
416 | |||
417 | 10 | $currentStyle = []; |
|
418 | 10 | foreach ($style as $content) { |
|
419 | 10 | $currentStyle = ArrayHelper::merge($currentStyle, $content); |
|
420 | } |
||
421 | |||
422 | // if negative is set, invert background and foreground |
||
423 | 10 | if ($negative) { |
|
424 | 1 | if (isset($currentStyle['color'])) { |
|
425 | 1 | $fgColor = $currentStyle['color']; |
|
426 | 1 | unset($currentStyle['color']); |
|
427 | } |
||
428 | 1 | if (isset($currentStyle['background-color'])) { |
|
429 | 1 | $bgColor = $currentStyle['background-color']; |
|
430 | 1 | unset($currentStyle['background-color']); |
|
431 | } |
||
432 | 1 | if (isset($fgColor)) { |
|
433 | 1 | $currentStyle['background-color'] = $fgColor; |
|
434 | } |
||
435 | 1 | if (isset($bgColor)) { |
|
436 | 1 | $currentStyle['color'] = $bgColor; |
|
437 | } |
||
438 | } |
||
439 | |||
440 | 10 | $styleString = ''; |
|
441 | 10 | foreach ($currentStyle as $name => $value) { |
|
442 | 10 | if (is_array($value)) { |
|
443 | 1 | $value = implode(' ', $value); |
|
444 | } |
||
445 | 10 | $styleString .= "$name: $value;"; |
|
446 | } |
||
447 | 10 | $tags++; |
|
448 | 10 | return "$return<span style=\"$styleString\">"; |
|
449 | 15 | }, |
|
450 | 15 | $string |
|
451 | ); |
||
452 | 15 | while ($tags > 0) { |
|
453 | $result .= '</span>'; |
||
454 | $tags--; |
||
455 | } |
||
456 | |||
457 | 15 | return $result; |
|
458 | } |
||
459 | |||
460 | /** |
||
461 | * Converts Markdown to be better readable in console environments by applying some ANSI format. |
||
462 | * @param string $markdown the markdown string. |
||
463 | * @return string the parsed result as ANSI formatted string. |
||
464 | */ |
||
465 | 2 | public static function markdownToAnsi($markdown) |
|
466 | { |
||
467 | 2 | $parser = new ConsoleMarkdown(); |
|
468 | 2 | return $parser->parse($markdown); |
|
469 | } |
||
470 | |||
471 | /** |
||
472 | * Converts a string to ansi formatted by replacing patterns like %y (for yellow) with ansi control codes. |
||
473 | * |
||
474 | * Uses almost the same syntax as https://github.com/pear/Console_Color2/blob/master/Console/Color2.php |
||
475 | * The conversion table is: ('bold' meaning 'light' on some |
||
476 | * terminals). It's almost the same conversion table irssi uses. |
||
477 | * <pre> |
||
478 | * text text background |
||
479 | * ------------------------------------------------ |
||
480 | * %k %K %0 black dark grey black |
||
481 | * %r %R %1 red bold red red |
||
482 | * %g %G %2 green bold green green |
||
483 | * %y %Y %3 yellow bold yellow yellow |
||
484 | * %b %B %4 blue bold blue blue |
||
485 | * %m %M %5 magenta bold magenta magenta |
||
486 | * %p %P magenta (think: purple) |
||
487 | * %c %C %6 cyan bold cyan cyan |
||
488 | * %w %W %7 white bold white white |
||
489 | * |
||
490 | * %F Blinking, Flashing |
||
491 | * %U Underline |
||
492 | * %8 Reverse |
||
493 | * %_,%9 Bold |
||
494 | * |
||
495 | * %n Resets the color |
||
496 | * %% A single % |
||
497 | * </pre> |
||
498 | * First param is the string to convert, second is an optional flag if |
||
499 | * colors should be used. It defaults to true, if set to false, the |
||
500 | * color codes will just be removed (And %% will be transformed into %) |
||
501 | * |
||
502 | * @param string $string String to convert |
||
503 | * @param bool $colored Should the string be colored? |
||
504 | * @return string |
||
505 | */ |
||
506 | 3 | public static function renderColoredString($string, $colored = true) |
|
507 | { |
||
508 | // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746 |
||
509 | 3 | static $conversions = [ |
|
510 | '%y' => [self::FG_YELLOW], |
||
511 | '%g' => [self::FG_GREEN], |
||
512 | '%b' => [self::FG_BLUE], |
||
513 | '%r' => [self::FG_RED], |
||
514 | '%p' => [self::FG_PURPLE], |
||
515 | '%m' => [self::FG_PURPLE], |
||
516 | '%c' => [self::FG_CYAN], |
||
517 | '%w' => [self::FG_GREY], |
||
518 | '%k' => [self::FG_BLACK], |
||
519 | '%n' => [0], // reset |
||
520 | '%Y' => [self::FG_YELLOW, self::BOLD], |
||
521 | '%G' => [self::FG_GREEN, self::BOLD], |
||
522 | '%B' => [self::FG_BLUE, self::BOLD], |
||
523 | '%R' => [self::FG_RED, self::BOLD], |
||
524 | '%P' => [self::FG_PURPLE, self::BOLD], |
||
525 | '%M' => [self::FG_PURPLE, self::BOLD], |
||
526 | '%C' => [self::FG_CYAN, self::BOLD], |
||
527 | '%W' => [self::FG_GREY, self::BOLD], |
||
528 | '%K' => [self::FG_BLACK, self::BOLD], |
||
529 | '%N' => [0, self::BOLD], |
||
530 | '%3' => [self::BG_YELLOW], |
||
531 | '%2' => [self::BG_GREEN], |
||
532 | '%4' => [self::BG_BLUE], |
||
533 | '%1' => [self::BG_RED], |
||
534 | '%5' => [self::BG_PURPLE], |
||
535 | '%6' => [self::BG_CYAN], |
||
536 | '%7' => [self::BG_GREY], |
||
537 | '%0' => [self::BG_BLACK], |
||
538 | '%F' => [self::BLINK], |
||
539 | '%U' => [self::UNDERLINE], |
||
540 | '%8' => [self::NEGATIVE], |
||
541 | '%9' => [self::BOLD], |
||
542 | '%_' => [self::BOLD], |
||
543 | ]; |
||
544 | |||
545 | 3 | if ($colored) { |
|
546 | 3 | $string = str_replace('%%', '% ', $string); |
|
547 | 3 | foreach ($conversions as $key => $value) { |
|
548 | 3 | $string = str_replace( |
|
549 | 3 | $key, |
|
550 | 3 | static::ansiFormatCode($value), |
|
551 | 3 | $string |
|
552 | ); |
||
553 | } |
||
554 | 3 | $string = str_replace('% ', '%', $string); |
|
555 | } else { |
||
556 | 1 | $string = preg_replace('/%((%)|.)/', '$2', $string); |
|
557 | } |
||
558 | |||
559 | 3 | return $string; |
|
560 | } |
||
561 | |||
562 | /** |
||
563 | * Escapes % so they don't get interpreted as color codes when |
||
564 | * the string is parsed by [[renderColoredString]]. |
||
565 | * |
||
566 | * @param string $string String to escape |
||
567 | * |
||
568 | * @return string |
||
569 | */ |
||
570 | public static function escape($string) |
||
571 | { |
||
572 | // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746 |
||
573 | return str_replace('%', '%%', $string); |
||
574 | } |
||
575 | |||
576 | /** |
||
577 | * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream. |
||
578 | * |
||
579 | * - windows without ansicon |
||
580 | * - not tty consoles |
||
581 | * |
||
582 | * @param mixed $stream |
||
583 | * @return bool true if the stream supports ANSI colors, otherwise false. |
||
584 | */ |
||
585 | 4 | public static function streamSupportsAnsiColors($stream) |
|
586 | { |
||
587 | 4 | return DIRECTORY_SEPARATOR === '\\' |
|
588 | ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON' |
||
589 | 4 | : function_exists('posix_isatty') && @posix_isatty($stream); |
|
590 | } |
||
591 | |||
592 | /** |
||
593 | * Returns true if the console is running on windows. |
||
594 | * @return bool |
||
595 | */ |
||
596 | 1 | public static function isRunningOnWindows() |
|
597 | { |
||
598 | 1 | return DIRECTORY_SEPARATOR === '\\'; |
|
599 | } |
||
600 | |||
601 | /** |
||
602 | * Returns terminal screen size. |
||
603 | * |
||
604 | * Usage: |
||
605 | * |
||
606 | * ```php |
||
607 | * [$width, $height] = ConsoleHelper::getScreenSize(); |
||
608 | * ``` |
||
609 | * |
||
610 | * @param bool $refresh whether to force checking and not re-use cached size value. |
||
611 | * This is useful to detect changing window size while the application is running but may |
||
612 | * not get up to date values on every terminal. |
||
613 | * @return array|bool An array of ($width, $height) or false when it was not able to determine size. |
||
614 | */ |
||
615 | 1 | public static function getScreenSize($refresh = false) |
|
616 | { |
||
617 | 1 | static $size; |
|
618 | 1 | if ($size !== null && !$refresh) { |
|
619 | 1 | return $size; |
|
620 | } |
||
621 | |||
622 | 1 | if (static::isRunningOnWindows()) { |
|
623 | $output = []; |
||
624 | exec('mode con', $output); |
||
625 | if (isset($output, $output[1]) && strpos($output[1], 'CON') !== false) { |
||
626 | return $size = [(int) preg_replace('~\D~', '', $output[4]), (int) preg_replace('~\D~', '', $output[3])]; |
||
627 | } |
||
628 | } else { |
||
629 | // try stty if available |
||
630 | 1 | $stty = []; |
|
631 | 1 | if (exec('stty -a 2>&1', $stty)) { |
|
632 | 1 | $stty = implode(' ', $stty); |
|
633 | |||
634 | // Linux stty output |
||
635 | 1 | if (preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', $stty, $matches)) { |
|
636 | 1 | return $size = [(int) $matches[2], (int) $matches[1]]; |
|
637 | } |
||
638 | |||
639 | // MacOS stty output |
||
640 | if (preg_match('/(\d+)\s+rows;\s*(\d+)\s+columns;/mi', $stty, $matches)) { |
||
641 | return $size = [(int) $matches[2], (int) $matches[1]]; |
||
642 | } |
||
643 | } |
||
644 | |||
645 | // fallback to tput, which may not be updated on terminal resize |
||
646 | if (($width = (int) exec('tput cols 2>&1')) > 0 && ($height = (int) exec('tput lines 2>&1')) > 0) { |
||
647 | return $size = [$width, $height]; |
||
648 | } |
||
649 | |||
650 | // fallback to ENV variables, which may not be updated on terminal resize |
||
651 | if (($width = (int) getenv('COLUMNS')) > 0 && ($height = (int) getenv('LINES')) > 0) { |
||
652 | return $size = [$width, $height]; |
||
653 | } |
||
654 | } |
||
655 | |||
656 | return $size = false; |
||
657 | } |
||
658 | |||
659 | /** |
||
660 | * Word wrap text with indentation to fit the screen size |
||
661 | * |
||
662 | * If screen size could not be detected, or the indentation is greater than the screen size, the text will not be wrapped. |
||
663 | * |
||
664 | * The first line will **not** be indented, so `Console::wrapText("Lorem ipsum dolor sit amet.", 4)` will result in the |
||
665 | * following output, given the screen width is 16 characters: |
||
666 | * |
||
667 | * ``` |
||
668 | * Lorem ipsum |
||
669 | * dolor sit |
||
670 | * amet. |
||
671 | * ``` |
||
672 | * |
||
673 | * @param string $text the text to be wrapped |
||
674 | * @param int $indent number of spaces to use for indentation. |
||
675 | * @param bool $refresh whether to force refresh of screen size. |
||
676 | * This will be passed to [[getScreenSize()]]. |
||
677 | * @return string the wrapped text. |
||
678 | * @since 2.0.4 |
||
679 | */ |
||
680 | 1 | public static function wrapText($text, $indent = 0, $refresh = false) |
|
681 | { |
||
682 | 1 | $size = static::getScreenSize($refresh); |
|
683 | 1 | if ($size === false || $size[0] <= $indent) { |
|
684 | return $text; |
||
685 | } |
||
686 | 1 | $pad = str_repeat(' ', $indent); |
|
687 | 1 | $lines = explode("\n", wordwrap($text, $size[0] - $indent, "\n", true)); |
|
688 | 1 | $first = true; |
|
689 | 1 | foreach ($lines as $i => $line) { |
|
690 | 1 | if ($first) { |
|
691 | 1 | $first = false; |
|
692 | 1 | continue; |
|
693 | } |
||
694 | 1 | $lines[$i] = $pad . $line; |
|
695 | } |
||
696 | 1 | return implode("\n", $lines); |
|
697 | } |
||
698 | |||
699 | /** |
||
700 | * Gets input from STDIN and returns a string right-trimmed for EOLs. |
||
701 | * |
||
702 | * @param bool $raw If set to true, returns the raw string without trimming |
||
703 | * @return string the string read from stdin |
||
704 | */ |
||
705 | public static function stdin($raw = false) |
||
709 | |||
710 | /** |
||
711 | * Prints a string to STDOUT. |
||
712 | * |
||
713 | * @param string $string the string to print |
||
714 | * @return int|bool Number of bytes printed or false on error |
||
715 | */ |
||
716 | public static function stdout($string) |
||
720 | |||
721 | /** |
||
722 | * Prints a string to STDERR. |
||
723 | * |
||
724 | * @param string $string the string to print |
||
725 | * @return int|bool Number of bytes printed or false on error |
||
726 | */ |
||
727 | public static function stderr($string) |
||
731 | |||
732 | /** |
||
733 | * Asks the user for input. Ends when the user types a carriage return (PHP_EOL). Optionally, It also provides a |
||
734 | * prompt. |
||
735 | * |
||
736 | * @param string $prompt the prompt to display before waiting for input (optional) |
||
737 | * @return string the user's input |
||
738 | */ |
||
739 | public static function input($prompt = null) |
||
747 | |||
748 | /** |
||
749 | * Prints text to STDOUT appended with a carriage return (PHP_EOL). |
||
750 | * |
||
751 | * @param string $string the text to print |
||
752 | * @return int|bool number of bytes printed or false on error. |
||
753 | */ |
||
754 | public static function output($string = null) |
||
758 | |||
759 | /** |
||
760 | * Prints text to STDERR appended with a carriage return (PHP_EOL). |
||
761 | * |
||
762 | * @param string $string the text to print |
||
763 | * @return int|bool number of bytes printed or false on error. |
||
764 | */ |
||
765 | public static function error($string = null) |
||
769 | |||
770 | /** |
||
771 | * Prompts the user for input and validates it |
||
772 | * |
||
773 | * @param string $text prompt string |
||
774 | * @param array $options the options to validate the input: |
||
775 | * |
||
776 | * - `required`: whether it is required or not |
||
777 | * - `default`: default value if no input is inserted by the user |
||
778 | * - `pattern`: regular expression pattern to validate user input |
||
779 | * - `validator`: a callable function to validate input. The function must accept two parameters: |
||
780 | * - `input`: the user input to validate |
||
781 | * - `error`: the error value passed by reference if validation failed. |
||
782 | * |
||
783 | * @return string the user input |
||
784 | */ |
||
785 | public static function prompt($text, $options = []) |
||
823 | |||
824 | /** |
||
825 | * Asks user to confirm by typing y or n. |
||
826 | * |
||
827 | * A typical usage looks like the following: |
||
828 | * |
||
829 | * ```php |
||
830 | * if (Console::confirm("Are you sure?")) { |
||
831 | * echo "user typed yes\n"; |
||
832 | * } else { |
||
833 | * echo "user typed no\n"; |
||
834 | * } |
||
835 | * ``` |
||
836 | * |
||
837 | * @param string $message to print out before waiting for user input |
||
838 | * @param bool $default this value is returned if no selection is made. |
||
839 | * @return bool whether user confirmed |
||
840 | */ |
||
841 | public static function confirm($message, $default = false) |
||
860 | |||
861 | /** |
||
862 | * Gives the user an option to choose from. Giving '?' as an input will show |
||
863 | * a list of options to choose from and their explanations. |
||
864 | * |
||
865 | * @param string $prompt the prompt message |
||
866 | * @param array $options Key-value array of options to choose from |
||
867 | * |
||
868 | * @return string An option character the user chose |
||
869 | */ |
||
870 | public static function select($prompt, $options = []) |
||
887 | |||
888 | private static $_progressStart; |
||
889 | private static $_progressWidth; |
||
890 | private static $_progressPrefix; |
||
891 | private static $_progressEta; |
||
892 | private static $_progressEtaLastDone = 0; |
||
893 | private static $_progressEtaLastUpdate; |
||
894 | |||
895 | /** |
||
896 | * Starts display of a progress bar on screen. |
||
897 | * |
||
898 | * This bar will be updated by [[updateProgress()]] and my be ended by [[endProgress()]]. |
||
899 | * |
||
900 | * The following example shows a simple usage of a progress bar: |
||
901 | * |
||
902 | * ```php |
||
903 | * Console::startProgress(0, 1000); |
||
904 | * for ($n = 1; $n <= 1000; $n++) { |
||
905 | * usleep(1000); |
||
906 | * Console::updateProgress($n, 1000); |
||
907 | * } |
||
908 | * Console::endProgress(); |
||
909 | * ``` |
||
910 | * |
||
911 | * Git clone like progress (showing only status information): |
||
912 | * ```php |
||
913 | * Console::startProgress(0, 1000, 'Counting objects: ', false); |
||
914 | * for ($n = 1; $n <= 1000; $n++) { |
||
915 | * usleep(1000); |
||
916 | * Console::updateProgress($n, 1000); |
||
917 | * } |
||
918 | * Console::endProgress("done." . PHP_EOL); |
||
919 | * ``` |
||
920 | * |
||
921 | * @param int $done the number of items that are completed. |
||
922 | * @param int $total the total value of items that are to be done. |
||
923 | * @param string $prefix an optional string to display before the progress bar. |
||
924 | * Default to empty string which results in no prefix to be displayed. |
||
925 | * @param int|bool $width optional width of the progressbar. This can be an integer representing |
||
926 | * the number of characters to display for the progress bar or a float between 0 and 1 representing the |
||
927 | * percentage of screen with the progress bar may take. It can also be set to false to disable the |
||
928 | * bar and only show progress information like percent, number of items and ETA. |
||
929 | * If not set, the bar will be as wide as the screen. Screen size will be detected using [[getScreenSize()]]. |
||
930 | * @see startProgress |
||
931 | * @see updateProgress |
||
932 | * @see endProgress |
||
933 | */ |
||
934 | public static function startProgress($done, $total, $prefix = '', $width = null) |
||
945 | |||
946 | /** |
||
947 | * Updates a progress bar that has been started by [[startProgress()]]. |
||
948 | * |
||
949 | * @param int $done the number of items that are completed. |
||
950 | * @param int $total the total value of items that are to be done. |
||
951 | * @param string $prefix an optional string to display before the progress bar. |
||
952 | * Defaults to null meaning the prefix specified by [[startProgress()]] will be used. |
||
953 | * If prefix is specified it will update the prefix that will be used by later calls. |
||
954 | * @see startProgress |
||
955 | * @see endProgress |
||
956 | */ |
||
957 | public static function updateProgress($done, $total, $prefix = null) |
||
993 | |||
994 | /** |
||
995 | * Return width of the progressbar |
||
996 | * @param string $prefix an optional string to display before the progress bar. |
||
997 | * @see updateProgress |
||
998 | * @return int screen width |
||
999 | * @since 2.0.14 |
||
1000 | */ |
||
1001 | private static function getProgressbarWidth($prefix) |
||
1024 | |||
1025 | /** |
||
1026 | * Calculate $_progressEta, $_progressEtaLastUpdate and $_progressEtaLastDone |
||
1027 | * @param int $done the number of items that are completed. |
||
1028 | * @param int $total the total value of items that are to be done. |
||
1029 | * @see updateProgress |
||
1030 | * @since 2.0.14 |
||
1031 | */ |
||
1032 | private static function setETA($done, $total) |
||
1047 | |||
1048 | /** |
||
1049 | * Ends a progress bar that has been started by [[startProgress()]]. |
||
1050 | * |
||
1051 | * @param string|bool $remove This can be `false` to leave the progress bar on screen and just print a newline. |
||
1052 | * If set to `true`, the line of the progress bar will be cleared. This may also be a string to be displayed instead |
||
1053 | * of the progress bar. |
||
1054 | * @param bool $keepPrefix whether to keep the prefix that has been specified for the progressbar when progressbar |
||
1055 | * gets removed. Defaults to true. |
||
1056 | * @see startProgress |
||
1057 | * @see updateProgress |
||
1058 | */ |
||
1059 | public static function endProgress($remove = false, $keepPrefix = true) |
||
1078 | |||
1079 | /** |
||
1080 | * Generates a summary of the validation errors. |
||
1081 | * @param Model|Model[] $models the model(s) whose validation errors are to be displayed. |
||
1082 | * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: |
||
1083 | * |
||
1084 | * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise |
||
1085 | * only the first error message for each attribute will be shown. Defaults to `false`. |
||
1086 | * |
||
1087 | * @return string the generated error summary |
||
1088 | * @since 2.0.14 |
||
1089 | */ |
||
1090 | 1 | public static function errorSummary($models, $options = []) |
|
1097 | |||
1098 | /** |
||
1099 | * Return array of the validation errors |
||
1100 | * @param Model|Model[] $models the model(s) whose validation errors are to be displayed. |
||
1101 | * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise |
||
1102 | * only the first error message for each attribute will be shown. |
||
1103 | * @return array of the validation errors |
||
1104 | * @since 2.0.14 |
||
1105 | */ |
||
1106 | 1 | private static function collectErrors($models, $showAllErrors) |
|
1119 | } |
||
1120 |
Let’s assume you have a class which uses late-static binding:
}
The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the
getSomeVariable()
on that sub-class, you will receive a runtime error:In the case above, it makes sense to update
SomeClass
to useself
instead: