Skip to content

Commit

Permalink
[output] add custom output style (#25)
Browse files Browse the repository at this point in the history
* chore: make ci faster

* chore: mark internal code

* chore: replace helpers with traits

* chore: add Style namespace

* chore: add output helper

* chore: remove un-used constructor

* chore: set output and input

* chore: organize namespaces

* wip

* wip

* chore: replace method wrappers with extend

* chore: convert output into a symfony style

* chore: allow mocking output

- for old symfony versions that don't type hint the `getFormatter()` method

* feat: add custom style

* chore(docs): update output style
  • Loading branch information
imdhemy authored Mar 29, 2024
1 parent c41f90f commit 792b5a3
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 721 deletions.
79 changes: 21 additions & 58 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,73 +1,36 @@
name: "Continuous Integration"

on:
pull_request:
push:

name: CI
on: [ push, pull_request ]
jobs:
phpunit:
name: "Unit Tests"
runs-on: ubuntu-latest
ci:
name: "CI"
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: [ ubuntu-latest, windows-latest, macos-latest ]
php-version: [ 8.3 ]
steps:
- name: "Checkout"
uses: actions/checkout@v3

- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
coverage: "pcov"
php-version: "8.1"
ini-values: memory_limit=-1
extensions: sodium, fileinfo, redis

- name: "Install dependencies"
uses: ramsey/composer-install@v2
with:
dependency-versions: "locked"

- name: "Run PHPUnit"
run: composer test

psalm:
name: "Static Analysis"
runs-on: ubuntu-latest
steps:
# --------- Setup steps ---------
- name: "Checkout"
uses: actions/checkout@v3

- name: "Install PHP"
- name: "Setup PHP"
uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
coverage: "pcov"
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1
extensions: sodium, fileinfo, redis
extensions: pcov, xdebug

- name: "Install dependencies"
uses: ramsey/composer-install@v2
with:
dependency-versions: "locked"

- name: "Run Psalm"
run: composer psalm

phpcs:
name: "Code Style"
runs-on: ubuntu-latest
steps:
- name: "Checkout"
uses: actions/checkout@v3

- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
ini-values: memory_limit=-1
extensions: sodium, fileinfo, redis
# --------- Run steps ---------
- name: "Unit Tests"
run: "composer test"

- name: "Install dependencies"
uses: ramsey/composer-install@v2
with:
dependency-versions: "locked"
- name: "Coding Style"
run: "composer cs-fix"

- name: "Run PHPCS"
run: composer cs-check
- name: "Static code analysis"
run: "composer psalm"
17 changes: 9 additions & 8 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,15 @@ if ($this->hasOption('queue')) {

## Writing output

All helper methods from the Symfony `SymfonyStyle` class are available in the command class.
The package provides a handy Console style that you can use to write output to the console:
<img alt="Output style" height="200" src="./output-style.png"/>

```php
$this->title('Lorem ipsum dolor sit amet');
$this->section('Adding a new user');
$this->text('Lorem ipsum dolor sit amet, consectetur adipiscing elit.');
//And so on...
$this->output->comment('This is a comment');
$this->output->success('This is a success message');
$this->output->error('This is an error message');
$this->output->warning('This is an info message');
$this->output->note('This is an info message');
$this->output->info('This is an info message');
$this->output->caution('This is a line message');
```

You can find the full list of available methods in Symfony's
documentation: [Helper methods](https://symfony.com/doc/current/console/style.html#helper-methods).
Binary file added docs/output-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
195 changes: 19 additions & 176 deletions src/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace Symblaze\Console;

use Symblaze\Console\IO\Helper\InputTrait;
use Symblaze\Console\IO\Helper\OutputTrait;
use Symblaze\Console\IO\Output;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
* @psalm-api - This file is part of the symblaze/console package.
Expand All @@ -18,16 +18,11 @@
*/
abstract class Command extends SymfonyCommand
{
protected InputInterface $input;
protected SymfonyStyle $output;
use InputTrait;
use OutputTrait;

private const VERBOSITY_MAP = [
'v' => OutputInterface::VERBOSITY_VERBOSE,
'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE,
'vvv' => OutputInterface::VERBOSITY_DEBUG,
'quiet' => OutputInterface::VERBOSITY_QUIET,
'normal' => OutputInterface::VERBOSITY_NORMAL,
];
protected InputInterface $input;
protected Output $output;

protected function configure(): void
{
Expand All @@ -40,185 +35,33 @@ protected function configure(): void

public function run(InputInterface $input, OutputInterface $output): int
{
$this->input = $input;
$this->output = StyleFactory::create($input, $output);
$this->setInput($input);
$this->setOutput(new Output($input, $output));

return parent::run($input, $output);
}

/**
* Determine if the given argument is present.
*/
protected function hasArgument($name): bool
{
return $this->input->hasArgument($name) && ! is_null($this->argument($name));
}

/**
* Determine if the given option is present.
*/
protected function hasOption($name): bool
{
return $this->input->hasOption($name) && ! is_null($this->option($name));
}

protected function option(string $key): bool|array|string|null
{
return $this->input->getOption($key);
}

protected function options(): array
{
return $this->input->getOptions();
}

/**
* Get the value of a command argument.
*/
protected function argument(string $key): bool|array|string|null
{
return $this->input->getArgument($key);
}

/**
* Get all the arguments passed to the command.
*/
protected function arguments(): array
{
return $this->input->getArguments();
}

/**
* Writes a message to the output and adds a newline at the end.
*/
protected function line(string $message, ?string $style = null, string|int $verbosity = 'normal'): void
{
$styled = $style ? "<$style>$message</$style>" : $message;

$this->output->writeln($styled, $this->parseVerbosity($verbosity));
}

protected function info(string|array $message): void
{
$this->output->info($message);
}

protected function comment(string|array $message): void
{
$this->output->comment($message);
}

protected function question(string|array $message): void
{
$this->line($message, 'question');
}

protected function error(string|array $message): void
{
$this->output->error($message);
}

protected function warning(string|array $message): void
{
$this->output->warning($message);
}

protected function success(string|array $message): void
{
$this->output->success($message);
}

protected function title(string $message): void
public function getInput(): InputInterface
{
$this->output->title($message);
return $this->input;
}

protected function section(string $message): void
public function setInput(InputInterface $input): static
{
$this->output->section($message);
}

protected function text(string|array $message): void
{
$this->output->text($message);
}

protected function listing(array $elements): void
{
$this->output->listing($elements);
}

protected function table(array $headers, array $rows): void
{
$this->output->table($headers, $rows);
}

protected function horizontalTable(array $headers, array $rows): void
{
$this->output->horizontalTable($headers, $rows);
}

protected function definitionList(string|array|TableSeparator ...$list): void
{
$this->output->definitionList(...$list);
}

protected function note(string|array $message): void
{
$this->output->note($message);
}

protected function caution(string|array $message): void
{
$this->output->caution($message);
}

protected function ask(string $question, ?string $default = null, ?callable $validator = null): mixed
{
return $this->output->ask($question, $default, $validator);
}

protected function askHidden(string $question, ?callable $validator = null): mixed
{
return $this->output->askHidden($question, $validator);
}

protected function confirm(string $question, bool $default = true): bool
{
return $this->output->confirm($question, $default);
}

protected function choice(string $question, array $choices, mixed $default = null, bool $multiSelect = false): mixed
{
return $this->output->choice($question, $choices, $default, $multiSelect);
}

protected function progressStart(int $max = 0): void
{
$this->output->progressStart($max);
}

protected function progressAdvance(int $step = 1): void
{
$this->output->progressAdvance($step);
}
$this->input = $input;

protected function progressFinish(): void
{
$this->output->progressFinish();
return $this;
}

protected function createProgressBar(int $max = 0): ProgressBar
public function getOutput(): Output
{
return $this->output->createProgressBar($max);
return $this->output;
}

private function parseVerbosity(int|string $level): int
public function setOutput(Output $output): static
{
if (is_int($level)) {
return $level;
}
$this->output = $output;

return self::VERBOSITY_MAP[$level] ?? OutputInterface::VERBOSITY_NORMAL;
return $this;
}
}
Loading

0 comments on commit 792b5a3

Please sign in to comment.