AboutCommand.php
TLDR
The AboutCommand.php
file in the Illuminate\Foundation\Console
namespace contains the AboutCommand
class, which displays basic information about an application. It extends the Command
class from the Illuminate\Console
namespace.
Methods
handle
Executes the console command and displays the application information. Returns an integer.
display
Displays the application information based on the desired output format: detail view or JSON.
displayDetail
Displays the application information as a detail view.
displayJson
Displays the application information as JSON.
gatherApplicationInformation
Gathers information about the application by adding data to the self::$data
array.
hasPhpFiles
Determines whether the given directory has PHP files.
add
Adds additional data to the output of the "about" command.
addToSection
Adds additional data to the output of the "about" command. Handles both array and callable data.
sections
Retrieves the sections provided to the command.
format
Materializes a function that formats a given value for CLI or JSON output.
toSearchKeyword
Formats the given string for searching.
Classes
AboutCommand
Represents a command that displays basic information about an application. Inherits from the Command
class.
<?php
namespace Illuminate\Foundation\Console;
use Closure;
use Illuminate\Console\Command;
use Illuminate\Support\Composer;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'about')]
class AboutCommand extends Command
{
/**
* The console command signature.
*
* @var string
*/
protected $signature = 'about {--only= : The section to display}
{--json : Output the information as JSON}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display basic information about your application';
/**
* The Composer instance.
*
* @var \Illuminate\Support\Composer
*/
protected $composer;
/**
* The data to display.
*
* @var array
*/
protected static $data = [];
/**
* The registered callables that add custom data to the command output.
*
* @var array
*/
protected static $customDataResolvers = [];
/**
* Create a new command instance.
*
* @param \Illuminate\Support\Composer $composer
* @return void
*/
public function __construct(Composer $composer)
{
parent::__construct();
$this->composer = $composer;
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->gatherApplicationInformation();
collect(static::$data)
->map(fn ($items) => collect($items)
->map(function ($value) {
if (is_array($value)) {
return [$value];
}
if (is_string($value)) {
$value = $this->laravel->make($value);
}
return collect($this->laravel->call($value))
->map(fn ($value, $key) => [$key, $value])
->values()
->all();
})->flatten(1)
)
->sortBy(function ($data, $key) {
$index = array_search($key, ['Environment', 'Cache', 'Drivers']);
return $index === false ? 99 : $index;
})
->filter(function ($data, $key) {
return $this->option('only') ? in_array($this->toSearchKeyword($key), $this->sections()) : true;
})
->pipe(fn ($data) => $this->display($data));
$this->newLine();
return 0;
}
/**
* Display the application information.
*
* @param \Illuminate\Support\Collection $data
* @return void
*/
protected function display($data)
{
$this->option('json') ? $this->displayJson($data) : $this->displayDetail($data);
}
/**
* Display the application information as a detail view.
*
* @param \Illuminate\Support\Collection $data
* @return void
*/
protected function displayDetail($data)
{
$data->each(function ($data, $section) {
$this->newLine();
$this->components->twoColumnDetail(' <fg=green;options=bold>'.$section.'</>');
$data->pipe(fn ($data) => $section !== 'Environment' ? $data->sort() : $data)->each(function ($detail) {
[$label, $value] = $detail;
$this->components->twoColumnDetail($label, value($value, false));
});
});
}
/**
* Display the application information as JSON.
*
* @param \Illuminate\Support\Collection $data
* @return void
*/
protected function displayJson($data)
{
$output = $data->flatMap(function ($data, $section) {
return [
(string) Str::of($section)->snake() => $data->mapWithKeys(fn ($item, $key) => [
$this->toSearchKeyword($item[0]) => value($item[1], true),
]),
];
});
$this->output->writeln(strip_tags(json_encode($output)));
}
/**
* Gather information about the application.
*
* @return void
*/
protected function gatherApplicationInformation()
{
self::$data = [];
$formatEnabledStatus = fn ($value) => $value ? '<fg=yellow;options=bold>ENABLED</>' : 'OFF';
$formatCachedStatus = fn ($value) => $value ? '<fg=green;options=bold>CACHED</>' : '<fg=yellow;options=bold>NOT CACHED</>';
static::addToSection('Environment', fn () => [
'Application Name' => config('app.name'),
'Laravel Version' => $this->laravel->version(),
'PHP Version' => phpversion(),
'Composer Version' => $this->composer->getVersion() ?? '<fg=yellow;options=bold>-</>',
'Environment' => $this->laravel->environment(),
'Debug Mode' => static::format(config('app.debug'), console: $formatEnabledStatus),
'URL' => Str::of(config('app.url'))->replace(['http://', 'https://'], ''),
'Maintenance Mode' => static::format($this->laravel->isDownForMaintenance(), console: $formatEnabledStatus),
]);
static::addToSection('Cache', fn () => [
'Config' => static::format($this->laravel->configurationIsCached(), console: $formatCachedStatus),
'Events' => static::format($this->laravel->eventsAreCached(), console: $formatCachedStatus),
'Routes' => static::format($this->laravel->routesAreCached(), console: $formatCachedStatus),
'Views' => static::format($this->hasPhpFiles($this->laravel->storagePath('framework/views')), console: $formatCachedStatus),
]);
static::addToSection('Drivers', fn () => array_filter([
'Broadcasting' => config('broadcasting.default'),
'Cache' => config('cache.default'),
'Database' => config('database.default'),
'Logs' => function ($json) {
$logChannel = config('logging.default');
if (config('logging.channels.'.$logChannel.'.driver') === 'stack') {
$secondary = collect(config('logging.channels.'.$logChannel.'.channels'));
return value(static::format(
value: $logChannel,
console: fn ($value) => '<fg=yellow;options=bold>'.$value.'</> <fg=gray;options=bold>/</> '.$secondary->implode(', '),
json: fn () => $secondary->all(),
), $json);
} else {
$logs = $logChannel;
}
return $logs;
},
'Mail' => config('mail.default'),
'Octane' => config('octane.server'),
'Queue' => config('queue.default'),
'Scout' => config('scout.driver'),
'Session' => config('session.driver'),
]));
collect(static::$customDataResolvers)->each->__invoke();
}
/**
* Determine whether the given directory has PHP files.
*
* @param string $path
* @return bool
*/
protected function hasPhpFiles(string $path): bool
{
return count(glob($path.'/*.php')) > 0;
}
/**
* Add additional data to the output of the "about" command.
*
* @param string $section
* @param callable|string|array $data
* @param string|null $value
* @return void
*/
public static function add(string $section, $data, string $value = null)
{
static::$customDataResolvers[] = fn () => static::addToSection($section, $data, $value);
}
/**
* Add additional data to the output of the "about" command.
*
* @param string $section
* @param callable|string|array $data
* @param string|null $value
* @return void
*/
protected static function addToSection(string $section, $data, string $value = null)
{
if (is_array($data)) {
foreach ($data as $key => $value) {
self::$data[$section][] = [$key, $value];
}
} elseif (is_callable($data) || ($value === null && class_exists($data))) {
self::$data[$section][] = $data;
} else {
self::$data[$section][] = [$data, $value];
}
}
/**
* Get the sections provided to the command.
*
* @return array
*/
protected function sections()
{
return collect(explode(',', $this->option('only') ?? ''))
->filter()
->map(fn ($only) => $this->toSearchKeyword($only))
->all();
}
/**
* Materialize a function that formats a given value for CLI or JSON output.
*
* @param mixed $value
* @param (\Closure(mixed):(mixed))|null $console
* @param (\Closure(mixed):(mixed))|null $json
* @return \Closure(bool):mixed
*/
public static function format($value, Closure $console = null, Closure $json = null)
{
return function ($isJson) use ($value, $console, $json) {
if ($isJson === true && $json instanceof Closure) {
return value($json, $value);
} elseif ($isJson === false && $console instanceof Closure) {
return value($console, $value);
}
return value($value);
};
}
/**
* Format the given string for searching.
*
* @param string $value
* @return string
*/
protected function toSearchKeyword(string $value)
{
return (string) Str::of($value)->lower()->snake();
}
}