2015-04-09 05:45:07 -04:00
< ? php
2024-05-24 13:43:47 -04:00
2015-04-09 05:45:07 -04:00
/**
2024-05-24 13:43:47 -04:00
* SPDX - FileCopyrightText : 2016 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - only
2015-04-09 05:45:07 -04:00
*/
namespace OC\Core\Command ;
2018-07-09 08:22:10 -04:00
use OC\Core\Command\User\ListCommand ;
2022-06-27 09:20:16 -04:00
use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface ;
2016-09-20 18:47:45 -04:00
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext ;
2015-04-09 05:45:07 -04:00
use Symfony\Component\Console\Command\Command ;
2022-06-27 09:20:16 -04:00
use Symfony\Component\Console\Helper\Table ;
2015-04-09 05:45:07 -04:00
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Input\InputOption ;
use Symfony\Component\Console\Output\OutputInterface ;
2016-09-20 18:47:45 -04:00
class Base extends Command implements CompletionAwareInterface {
2020-04-10 10:54:27 -04:00
public const OUTPUT_FORMAT_PLAIN = 'plain' ;
public const OUTPUT_FORMAT_JSON = 'json' ;
public const OUTPUT_FORMAT_JSON_PRETTY = 'json_pretty' ;
2015-07-17 03:25:19 -04:00
2022-04-12 11:55:01 -04:00
protected string $defaultOutputFormat = self :: OUTPUT_FORMAT_PLAIN ;
private bool $php_pcntl_signal = false ;
private bool $interrupted = false ;
2025-07-31 11:40:36 -04:00
public string $defaultHelp = 'More extensive and thorough documentation may be found at https://docs.nextcloud.com' . PHP_EOL ; // TODO: link to appropriately versioned/themed Admin Manual
2016-01-29 09:59:57 -05:00
2015-04-09 05:45:07 -04:00
protected function configure () {
$this
2025-07-31 11:40:36 -04:00
-> setHelp ( $this -> defaultHelp )
2015-04-09 05:45:07 -04:00
-> addOption (
'output' ,
null ,
InputOption :: VALUE_OPTIONAL ,
2015-04-09 08:42:44 -04:00
'Output format (plain, json or json_pretty, default is plain)' ,
2015-07-16 11:51:33 -04:00
$this -> defaultOutputFormat
2015-04-09 05:45:07 -04:00
)
;
}
2024-07-08 10:56:07 -04:00
protected function writeArrayInOutputFormat ( InputInterface $input , OutputInterface $output , iterable $items , string $prefix = ' - ' ) : void {
2015-04-09 08:42:44 -04:00
switch ( $input -> getOption ( 'output' )) {
2015-07-17 03:25:19 -04:00
case self :: OUTPUT_FORMAT_JSON :
2024-07-08 11:02:43 -04:00
$items = ( is_array ( $items ) ? $items : iterator_to_array ( $items ));
$output -> writeln ( json_encode ( $items ));
2015-04-09 08:42:44 -04:00
break ;
2015-07-17 03:25:19 -04:00
case self :: OUTPUT_FORMAT_JSON_PRETTY :
2024-07-08 11:02:43 -04:00
$items = ( is_array ( $items ) ? $items : iterator_to_array ( $items ));
$output -> writeln ( json_encode ( $items , JSON_PRETTY_PRINT ));
2015-04-09 05:45:07 -04:00
break ;
default :
foreach ( $items as $key => $item ) {
2024-07-08 10:56:07 -04:00
if ( is_iterable ( $item )) {
2015-04-30 09:42:18 -04:00
$output -> writeln ( $prefix . $key . ':' );
$this -> writeArrayInOutputFormat ( $input , $output , $item , ' ' . $prefix );
continue ;
}
2024-03-28 11:13:19 -04:00
if ( ! is_int ( $key ) || get_class ( $this ) === ListCommand :: class ) {
2015-04-20 10:31:43 -04:00
$value = $this -> valueToString ( $item );
if ( ! is_null ( $value )) {
2015-04-30 09:42:18 -04:00
$output -> writeln ( $prefix . $key . ': ' . $value );
2015-04-20 10:31:43 -04:00
} else {
2015-04-30 09:42:18 -04:00
$output -> writeln ( $prefix . $key );
2015-04-20 10:31:43 -04:00
}
} else {
2015-04-30 09:42:18 -04:00
$output -> writeln ( $prefix . $this -> valueToString ( $item ));
2015-04-20 10:31:43 -04:00
}
2015-04-09 05:45:07 -04:00
}
break ;
}
}
2015-04-20 10:31:43 -04:00
2022-06-27 09:20:16 -04:00
protected function writeTableInOutputFormat ( InputInterface $input , OutputInterface $output , array $items ) : void {
switch ( $input -> getOption ( 'output' )) {
case self :: OUTPUT_FORMAT_JSON :
$output -> writeln ( json_encode ( $items ));
break ;
case self :: OUTPUT_FORMAT_JSON_PRETTY :
$output -> writeln ( json_encode ( $items , JSON_PRETTY_PRINT ));
break ;
default :
$table = new Table ( $output );
$table -> setRows ( $items );
if ( ! empty ( $items ) && is_string ( array_key_first ( reset ( $items )))) {
$table -> setHeaders ( array_keys ( reset ( $items )));
}
$table -> render ();
break ;
}
}
2025-03-28 10:23:13 -04:00
protected function writeStreamingTableInOutputFormat ( InputInterface $input , OutputInterface $output , \Iterator $items , int $tableGroupSize ) : void {
switch ( $input -> getOption ( 'output' )) {
case self :: OUTPUT_FORMAT_JSON :
case self :: OUTPUT_FORMAT_JSON_PRETTY :
$this -> writeStreamingJsonArray ( $input , $output , $items );
break ;
default :
foreach ( $this -> chunkIterator ( $items , $tableGroupSize ) as $chunk ) {
$this -> writeTableInOutputFormat ( $input , $output , $chunk );
}
break ;
}
}
protected function writeStreamingJsonArray ( InputInterface $input , OutputInterface $output , \Iterator $items ) : void {
$first = true ;
$outputType = $input -> getOption ( 'output' );
$output -> writeln ( '[' );
foreach ( $items as $item ) {
if ( ! $first ) {
$output -> writeln ( ',' );
}
if ( $outputType === self :: OUTPUT_FORMAT_JSON_PRETTY ) {
$output -> write ( json_encode ( $item , JSON_PRETTY_PRINT ));
} else {
$output -> write ( json_encode ( $item ));
}
$first = false ;
}
$output -> writeln ( " \n ] " );
}
public function chunkIterator ( \Iterator $iterator , int $count ) : \Iterator {
$chunk = [];
for ( $i = 0 ; $iterator -> valid (); $i ++ ) {
$chunk [] = $iterator -> current ();
$iterator -> next ();
if ( count ( $chunk ) == $count ) {
// Got a full chunk, yield and start a new one
yield $chunk ;
$chunk = [];
}
}
if ( count ( $chunk )) {
// Yield the last chunk even if incomplete
yield $chunk ;
}
}
2022-06-27 09:20:16 -04:00
2015-05-08 11:13:46 -04:00
/**
* @ param mixed $item
*/
protected function writeMixedInOutputFormat ( InputInterface $input , OutputInterface $output , $item ) {
if ( is_array ( $item )) {
$this -> writeArrayInOutputFormat ( $input , $output , $item , '' );
return ;
}
switch ( $input -> getOption ( 'output' )) {
2015-07-17 03:25:19 -04:00
case self :: OUTPUT_FORMAT_JSON :
2015-05-08 11:13:46 -04:00
$output -> writeln ( json_encode ( $item ));
break ;
2015-07-17 03:25:19 -04:00
case self :: OUTPUT_FORMAT_JSON_PRETTY :
2015-05-08 11:13:46 -04:00
$output -> writeln ( json_encode ( $item , JSON_PRETTY_PRINT ));
break ;
default :
$output -> writeln ( $this -> valueToString ( $item , false ));
break ;
}
}
2022-04-12 11:55:01 -04:00
protected function valueToString ( $value , bool $returnNull = true ) : ? string {
2015-04-20 10:31:43 -04:00
if ( $value === false ) {
return 'false' ;
2020-04-10 04:35:09 -04:00
} elseif ( $value === true ) {
2015-04-20 10:31:43 -04:00
return 'true' ;
2020-04-10 04:35:09 -04:00
} elseif ( $value === null ) {
2018-01-26 17:46:40 -05:00
return $returnNull ? null : 'null' ;
2025-07-23 12:22:51 -04:00
} if ( $value instanceof \UnitEnum ) {
return $value -> value ;
2015-04-20 10:31:43 -04:00
} else {
return $value ;
}
}
2016-01-29 09:59:57 -05:00
/**
2018-10-07 12:12:38 -04:00
* Throw InterruptedException when interrupted by user
*
* @ throws InterruptedException
2016-01-29 09:59:57 -05:00
*/
2018-10-08 07:05:00 -04:00
protected function abortIfInterrupted () {
2018-10-07 12:12:38 -04:00
if ( $this -> php_pcntl_signal === false ) {
return ;
}
pcntl_signal_dispatch ();
if ( $this -> interrupted === true ) {
throw new InterruptedException ( 'Command interrupted by user' );
2016-01-29 09:59:57 -05:00
}
}
/**
* Changes the status of the command to " interrupted " if ctrl - c has been pressed
*
* Gives a chance to the command to properly terminate what it ' s doing
*/
2023-08-27 11:29:26 -04:00
public function cancelOperation () : void {
2016-01-29 09:59:57 -05:00
$this -> interrupted = true ;
}
2024-10-02 17:38:05 -04:00
public function run ( InputInterface $input , OutputInterface $output ) : int {
2016-02-09 09:52:19 -05:00
// check if the php pcntl_signal functions are accessible
$this -> php_pcntl_signal = function_exists ( 'pcntl_signal' );
if ( $this -> php_pcntl_signal ) {
// Collect interrupts and notify the running command
pcntl_signal ( SIGTERM , [ $this , 'cancelOperation' ]);
pcntl_signal ( SIGINT , [ $this , 'cancelOperation' ]);
}
return parent :: run ( $input , $output );
}
2016-09-20 18:47:45 -04:00
/**
* @ param string $optionName
* @ param CompletionContext $context
* @ return string []
*/
public function completeOptionValues ( $optionName , CompletionContext $context ) {
2016-09-21 08:03:09 -04:00
if ( $optionName === 'output' ) {
return [ 'plain' , 'json' , 'json_pretty' ];
}
2016-09-20 18:47:45 -04:00
return [];
}
/**
* @ param string $argumentName
* @ param CompletionContext $context
* @ return string []
*/
public function completeArgumentValues ( $argumentName , CompletionContext $context ) {
return [];
}
2015-04-09 05:45:07 -04:00
}