@@ -29,11 +29,11 @@ static function _call( $func, $args ) {
2929 *
3030 * @return bool
3131 */
32- static public function isTty () {
33- if ( function_exists ('stream_isatty ' ) ) {
34- return stream_isatty (static ::$ out );
32+ public static function isTty () {
33+ if ( function_exists ( 'stream_isatty ' ) ) {
34+ return stream_isatty ( static ::$ out );
3535 } else {
36- return (function_exists ('posix_isatty ' ) && posix_isatty (static ::$ out) );
36+ return ( function_exists ( 'posix_isatty ' ) && posix_isatty ( static ::$ out ) );
3737 }
3838 }
3939
@@ -48,31 +48,32 @@ static public function isTty() {
4848 * @return string The rendered string.
4949 */
5050 public static function render ( $ msg , ...$ args ) {
51- $ args = func_get_args ();
52-
5351 // No string replacement is needed
54- if ( count ( $ args ) == 1 || ( is_string ( $ args [1 ] ) && '' === $ args [1 ] ) ) {
52+ if ( empty ( $ args ) || ( is_string ( $ args [0 ] ) && '' === $ args [0 ] ) ) {
5553 return Colors::shouldColorize () ? Colors::colorize ( $ msg ) : $ msg ;
5654 }
5755
5856 // If the first argument is not an array just pass to sprintf
59- if ( !is_array ( $ args [1 ] ) ) {
57+ if ( ! is_array ( $ args [0 ] ) ) {
6058 // Normalize color tokens before sprintf: colorize or strip them so no raw %tokens reach sprintf
6159 if ( Colors::shouldColorize () ) {
62- $ args [ 0 ] = Colors::colorize ( $ args [ 0 ] );
60+ $ msg = Colors::colorize ( $ msg );
6361 } else {
64- $ args [ 0 ] = Colors::decolorize ( $ args [ 0 ] );
62+ $ msg = Colors::decolorize ( $ msg );
6563 }
6664
6765 // Escape percent characters for sprintf
68- $ args [ 0 ] = preg_replace ('/(%([^\w]|$))/ ' , " %$1 " , $ args [ 0 ] );
66+ $ msg = ( string ) preg_replace ( '/(%([^\w]|$))/ ' , ' %$1 ' , $ msg );
6967
70- return call_user_func_array ( 'sprintf ' , $ args );
68+ $ sprintf_args = array_merge ( array ( $ msg ), $ args );
69+ /** @var string $rendered */
70+ $ rendered = call_user_func_array ( 'sprintf ' , $ sprintf_args );
71+ return $ rendered ;
7172 }
7273
7374 // Here we do named replacement so formatting strings are more understandable
74- foreach ( $ args [1 ] as $ key => $ value ) {
75- $ msg = str_replace ( '{: ' . $ key . '} ' , $ value , $ msg );
75+ foreach ( $ args [0 ] as $ key => $ value ) {
76+ $ msg = str_replace ( '{: ' . $ key . '} ' , is_scalar ( $ value ) ? ( string ) $ value : '' , $ msg );
7677 }
7778 return Colors::shouldColorize () ? Colors::colorize ( $ msg ) : $ msg ;
7879 }
@@ -87,7 +88,8 @@ public static function render( $msg, ...$args ) {
8788 * @see \cli\render()
8889 */
8990 public static function out ( $ msg , ...$ args ) {
90- fwrite ( static ::$ out , self ::_call ( 'render ' , func_get_args () ) );
91+ $ rendered = self ::_call ( 'render ' , func_get_args () );
92+ fwrite ( static ::$ out , is_scalar ( $ rendered ) ? (string ) $ rendered : '' );
9193 }
9294
9395 /**
@@ -99,7 +101,8 @@ public static function out( $msg, ...$args ) {
99101 * @see cli\out()
100102 */
101103 public static function out_padded ( $ msg , ...$ args ) {
102- $ msg = self ::_call ( 'render ' , func_get_args () );
104+ $ rendered = self ::_call ( 'render ' , func_get_args () );
105+ $ msg = is_scalar ( $ rendered ) ? (string ) $ rendered : '' ;
103106 self ::out ( str_pad ( $ msg , \cli \Shell::columns () ) );
104107 }
105108
@@ -113,8 +116,8 @@ public static function out_padded( $msg, ...$args ) {
113116 */
114117 public static function line ( $ msg = '' ) {
115118 // func_get_args is empty if no args are passed even with the default above.
116- $ args = array_merge ( func_get_args (), array ( '' ) );
117- $ args [0 ] .= "\n" ;
119+ $ args = array_merge ( func_get_args (), array ( '' ) );
120+ $ args [0 ] = ( is_scalar ( $ args [ 0 ] ) ? ( string ) $ args [ 0 ] : '' ) . "\n" ;
118121
119122 self ::_call ( 'out ' , $ args );
120123 }
@@ -130,9 +133,10 @@ public static function line( $msg = '' ) {
130133 */
131134 public static function err ( $ msg = '' , ...$ args ) {
132135 // func_get_args is empty if no args are passed even with the default above.
133- $ args = array_merge ( func_get_args (), array ( '' ) );
134- $ args [0 ] .= "\n" ;
135- fwrite ( static ::$ err , self ::_call ( 'render ' , $ args ) );
136+ $ args = array_merge ( func_get_args (), array ( '' ) );
137+ $ args [0 ] = ( is_scalar ( $ args [0 ] ) ? (string ) $ args [0 ] : '' ) . "\n" ;
138+ $ rendered = self ::_call ( 'render ' , $ args );
139+ fwrite ( static ::$ err , is_scalar ( $ rendered ) ? (string ) $ rendered : '' );
136140 }
137141
138142 /**
@@ -147,10 +151,11 @@ public static function err( $msg = '', ...$args ) {
147151 * @throws \Exception Thrown if ctrl-D (EOT) is sent as input.
148152 */
149153 public static function input ( $ format = null , $ hide = false ) {
150- if ( $ hide )
154+ if ( $ hide ) {
151155 Shell::hide ();
156+ }
152157
153- if ( $ format ) {
158+ if ( $ format ) {
154159 fscanf ( static ::$ in , $ format . "\n" , $ line );
155160 } else {
156161 $ line = fgets ( static ::$ in );
@@ -161,7 +166,7 @@ public static function input( $format = null, $hide = false ) {
161166 echo "\n" ;
162167 }
163168
164- if ( $ line === false ) {
169+ if ( $ line === false ) {
165170 throw new \Exception ( 'Caught ^D during input ' );
166171 }
167172
@@ -181,18 +186,18 @@ public static function input( $format = null, $hide = false ) {
181186 * @see cli\input()
182187 */
183188 public static function prompt ( $ question , $ default = false , $ marker = ': ' , $ hide = false ) {
184- if ( $ default && strpos ( $ question , '[ ' ) === false ) {
189+ if ( $ default && strpos ( $ question , '[ ' ) === false ) {
185190 $ question .= ' [ ' . $ default . '] ' ;
186191 }
187192
188- while ( true ) {
193+ while ( true ) {
189194 self ::out ( $ question . $ marker );
190195 $ line = self ::input ( null , $ hide );
191196
192197 if ( trim ( $ line ) !== '' ) {
193198 return $ line ;
194199 }
195- if ( $ default !== false ) {
200+ if ( $ default !== false ) {
196201 return (string ) $ default ;
197202 }
198203 }
@@ -209,7 +214,7 @@ public static function prompt( $question, $default = false, $marker = ': ', $hid
209214 * @see cli\prompt()
210215 */
211216 public static function choose ( $ question , $ choice = 'yn ' , $ default = 'n ' ) {
212- if ( !is_string ( $ choice ) ) {
217+ if ( ! is_string ( $ choice ) ) {
213218 $ choice = join ( '' , $ choice );
214219 }
215220
@@ -222,13 +227,13 @@ public static function choose( $question, $choice = 'yn', $default = 'n' ) {
222227 // Separate each choice with a forward-slash
223228 $ choices = trim ( join ( '/ ' , str_split ( $ choice ) ), '/ ' );
224229
225- while ( true ) {
230+ while ( true ) {
226231 $ line = self ::prompt ( sprintf ( '%s? [%s] ' , $ question , $ choices ), $ default ?? false , '' );
227232
228- if ( stripos ( $ choice , $ line ) !== false ) {
233+ if ( stripos ( $ choice , $ line ) !== false ) {
229234 return strtolower ( $ line );
230235 }
231- if ( !empty ( $ default ) ) {
236+ if ( ! empty ( $ default ) ) {
232237 return strtolower ( $ default );
233238 }
234239 }
@@ -250,29 +255,42 @@ public static function choose( $question, $choice = 'yn', $default = 'n' ) {
250255 public static function menu ( $ items , $ default = null , $ title = 'Choose an item ' ) {
251256 $ map = array_values ( $ items );
252257
253- if ( $ default && strpos ( $ title , '[ ' ) === false && isset ( $ items [$ default ] ) ) {
254- $ title .= ' [ ' . $ items [$ default ] . '] ' ;
258+ if ( $ default && strpos ( $ title , '[ ' ) === false && isset ( $ items [ $ default ] ) ) {
259+ $ default_item = $ items [ $ default ];
260+ $ default_str = '' ;
261+ if ( is_scalar ( $ default_item ) ) {
262+ $ default_str = (string ) $ default_item ;
263+ } elseif ( is_object ( $ default_item ) && method_exists ( $ default_item , '__toString ' ) ) {
264+ $ default_str = (string ) $ default_item ;
265+ }
266+ $ title .= ' [ ' . $ default_str . '] ' ;
255267 }
256268
257- foreach ( $ map as $ idx => $ item ) {
258- self ::line ( ' %d. %s ' , $ idx + 1 , (string )$ item );
269+ foreach ( $ map as $ idx => $ item ) {
270+ $ item_str = '' ;
271+ if ( is_scalar ( $ item ) ) {
272+ $ item_str = (string ) $ item ;
273+ } elseif ( is_object ( $ item ) && method_exists ( $ item , '__toString ' ) ) {
274+ $ item_str = (string ) $ item ;
275+ }
276+ self ::line ( ' %d. %s ' , $ idx + 1 , $ item_str );
259277 }
260278 self ::line ();
261279
262- while ( true ) {
280+ while ( true ) {
263281 fwrite ( static ::$ out , sprintf ( '%s: ' , $ title ) );
264282 $ line = self ::input ();
265283
266- if ( is_numeric ( $ line ) ) {
267- $ line -- ;
268- if ( isset ( $ map [$ line ] ) ) {
269- return (string ) array_search ( $ map [$ line ], $ items );
284+ if ( is_numeric ( $ line ) ) {
285+ -- $ line ;
286+ if ( isset ( $ map [ $ line ] ) ) {
287+ return (string ) array_search ( $ map [ $ line ], $ items );
270288 }
271289
272- if ( $ line < 0 || $ line >= count ( $ map ) ) {
290+ if ( $ line < 0 || $ line >= count ( $ map ) ) {
273291 self ::err ( 'Invalid menu selection: out of range ' );
274292 }
275- } else if ( isset ( $ default ) ) {
293+ } elseif ( isset ( $ default ) ) {
276294 return $ default ;
277295 }
278296 }
@@ -295,15 +313,16 @@ public static function menu( $items, $default = null, $title = 'Choose an item'
295313 * @throws \Exception Thrown if $stream is not a resource of the 'stream' type.
296314 */
297315 public static function setStream ( $ whichStream , $ stream ) {
298- if ( !is_resource ( $ stream ) || get_resource_type ( $ stream ) !== 'stream ' ) {
316+ if ( ! is_resource ( $ stream ) || get_resource_type ( $ stream ) !== 'stream ' ) {
299317 throw new \Exception ( 'Invalid resource type! ' );
300318 }
301- if ( property_exists ( __CLASS__ , $ whichStream ) ) {
319+ if ( property_exists ( __CLASS__ , $ whichStream ) ) {
302320 static ::$ {$ whichStream } = $ stream ;
303321 }
304- register_shutdown_function ( function () use ($ stream ) {
305- fclose ( $ stream );
306- } );
322+ register_shutdown_function (
323+ function () use ( $ stream ) {
324+ fclose ( $ stream );
325+ }
326+ );
307327 }
308-
309328}
0 commit comments