@@ -20,35 +20,62 @@ class Cli
2020 const FG_BLACK = 30 ;
2121 const FG_RED = 31 ;
2222 const FG_GREEN = 32 ;
23- const FG_BROWN = 33 ;
23+ const FG_BROWN = 33 ; // like yellow
2424 const FG_BLUE = 34 ;
2525 const FG_CYAN = 36 ;
2626 const FG_WHITE = 37 ;
2727 const FG_DEFAULT = 39 ;
2828
29+ // extra Foreground color
30+ const FG_DARK_GRAY = 90 ;
31+ const FG_LIGHT_RED = 91 ;
32+ const FG_LIGHT_GREEN = 92 ;
33+ const FG_LIGHT_YELLOW = 93 ;
34+ const FG_LIGHT_BLUE = 94 ;
35+ const FG_LIGHT_MAGENTA = 95 ;
36+ const FG_LIGHT_CYAN = 96 ;
37+ const FG_WHITE_W = 97 ;
38+
2939 // Background color
3040 const BG_BLACK = 40 ;
3141 const BG_RED = 41 ;
3242 const BG_GREEN = 42 ;
33- const BG_BROWN = 43 ;
43+ const BG_BROWN = 43 ; // like yellow
3444 const BG_BLUE = 44 ;
3545 const BG_CYAN = 46 ;
3646 const BG_WHITE = 47 ;
3747 const BG_DEFAULT = 49 ;
3848
49+ // extra Background color
50+ const BG_DARK_GRAY = 100 ;
51+ const BG_LIGHT_RED = 101 ;
52+ const BG_LIGHT_GREEN = 102 ;
53+ const BG_LIGHT_YELLOW = 103 ;
54+ const BG_LIGHT_BLUE = 104 ;
55+ const BG_LIGHT_MAGENTA = 105 ;
56+ const BG_LIGHT_CYAN = 106 ;
57+ const BG_WHITE_W = 107 ;
58+
3959 // color option
4060 const BOLD = 1 ; // 加粗
4161 const FUZZY = 2 ; // 模糊(不是所有的终端仿真器都支持)
4262 const ITALIC = 3 ; // 斜体(不是所有的终端仿真器都支持)
43- const UNDERSCORE = 4 ; // 下划线
63+ const UNDERSCORE = 4 ; // 下划线
4464 const BLINK = 5 ; // 闪烁
45- const REVERSE = 7 ; // 颠倒的 交换背景色与前景色
65+ const REVERSE = 7 ; // 颠倒的 交换背景色与前景色
66+ const CONCEALED = 8 ; // 隐匿的
67+
68+ /**
69+ * Regex to match tags
70+ * @var string
71+ */
72+ const COLOR_TAG = '/<([a-z=;]+)>(.*?)<\/ \\1>/s ' ;
4673
4774 /**
4875 * some styles
4976 * @var array
5077 */
51- public static $ styles = [
78+ const STYLES = [
5279 'light_red ' => '1;31 ' ,
5380 'light_green ' => '1;32 ' ,
5481 'yellow ' => '1;33 ' ,
@@ -62,36 +89,84 @@ class Cli
6289 'brown ' => '0;33 ' ,
6390 'blue ' => '0;34 ' ,
6491 'cyan ' => '0;36 ' ,
92+
6593 'bold ' => '1 ' ,
6694 'underscore ' => '4 ' ,
6795 'reverse ' => '7 ' ,
96+
97+ //
98+ 'suc ' => '1;32 ' ,// same 'green' and 'bold'
99+ 'success ' => '1;32 ' ,
100+ 'info ' => '0;32 ' ,// same 'green'
101+ 'comment ' => '0;33 ' ,// same 'brown'
102+ 'warning ' => '0;30;43 ' ,
103+ 'danger ' => '0;31 ' ,// same 'red'
104+ 'error ' => '30;41 ' ,
68105 ];
69106
107+ /*******************************************************************************
108+ * color render
109+ ******************************************************************************/
110+
70111 /**
71112 * @param $text
72113 * @param string|int|array $style
73114 * @return string
74115 */
75- public static function color ($ text , $ style = self :: NORMAL )
116+ public static function color ($ text , $ style = null )
76117 {
77- if (!self :: isSupportColor () ) {
118+ if (!$ text ) {
78119 return $ text ;
79120 }
80121
122+ if (!self ::isSupportColor ()) {
123+ return self ::clearColor ($ text );
124+ }
125+
81126 if (is_string ($ style )) {
82- $ out = self ::$ styles [$ style ] ?? '0 ' ;
127+ $ color = self ::STYLES [$ style ] ?? '0 ' ;
83128 } elseif (is_int ($ style )) {
84- $ out = $ style ;
129+ $ color = $ style ;
85130
86131 // array: [self::FG_GREEN, self::BG_WHITE, self::UNDERSCORE]
87132 } elseif (is_array ($ style )) {
88- $ out = implode ('; ' , $ style );
133+ $ color = implode ('; ' , $ style );
134+ } elseif (strpos ($ text , '< ' ) !== false ) {
135+ return self ::renderColor ($ text );
89136 } else {
90- $ out = self ::NORMAL ;
137+ return $ text ;
138+ }
139+
140+ // $result = chr(27). "$color{$text}" . chr(27) . chr(27) . "[0m". chr(27);
141+ return "\033[ {$ color }m {$ text }\033[0m " ;
142+ }
143+
144+ public static function renderColor ($ text )
145+ {
146+ if (!$ text || false === strpos ($ text , '< ' )) {
147+ return $ text ;
148+ }
149+
150+ // if don't support output color text, clear color tag.
151+ if (!EnvHelper::isSupportColor ()) {
152+ return static ::clearColor ($ text );
153+ }
154+
155+ if (!preg_match_all (self ::COLOR_TAG , $ text , $ matches )) {
156+ return $ text ;
157+ }
158+
159+ foreach ((array )$ matches [0 ] as $ i => $ m ) {
160+ if ($ style = self ::STYLES [$ matches [1 ][$ i ]] ?? null ) {
161+ $ tag = $ matches [1 ][$ i ];
162+ $ match = $ matches [2 ][$ i ];
163+
164+ $ replace = sprintf ("\033[%sm%s \033[0m " , $ style , $ match );
165+ $ text = str_replace ("< $ tag> $ match</ $ tag> " , $ replace , $ text );
166+ }
91167 }
92168
93- // $result = chr(27). "$out{$text}" . chr(27) . chr(27) . "[0m". chr(27);
94- return "\033[ {$ out }m {$ text }\033[0m " ;
169+ return $ text ;
95170 }
96171
97172 /**
@@ -101,41 +176,85 @@ public static function color($text, $style = self::NORMAL)
101176 public static function clearColor ($ text )
102177 {
103178 // return preg_replace('/\033\[(?:\d;?)+m/', '' , "\033[0;36mtext\033[0m");
104- return preg_replace ('/\033\[(?:\d;?)+m/ ' , '' , $ text );
179+ return preg_replace ('/\033\[(?:\d;?)+m/ ' , '' , strip_tags ( $ text) );
105180 }
106181
182+ /*******************************************************************************
183+ * read/write message
184+ ******************************************************************************/
185+
107186 /**
108- * Returns true if STDOUT supports colorization.
109- * This code has been copied and adapted from
110- * \Symfony\Component\Console\Output\OutputStream.
111- * @return boolean
187+ * @param mixed $message
188+ * @param bool $nl
189+ * @return string
112190 */
113- public static function isSupportColor ()
191+ public static function read ( $ message = null , $ nl = false ): string
114192 {
115- if (DIRECTORY_SEPARATOR === '\\' ) {
116- return
117- '10.0.10586 ' === PHP_WINDOWS_VERSION_MAJOR . '. ' . PHP_WINDOWS_VERSION_MINOR . '. ' . PHP_WINDOWS_VERSION_BUILD
118- || false !== getenv ('ANSICON ' )
119- || 'ON ' === getenv ('ConEmuANSI ' )
120- || 'xterm ' === getenv ('TERM ' )// || 'cygwin' === getenv('TERM')
121- ;
193+ if ($ message ) {
194+ self ::write ($ message , $ nl );
122195 }
123196
124- if (!defined ('STDOUT ' )) {
125- return false ;
197+ return trim (fgets (STDIN ));
198+ }
199+
200+ /**
201+ * write message to console
202+ * @param $message
203+ * @param bool $nl
204+ * @param bool $quit
205+ */
206+ public static function write ($ message , $ nl = true , $ quit = false )
207+ {
208+ if (is_array ($ message )) {
209+ $ message = implode ($ nl ? PHP_EOL : '' , $ message );
126210 }
127211
128- return self ::isInteractive ( STDOUT );
212+ self ::stdout ( self :: renderColor ( $ message ), $ nl , $ quit );
129213 }
130214
131215 /**
132- * Returns if the file descriptor is an interactive terminal or not.
133- * @param int|resource $fileDescriptor
216+ * Logs data to stdout
217+ * @param string $message
218+ * @param bool $nl
219+ * @param bool|int $quit
220+ */
221+ public static function stdout ($ message , $ nl = true , $ quit = false )
222+ {
223+ fwrite (\STDOUT , $ message . ($ nl ? PHP_EOL : '' ));
224+ fflush (\STDOUT );
225+
226+ if (($ isTrue = true === $ quit ) || is_int ($ quit )) {
227+ $ code = $ isTrue ? 0 : $ quit ;
228+ exit ($ code );
229+ }
230+ }
231+
232+ /**
233+ * Logs data to stderr
234+ * @param string $message
235+ * @param bool $nl
236+ * @param bool|int $quit
237+ */
238+ public static function stderr ($ message , $ nl = true , $ quit = -200 )
239+ {
240+ fwrite (\STDERR , self ::color ('[ERROR] ' , 'red ' ) . $ message . ($ nl ? PHP_EOL : '' ));
241+ fflush (\STDOUT );
242+
243+ if (($ isTrue = true === $ quit ) || is_int ($ quit )) {
244+ $ code = $ isTrue ? 0 : $ quit ;
245+ exit ($ code );
246+ }
247+ }
248+
249+ /**
250+ * Returns true if STDOUT supports colorization.
251+ * This code has been copied and adapted from
252+ * \Symfony\Component\Console\Output\OutputStream.
134253 * @return boolean
135254 */
136- public static function isInteractive ( $ fileDescriptor )
255+ public static function isSupportColor ( )
137256 {
138- return function_exists ( ' posix_isatty ' ) && @ posix_isatty ( $ fileDescriptor );
257+ return EnvHelper:: isSupportColor ( );
139258 }
140259
141260 /**
@@ -152,7 +271,7 @@ public static function isInteractive($fileDescriptor)
152271 * @param bool $mergeOpts
153272 * @return array
154273 */
155- public static function parseOptArgs (array $ noValues = [], $ mergeOpts = false )
274+ public static function argOptParse (array $ noValues = [], $ mergeOpts = false )
156275 {
157276 $ params = $ GLOBALS ['argv ' ];
158277 reset ($ params );
@@ -164,6 +283,8 @@ public static function parseOptArgs(array $noValues = [], $mergeOpts = false)
164283 // each() will deprecated at 7.2 so,there use current and next instead it.
165284 // while (list(,$p) = each($params)) {
166285 while (false !== ($ p = current ($ params ))) {
286+ next ($ params );
287+
167288 // is options
168289 if ($ p {0 } === '- ' ) {
169290 $ isLong = false ;
@@ -228,35 +349,4 @@ public static function parseOptArgs(array $noValues = [], $mergeOpts = false)
228349 return [$ fullScript , $ script , $ args , $ sOpts , $ lOpts ];
229350 }
230351
231- /**
232- * Logs data to stdout
233- * @param string $logString
234- * @param bool $nl
235- * @param bool|int $quit
236- */
237- public static function stdout ($ logString , $ nl = true , $ quit = false )
238- {
239- fwrite (\STDOUT , $ logString . ($ nl ? PHP_EOL : '' ));
240-
241- if (($ isTrue = true === $ quit ) || is_int ($ quit )) {
242- $ code = $ isTrue ? 0 : $ quit ;
243- exit ($ code );
244- }
245- }
246-
247- /**
248- * Logs data to stderr
249- * @param string $text
250- * @param bool $nl
251- * @param bool|int $quit
252- */
253- public static function stderr ($ text , $ nl = true , $ quit = -200 )
254- {
255- fwrite (\STDERR , self ::color ('[ERROR] ' , 'red ' ) . $ text . ($ nl ? PHP_EOL : '' ));
256-
257- if (($ isTrue = true === $ quit ) || is_int ($ quit )) {
258- $ code = $ isTrue ? 0 : $ quit ;
259- exit ($ code );
260- }
261- }
262352}
0 commit comments