@@ -24,71 +24,97 @@ class Interact
24
24
const NL_TAB = "\n " ;// new line + tab
25
25
26
26
/////////////////////////////////////////////////////////////////
27
- /// Interactive
27
+ /// Interactive method (select/confirm/question/loopAsk)
28
28
/////////////////////////////////////////////////////////////////
29
29
30
30
/**
31
31
* 多选一
32
- * @param string $question 说明
33
- * @param mixed $option 选项数据
34
- * @param bool $allowExit 有退出选项 默认 true
32
+ * @param string $description 说明
33
+ * @param mixed $options 选项数据
34
+ * e.g
35
+ * [
36
+ * // option => value
37
+ * '1' => 'chengdu',
38
+ * '2' => 'beijing'
39
+ * ]
40
+ * @param mixed $default 默认选项
41
+ * @param bool $allowExit 有退出选项 默认 true
35
42
* @return string
36
43
*/
37
- public static function choice ( $ question , $ option , $ allowExit =true )
44
+ public static function select ( $ description , $ options , $ default = null , $ allowExit =true )
38
45
{
39
- self ::write (" <comment> $ question</comment> " );
46
+ self ::choice ($ description , $ options , $ default , $ allowExit );
47
+ }
48
+ public static function choice ($ description , $ options , $ default = null , $ allowExit =true )
49
+ {
50
+ if ( !$ description = trim ($ description ) ) {
51
+ self ::error ('Please provide a description text! ' , 1 );
52
+ }
53
+
54
+ self ::write (" <comment> $ description</comment> " );
40
55
41
- $ option = is_array ($ option ) ? $ option : explode (', ' , $ option );
42
- // no set key
43
- $ isNumeric = isset ($ option [0 ]);
44
56
$ keys = [];
45
57
$ optStr = '' ;
58
+ $ options = is_array ($ options ) ? $ options : explode (', ' , $ options );
46
59
47
- foreach ($ option as $ key => $ value ) {
48
- $ keys [] = $ isNumeric ? ++$ key : $ key ;
60
+ // If defaut option is error
61
+ if ( null === $ default && !isset ($ options [$ default ]) ) {
62
+ self ::error ("The default option [ {$ default }] don't exists. " , true );
63
+ }
49
64
50
- $ optStr .= "\n $ key) $ value " ;
65
+ foreach ($ options as $ key => $ value ) {
66
+ $ keys [] = $ key ;
67
+ $ optStr .= "\n <info> $ key</info>) $ value " ;
51
68
}
52
69
53
70
if ($ allowExit ) {
54
71
$ keys [] = 'q ' ;
55
72
$ optStr .= "\n q) quit " ;
56
73
}
57
74
58
- self ::write ($ optStr . "\n You choice : " );
59
-
60
- $ r = self ::readRow ();
75
+ $ r = self ::read ($ optStr . "\n You choice : " );
61
76
77
+ // error, allow try again
62
78
if ( !in_array ($ r , $ keys ) ) {
63
- self ::write ("Warning! option <comment> $ r<comment>) don't exists! please entry again! : " );
79
+ $ r = self ::read ("Warning! Option <info> $ r</info>) don't exists! Please entry again! : " );
80
+ }
64
81
65
- $ r = self ::readRow ();
82
+ // exit
83
+ if ( $ r === 'q ' ) {
84
+ self ::write ("\n Quit,ByeBye. " , true , true );
66
85
}
67
86
68
- if ($ r === 'q ' || !in_array ($ r , $ keys ) ) {
69
- exit ("\n\n Quit,ByeBye. \n" );
87
+ // error
88
+ if ( !in_array ($ r , $ keys ) ) {
89
+ if ( null === $ default ) {
90
+ self ::write ("\n Select error. Quit,ByeBye. " , true , true );
91
+ }
92
+
93
+ $ r = $ default ;
70
94
}
71
95
72
96
return $ r ;
73
97
}
74
98
75
99
/**
76
- * 确认, 发出信息要求确认;返回 true | false
77
- * @param string $question 发出的信息
78
- * @param bool $yes
100
+ * 确认, 发出信息要求确认
101
+ * @param string $question 发出的信息
102
+ * @param bool $default Default value
79
103
* @return bool
80
104
*/
81
- public static function confirm ($ question , $ yes = true )
105
+ public static function confirm ($ question , $ default = true )
82
106
{
83
- $ question = ucfirst (trim ($ question ));
84
- $ defaultText = $ yes ? 'yes ' : 'no ' ;
107
+ if ( !$ question = trim ($ question ) ) {
108
+ self ::error ('Please provide a question text! ' , 1 );
109
+ }
85
110
86
- $ message = " <comment> $ question</comment> \n Please confirm (yes|no) [default:<info> $ defaultText </info>]: " ;
87
- self :: write ( $ message , false ) ;
111
+ $ question = ucfirst ( $ question) ;
112
+ $ defaultText = ( bool ) $ default ? ' yes ' : ' no ' ;
88
113
89
- $ answer = self ::readRow ();
114
+ $ message = " <comment> $ question ?</comment> \n Please confirm (yes|no) [default:<info> $ defaultText</info>]: " ;
115
+ $ answer = self ::read ($ message );
90
116
91
- return $ answer ? !strncasecmp ($ answer , 'y ' , 1 ) : (bool )$ yes ;
117
+ return $ answer ? !strncasecmp ($ answer , 'y ' , 1 ) : (bool )$ default ;
92
118
}
93
119
94
120
/**
@@ -231,7 +257,7 @@ public static function loopAsk($question, $default = null, \Closure $validator =
231
257
}
232
258
233
259
/////////////////////////////////////////////////////////////////
234
- /// Output Message
260
+ /// Output Format Message(title/section/helpPanel/panel/table)
235
261
/////////////////////////////////////////////////////////////////
236
262
237
263
/**
@@ -260,31 +286,140 @@ public static function section($msg, $width = 50, $char = '-')
260
286
}
261
287
262
288
/**
263
- * 多行信息展示
289
+ * Show console help message
290
+ * @param string $usage The usage message text. e.g 'command [options] [arguments]'
291
+ * @param array $commands The command list
292
+ * e.g
293
+ * [
294
+ * // command => description
295
+ * 'start' => 'Start the app server',
296
+ * ... ...
297
+ * ]
298
+ * @param array $options The option list
299
+ * e.g
300
+ * [
301
+ * // option => description
302
+ * '-d' => 'Run the server on daemonize.(default: <comment>false</comment>)',
303
+ * '-h, --help' => 'Display this help message'
304
+ * ... ...
305
+ * ]
306
+ * @param array $examples The command usage example. e.g 'php server.php {start|reload|restart|stop} [-d]'
307
+ * @param string $description The description text. e.g 'Composer version 1.3.2'
308
+ */
309
+ public static function consoleHelp ($ usage , $ commands = [], $ options = [], $ examples = [], $ description = '' )
310
+ {
311
+ self ::helpPanel ($ usage , $ commands , $ options , $ examples , $ description );
312
+ }
313
+ public static function helpPanel ($ usage , $ commands = [], $ options = [], $ examples = [], $ description = '' )
314
+ {
315
+ // usage
316
+ self ::write ("<comment>Usage</comment>: \n {$ usage }\n" );
317
+
318
+ // options list
319
+ if ( $ options ) {
320
+ // translate array to string
321
+ if ( is_array ($ options )) {
322
+ $ optionMaxWidth = ConsoleHelper::keyMaxWidth ($ options );
323
+ $ options = ConsoleHelper::spliceKeyValue ($ options , $ optionMaxWidth );
324
+ }
325
+
326
+ if ( is_string ($ options ) ) {
327
+ self ::write ("<comment>Options</comment>: \n {$ options }\n" );
328
+ }
329
+ }
330
+
331
+ // command list
332
+ if ( $ commands ) {
333
+ // translate array to string
334
+ if ( is_array ($ commands )) {
335
+ $ commandMaxWidth = ConsoleHelper::keyMaxWidth ($ commands );
336
+ $ commands = ConsoleHelper::spliceKeyValue ($ commands , $ commandMaxWidth );
337
+ }
338
+
339
+ if ( is_string ($ commands ) ) {
340
+ self ::write ("<comment>Commands</comment>: \n {$ commands }\n" );
341
+ }
342
+ }
343
+
344
+ // examples list
345
+ if ( $ examples ) {
346
+ $ examples = is_array ($ examples ) ? implode (PHP_EOL , $ examples ) : $ examples ;
347
+ self ::write ("<comment>Examples</comment>: \n {$ examples }\n" );
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Show information data panel
264
353
* @param mixed $data
265
354
* @param string $title
266
355
* @return void
267
356
*/
268
- public static function panel (array $ data , $ title ='Info panel ' )
357
+ public static function panel ($ data , $ title ='Info panel ' , $ char = ' * ' )
269
358
{
270
359
$ data = is_array ($ data ) ? array_filter ($ data ) : [trim ($ data )];
271
- $ title = ucwords (trim ($ title ));
360
+ $ title = trim ($ title );
361
+
362
+ $ panelData = []; // [ 'label' => 'value' ]
363
+ $ labelMaxWidth = 0 ; // if label exists, label max width
364
+ $ valueMaxWidth = 0 ; // value max width
272
365
273
366
self ::write ("\n " . sprintf (self ::STAR_LINE ,"<bold> $ title</bold> " ), false );
274
367
275
368
foreach ($ data as $ label => $ value ) {
276
- $ line = ' * ' ;
369
+ // label exists
370
+ if ( !is_numeric ($ label ) ) {
371
+ $ width = mb_strlen ($ label , 'UTF-8 ' );
372
+ $ labelMaxWidth = $ width > $ labelMaxWidth ? $ width : $ labelMaxWidth ;
373
+ }
374
+
375
+ // translate array to string
376
+ if ( is_array ($ value ) ) {
377
+ $ temp = '' ;
277
378
278
- if (!is_numeric ($ label )) {
279
- $ line .= "$ label: " ;
379
+ foreach ($ value as $ key => $ val ) {
380
+ $ val = (string )$ val ;
381
+ $ temp .= (!is_numeric ($ key ) ? "$ key: " : '' ) . "<info> $ value</info>, " ;
382
+ }
383
+
384
+ $ value = rtrim ($ temp , ' , ' );
280
385
}
281
386
282
- self ::write ("$ line <info> $ value</info> " );
387
+ // get value width
388
+ if ( is_string ($ value ) || is_numeric ($ value ) ) {
389
+ $ value = trim ($ value );
390
+ $ width = mb_strlen (strip_tags ($ value ), 'UTF-8 ' ); // must clear style tag
391
+ $ valueMaxWidth = $ width > $ valueMaxWidth ? $ width : $ valueMaxWidth ;
392
+ } else {
393
+ throw new \Exception ('Panel data value only allow [array|string|number] ' );
394
+ }
395
+
396
+ $ panelData [$ label ] = $ value ;
397
+ }
398
+
399
+ $ panelWidth = $ labelMaxWidth + $ valueMaxWidth ;
400
+
401
+ // output title
402
+ if ($ title ) {
403
+ $ title = ucwords ($ title );
404
+ $ titleLength = mb_strlen ($ title , 'UTF-8 ' );
405
+ $ indentSpace = str_pad (' ' , ceil ($ panelWidth /2 ) - ceil ($ titleLength /2 ) + 2 *2 , ' ' );
406
+ self ::write (" {$ indentSpace }<bold> {$ title }</bold> " );
283
407
}
284
408
285
- $ star = $ title ? substr (self ::STAR_LINE , 0 , strlen ($ title )): '' ;
409
+ // output panel top border
410
+ $ border = str_pad ($ char , $ panelWidth + (2 *3 ), $ char );
411
+ self ::write (' ' . $ border );
412
+
413
+ // output panel body
414
+ $ panelStr = ConsoleHelper::spliceKeyValue ($ panelData , $ labelMaxWidth , ' | ' , " $ char " );
415
+
416
+ // already exists "\n"
417
+ self ::write ($ panelStr , false );
418
+
419
+ // output panel bottom border
420
+ self ::write (" $ border \n" );
286
421
287
- self :: write ( ' ' . sprintf ( self :: STAR_LINE , $ star ) );
422
+ unset( $ data , $ panelData );
288
423
}
289
424
290
425
/**
0 commit comments