This is a console command used as a demonstration to quickly find the various possibilities offered by the output of a console command.
- Copy the file as
TestOutputCommand.phpin your pluginconsoledirectory - Adapt namespace
Acme\Myplugin\Consoleto your plugin - Register the command in your plugin's
plugin.phpfile
public function register(): void
{
// ...
$this->registerConsoleCommand(
'myplugin.testoutput',
\Acme\Myplugin\Console\TestOutputCommand::class
);
}# Show all
php artisan myplugin:testoutput
# Show default output commands
php artisan myplugin:testoutput default
# Show laravel output commands
php artisan myplugin:testoutput laravel
# Show availables styles
php artisan myplugin:testoutput styles
# Show a filtered list of commands matching a keyword
php artisan myplugin:testoutput --keyword=list
# Same combined with choosen command
php artisan myplugin:testoutput default --keyword=listSome commands are not rendered in the same way on all OS, especially Windows.
<?php
namespace Acme\Myplugin\Console;
use ReflectionProperty;
use Symfony\Component\Console\Color;
use Symfony\Component\Console\Helper\TableSeparator;
use Winter\Storm\Console\Command;
class TestOutputCommand extends Command
{
/**
* @var string The console command name.
*/
protected static $defaultName = 'myplugin:testoutput';
/**
* @var string The name and signature of this command.
*/
protected $signature = 'myplugin:testoutput
{outputStyle? : Type of command to display (none, default, laravel or styles).}
{--keyword=* : Filter the output functions based on keyword(s) (optional, multiple values possible).}
';
/**
* @var string The console command description.
*/
protected $description = 'Demonstration of console output methods';
private array $titles = [
'default' => [
' ____ ____ ____ __ __',
' / __ \___ / __/___ ___ __/ / /_ ____ __ __/ /_____ __ __/ /______',
' / / / / _ \/ /_/ __ `/ / / / / __/ / __ \/ / / / __/ __ \/ / / / __/ ___/',
' / /_/ / __/ __/ /_/ / /_/ / / /_ / /_/ / /_/ / /_/ /_/ / /_/ / /_(__ )',
'/_____/\___/_/ \__,_/\__,_/_/\__/ \____/\__,_/\__/ .___/\__,_/\__/____/',
' /_/',
],
'laravel' => [
' __ __ __ __',
' / / ____ __________ __ _____ / / ____ __ __/ /_____ __ __/ /______',
' / / / __ `/ ___/ __ `/ | / / _ \/ / / __ \/ / / / __/ __ \/ / / / __/ ___/',
' / /___/ /_/ / / / /_/ /| |/ / __/ / / /_/ / /_/ / /_/ /_/ / /_/ / /_(__ )',
'/_____/\__,_/_/ \__,_/ |___/\___/_/ \____/\__,_/\__/ .___/\__,_/\__/____/',
' /_/',
],
'styles' => [
' ___ ____ __ __',
' / | / / / _____/ /___ __/ /__ _____',
' / /| | / / / / ___/ __/ / / / / _ \/ ___/',
' / ___ |/ / / (__ ) /_/ /_/ / / __(__ )',
'/_/ |_/_/_/ /____/\__/\__, /_/\___/____/',
' /____/',
],
];
public function __construct()
{
parent::__construct();
$this->outputs = collect([
// Titles
'title' => [
'display' => 'Title',
'description' => 'Display a title in the console output.',
'keywords' => ['title'],
'methods' => [
'default' => 'title',
],
'arguments' => ['This is a title'],
],
// Sections
'section' => [
'display' => 'Section',
'description' => 'Display a section title in the console output.',
'keywords' => ['section'],
'methods' => [
'default' => 'section',
],
'arguments' => ['This is a section'],
],
// Texts
'text' => [
'display' => 'Text',
'description' => 'Display a text in the console output.',
'keywords' => ['text', 'line'],
'methods' => [
'default' => 'text',
// 'laravel' => 'line',
],
'arguments' => ['This is a text'],
],
'text (multiple)' => [
'display' => 'Text',
'description' => 'Display a text in the console output.',
'keywords' => ['text', 'line'],
'methods' => [
'default' => 'text',
],
'arguments' => [[
'This is a text',
'in a multi-line format',
]],
],
// Tables
'table' => [
'display' => 'Table',
'description' => 'Display a table with headers and rows.',
'keywords' => ['table', 'row', 'column'],
'methods' => [
'default' => 'table',
'short' => 'default',
],
'arguments' => [
['Header 1', 'Header 2'],
[
['Cell 1-1', 'Cell 1-2'],
['Cell 2-1', 'Cell 2-2'],
['Cell 3-1', 'Cell 3-2'],
]
],
],
'horizontalTable' => [
'display' => 'Horizontal Table',
'description' => 'Display a horizontal table with headers and rows.',
'keywords' => ['table', 'row', 'column'],
'methods' => [
'default' => 'horizontalTable',
],
'arguments' => [
['Header 1', 'Header 2'],
[
['Cell 1-1', 'Cell 1-2'],
['Cell 2-1', 'Cell 2-2'],
['Cell 3-1', 'Cell 3-2'],
]
],
],
'twoColumnDetail' => [
'display' => 'Two Column Detail',
'description' => 'Display two columns of details.',
'keywords' => ['table', 'row', 'column', 'detail'],
'methods' => [
'laravel' => 'twoColumnDetail',
],
'arguments' => [
'Column 1: Lorem ipsum dolor sit amet',
'Column 2: Consectetur adipiscing elit',
],
],
// Definition List
'definitionList' => [
'display' => 'Definition List',
'description' => 'Display a definition list with terms and descriptions.',
'keywords' => ['list', 'definition'],
'methods' => [
'default' => 'definitionList',
],
'arguments' => [
'This is a title',
['foo1' => 'bar1'],
['foo2' => 'bar2'],
['foo3' => 'bar3'],
new TableSeparator(),
'This is another title',
['foo4' => 'bar4'],
],
],
// Listing
'listing' => [
'display' => 'Listing',
'description' => 'Display a list of items.',
'keywords' => ['list', 'definition'],
'methods' => [
'default' => 'listing',
'laravel' => 'bulletList',
],
'arguments' => [
[
'Element #1 Lorem ipsum dolor sit amet',
'Element #2 Lorem ipsum dolor sit amet',
'Element #3 Lorem ipsum dolor sit amet',
],
],
],
// Info
'info' => [
'display' => 'Info',
'description' => 'Display an informational message.',
'keywords' => ['message', 'info'],
'methods' => [
'default' => 'info',
'laravel' => 'info',
],
'arguments' => [
'This is an informational message.',
],
],
'info (multiple)' => [
'display' => 'Info',
'description' => 'Display an informational message.',
'keywords' => ['message', 'info'],
'methods' => [
'default' => 'info',
],
'arguments' => [
[
'This is an informational message.',
'This is another informational message.',
],
],
],
// Success
'success' => [
'display' => 'Success',
'description' => 'Display a success message.',
'keywords' => ['message', 'success'],
'methods' => [
'default' => 'success',
],
'arguments' => [
'This is a success message.',
],
],
'success (multiple)' => [
'display' => 'Success',
'description' => 'Display a success message.',
'keywords' => ['message', 'success'],
'methods' => [
'default' => 'success',
],
'arguments' => [
[
'This is a success message.',
'This is another success message.',
],
],
],
// Note
'note' => [
'display' => 'Note',
'description' => 'Display a note message.',
'keywords' => ['message', 'note'],
'methods' => [
'default' => 'note',
],
'arguments' => [
'This is a note message.',
],
],
'note (multiple)' => [
'display' => 'Note',
'description' => 'Display a note message.',
'keywords' => ['message', 'note'],
'methods' => [
'default' => 'note',
],
'arguments' => [
[
'This is a note message.',
'This is another note message.',
],
],
],
// Warning
'warning' => [
'display' => 'Warning',
'description' => 'Display a warning message.',
'keywords' => ['message', 'warning'],
'methods' => [
'default' => 'caution',
'laravel' => 'warn',
],
'arguments' => [
'This is a warning message.',
],
],
'warning (multiple)' => [
'display' => 'Warning',
'description' => 'Display a warning message.',
'keywords' => ['message', 'warning'],
'methods' => [
'default' => 'caution',
],
'arguments' => [
[
'This is an warning message.',
'This is another warning message.',
],
],
],
// Error
'error' => [
'display' => 'Error',
'description' => 'Display an error message.',
'keywords' => ['message', 'error'],
'methods' => [
'default' => 'error',
'laravel' => 'error',
],
'arguments' => [
'This is an error message.',
],
],
'error (multiple)' => [
'display' => 'Error',
'description' => 'Display an error message.',
'keywords' => ['message', 'error'],
'methods' => [
'default' => 'error',
],
'arguments' => [
[
'This is an error message.',
'This is another error message.',
],
],
],
// Alert
'alert' => [
'display' => 'Alert',
'description' => 'Display an alert message.',
'keywords' => ['message', 'alert'],
'methods' => [
'laravel' => 'alert',
],
'arguments' => [
'This is an alert message.',
],
],
// Task
'task' => [
'display' => 'Task',
'description' => 'Display a task with a description and a callable function.',
'keywords' => ['task', 'function', 'callable'],
'methods' => [
'laravel' => 'task',
],
'arguments' => [
'Task description',
fn() => $this->fakeCallable(),
],
],
// Ask
'ask' => [
'display' => 'Ask',
'description' => 'Ask a question and get an answer.',
'keywords' => ['question', 'answer'],
'methods' => [
'default' => 'ask',
'laravel' => 'ask',
],
'arguments' => [
'What is your name?',
'John Doe',
],
'response' => true,
],
// Ask with completion
'askWithCompletion' => [
'display' => 'Ask with Completion',
'description' => 'Ask a question with autocompletion options.',
'keywords' => ['question', 'answer', 'completion'],
'methods' => [
'laravel' => 'askWithCompletion',
],
'arguments' => [
'What is your favorite color?',
['red', 'green', 'blue', 'yellow'],
'blue'
],
'response' => true,
],
// Choice
'choice' => [
'display' => 'Choice',
'description' => 'Ask a question with multiple choice options.',
'keywords' => ['question', 'answer', 'choice'],
'methods' => [
'default' => 'choice',
'laravel' => 'choice',
],
'arguments' => [
'What is your favorite color?',
['red', 'green', 'blue', 'yellow'],
'blue',
1,
false,
],
'response' => true,
],
'choice (multiple)' => [
'display' => 'Choice',
'description' => 'Ask a question with multiple choice options.',
'keywords' => ['question', 'answer', 'choice', 'multiple'],
'methods' => [
'default' => 'choice',
'laravel' => 'choice',
],
'arguments' => [
'What is your favorite color?',
['red', 'green', 'blue', 'yellow'],
'blue',
1,
true,
],
'response' => true,
],
// Confirm
'confirm' => [
'display' => 'Confirm',
'description' => 'Ask a yes/no question and get a confirmation response.',
'keywords' => ['question', 'answer', 'confirmation'],
'methods' => [
'default' => 'confirm',
'laravel' => 'confirm',
],
'arguments' => [
'Do you want to proceed?',
'yes',
],
'confirm' => true,
],
// Tree - only available in Symfony 7.3+
// 'tree' => [
// 'display' => 'Tree',
// 'description' => 'Display a tree structure.',
// 'keywords' => ['tree', 'structure'],
// 'methods' => [
// 'default' => 'tree',
// ],
// 'arguments' => [
// [
// 'src' => [
// 'Controller' => [
// 'DefaultController.php',
// ],
// 'Kernel.php',
// ],
// 'templates' => [
// 'base.html.twig',
// ],
// ]
// ],
// ],
]);
}
/**
* Execute the console command.
* @return void
*/
public function handle()
{
$availableModes = ['default', 'laravel', 'styles'];
if (!$this->argument('outputStyle')) {
if ($this->option('keyword')) {
// Remove styles from $availableModes
$availableModes = ['default', 'laravel'];
}
foreach ($availableModes as $key) {
$this->output->text($this->titles[$key]);
$this->output->newLine();
$outputMethod = $key .'Outputs';
$this->{$outputMethod}();
}
} elseif (in_array($this->argument('outputStyle'), $availableModes, true)) {
$mode = $this->argument('commandType');
$this->output->text($this->titles[$mode]);
$this->output->newLine();
$outputMethod = $mode .'Outputs';
$this->{$outputMethod}();
} else {
return Command::FAILURE;
}
return Command::SUCCESS;
}
/**
* Display default console outputs.
*/
private function defaultOutputs(): void
{
$defaultOutputs = $this->outputs->filter(function ($output, $key) {
return array_get($output, 'methods.default', false);
});
$defaultOutputs = $this->filterOutputsByKeywords($defaultOutputs, $this->option('keyword'));
if ($defaultOutputs->isEmpty()) {
$this->output->text('<fg=red>No default outputs found for the given keywords.</>');
return;
} else {
$this->showOutputs($defaultOutputs, 'default');
}
}
/**
* Display Laravel console outputs.
*/
private function laravelOutputs(): void
{
$laravelOutputs = $this->outputs->filter(function ($output, $key) {
return array_get($output, 'methods.laravel', false);
});
$laravelOutputs = $this->filterOutputsByKeywords($laravelOutputs, $this->option('keyword'));
if ($laravelOutputs->isEmpty()) {
$this->output->text('<fg=red>No Laravel outputs found for the given keywords.</>');
return;
} else {
$this->showOutputs($laravelOutputs, 'laravel');
}
}
/**
* Display available console styles.
*/
private function stylesOutputs(): void
{
$reflect = new \ReflectionClass(Color::class);
$availableColorsAndOptions = $reflect->getConstants(\ReflectionProperty::IS_PRIVATE);
$lines= [];
foreach ($availableColorsAndOptions as $key => $options) {
$lines[] = '';
$lines[] = 'π '. $key;
$lines[] = '-------------------------------';
foreach ($options as $color => $value) {
switch ($key) {
case 'COLORS':
$lines[] = "<fg={$color}>{$color}</>";
break;
case 'BRIGHT_COLORS':
$lines[] = "<fg={$color}>{$color}</>";
break;
case 'AVAILABLE_OPTIONS':
$lines[] = "<options={$color}>{$color}</>";
break;
}
}
}
$this->output->text($lines);
$this->output->text([
'',
'π Styles',
'-------------------------------',
'<info>info</info>',
'<comment>comment</comment>',
'<question>question</question>',
'<error>error</error>',
]);
$this->output->text([
'',
'π Custom combos',
]);
$this->output->definitionList(
['Custom foreground and background' => '<fg=blue;options=blink;bg=yellow>blue text on yellow background</>'],
['Clickable URL' => '<fg=bright-blue;href=https://gist.github.com/damsfx/e0a0132970cc682054207c7ad141d57f;>Edit this gist on github.com</>'],
);
$this->output->newLine();
}
private function showOutputs($outputs, string $method): void
{
$outputToUse = $method === 'laravel' ? 'components' : 'output';
foreach ($outputs as $key => $output) {
$this->output->text([
"<fg=bright-yellow>π {$key} : \$this->{$outputToUse}->{$output['methods'][$method]}</>",
"<fg=bright-yellow> {$output['description']}</>",
]);
$needResponse = array_get($output, 'response', false);
$confirmation = array_get($output, 'confirm', false);
if ($needResponse) {
$response = $this->{$outputToUse}->{$output['methods'][$method]}(...$output['arguments']);
if (is_array($response)) {
$response = implode(', ', $response);
}
$this->output->writeln('<info>Response: </info>' . $response);
} elseif ($confirmation) {
$response = $this->{$outputToUse}->{$output['methods'][$method]}(...$output['arguments']);
$this->output->writeln('<info>Confirmation: </info>' . ($response ? 'Yes' : 'No'));
} else {
$this->{$outputToUse}->{$output['methods'][$method]}(...$output['arguments']);
}
if (array_get($output['methods'], 'short', null) === $method) {
$this->output->block(">> {$key} (short) : \$this->{$output['methods'][$method]}", null, 'fg=yellow');
$this->{$output['methods'][$method]}(...$output['arguments']);
}
$this->newLine();
}
}
private function filterOutputsByKeywords($outputs, $keywords)
{
if (!$keywords = $this->option('keyword')) {
return $outputs;
}
// Ensure keywords is an array
if (!is_array($keywords)) {
$keywords = [$keywords];
}
// Filter outputs by keywords
$outputs = $outputs->filter(function ($output) use ($keywords) {
$outputKeywords = array_get($output, 'keywords', []);
return !empty(array_intersect($keywords, $outputKeywords));
});
return $outputs;
}
/**
* Simulate a callable function for demonstration purposes.
* This is just a placeholder to simulate some processing time.
*/
private function fakeCallable(): void
{
// Simulate a delay with a random sleep
usleep(rand(100000, 500000));
}
}