Two Reasons You Should Not Use PHP
As a PHP developer I still remember the days of PHP4, the first (optional!) GC implementation in PHP5, and I lived through the _hype_ of the advent of PHP7 and...
This article was created with the aim to show you the most useful and retrieving tips and tricks about Symfony Console Development.
If you are a PHP developer that wants to bring your PHP software development to the next level this article is for you. Without further ado let’s cut to the chase.
You often hear:
PHP is only used to make web pages
This is completely untrue because nowadays PHP development is used in many different business areas, often not so trivial at first sight.
Both the PHP language and its environment perfectly support HTTP communication which, together with the use of the CLI environment, makes it possible to quickly create web bots, web crawlers or tools that synchronize data in external environments.
To support this I have some statistics that show that the Console component of the Symfony framework, which allows easy access to the command line, is in the TOP5 most used and downloaded Symfony packages of all time.
At this point, we’d like to share with you a few tricks that every PHP developer should know when writing code in PHP that is supposed to work in the CLI.
Table rendering with the ability to choose entries from can be achieved in an easy way using Symfony Command:
// src/App/Command/TestCommand.php
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title("Interactive selection table example");
$table = $io->createTable();
$table->setHeaderTitle("Interactive selection table example");
$table->setRows(iterator_to_array($this->tagsTableRows()));
$table->render();
$io->newLine();
return Command::SUCCESS;
}
In above example we’re taking advantage of PHP Generators as source of data – which helps with scalability in future. Simple data generator for above example:
protected function tagsTableRows(): Generator
{
$apiTagsResponse = $this->someInjectedService->getTags();
foreach ($apiTagsResponse as $apiTagResponse) {
yield [ $apiTagResponse->getName(), $apiTagResponse->getId() ];
}
}
The end result is a Table rendered in CLI.
To make it interactive we need to use QuestionHelper, provided as wrapper around SymfonyStyle
output.
(...)
$choice = new ChoiceQuestion(
question: 'Which selection you choose?',
choices: array_reduce(
array: $rows,
callback: function($carry, $item) {
$carry[] = $item[0];
return $carry;
}
)
);
$answer = $io->askQuestion($choice);
(...)
As result, we’re getting an interactive choice field with a table display from the same source.
Hiding prompt while providing sensitive data is a common practive and it’s also easily achievable in Symfony Console.
You can use built-in wrapper in SymfonyStyle
class by the following example:
(...)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title("Asking user for sensitive data");
$io->askHidden(
question: "Provide your API key here",
validator: function($answer) { return (42 == strlen($answer)) ?? $answer; }
);
return Command::SUCCESS;
}
To communicate some progress to the user we can use Progress Bars. Symfony Console has a great way to show progress to the user, but always remember to style your progress output correctly. You can have full controler over different parts and how they’re rendered using Formatter
. The progress bar format just a string of different placeholders. The available placeholders are: current
, max
, bar
, percent
, elapsed
, remaining
, estimated
, memory
and message
. Fiddle with them with the example below.
(...)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title("Styled progress bar example");
$progressBar = new ProgressBar($io, 10000);
$progressBar->setFormat('<comment>%current%/%max% [%bar%] %percent:3s%%</comment>
<info>%elapsed:6s%/%estimated:-6s%</info> <error>%memory:6s%</error>');
$progressBar->start();
for ($i = 0; $i < 10000; $i++) {
$progressBar->advance();
usleep(420);
}
$progressBar->finish();
$io->newLine();
return Command::SUCCESS;
}
You can use almost anything as and formatter as long as your terminal is capable of displaying it. Official Symfony documentation
We’re used to console suggestion, autocompletion features, and such in our development life. If you’re creating console application you should consider adding it so that your users will not be confused about how to use your low-level CLI solution. PHP Symfony Console can provide that too, out of the box, using CompletionInput
class.
(...)
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor(argumentName: 'someArgument')) {
$suggestions->suggestValues(['someSuggestion', 'otherSuggestion']);
}
}
Symfony 5.1 introduced a new way of handling cursor position in CLI applications. Since then it’s possible to read and write at a specific place at screen using handy Cursor
class:
(...)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title("Cursor example");
$cursor = new Cursor($io);
$cursor->clearScreen();
for ($x = 0; $x <= 10; $x++) {
for ($y = 0; $y <= 10; $y++) {
$cursor->moveToPosition($x, $y);
if ($y === $x) {
$io->write(".");
}
}
}
$io->write("Hello, World!");
$io->newLine();
return Command::SUCCESS;
}
This short list of tricks is just the tip of the iceberg. The possibilities of Symfony Console are endless, as evidenced by the numerous projects like Psalm, PHPStan or Composer that are based on PHP and used by millions of PHP developers around the world.