In this second of a three-part series, we introduce four additional new features added by Symfony 3.2 to the Console component to improve its DX (developer experience).
Read the part 1 and part 3 of this series of articles explaining the new features of the Console component in Symfony 3.2.
Introduced a new Terminal class
Console's Application
class defines several methods to get the dimensions
(height and width) of the terminal window:
1 2 3 4 5 6
use Symfony\Component\Console\Application;
$application = new Application();
$dimensions = $application->getTerminalDimensions(); // [$width, $height]
$height = $application->getTerminalHeight();
$width = $application->getTerminalWidth();
Technically, getting this information for all kinds of terminals and operating
systems is a complex, convoluted, slow and error prone process. In Symfony 3.2
we decided to move all this logic into a new Terminal
class:
1 2 3 4
use Symfony\Component\Console\Terminal;
$height = (new Terminal())->getHeight();
$width = (new Terminal())->getWidth();
In addition, we improved the logic to get/set the terminal dimensions to
prioritize the use of environment variables. If the COLUMNS
and LINES
environment variables are defined, Terminal
uses their values to get the
dimensions. When setting the terminal dimensions, Terminal
creates or
updates the values of those variables.
This new Terminal
class will be used in the future to get/set more
information about the terminal besides its dimensions. For now, these changes
have allowed us to fix some edge cases in the progress bar helper when the
terminal window was small.
Introduced a new StreamableInputInterface
In Symfony 2.8 we introduced a new style guide for console commands that
simplifies creating consistent-looking commands. However, these commands were
hard to test, specially when using the ask()
helper to ask for user's input.
In Symfony 3.2 we've introduced a new StreamableInputInterface
and made the
abstract Symfony
implement it. This change
allows to centralize the management of the input stream in a single class and
makes the QuestionHelper
related code easier to test.
Added a hasErrored() method in ConsoleLogger
In Symfony 3.2, the ConsoleLogger
class includes a hasErrored()
method
that returns true
as soon as one message of ERROR
level has been logged.
This way you don't have to add any custom logic to decide whether your command
should return an error exit code (exit(1)
) or not.
Added a "Lockable" trait
In Symfony 2.6 we introduced a lock handler to provide a simple abstraction to lock anything by means of a file lock. This lock handler is mainly used to avoid concurrency issues preventing multiple executions of the same command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use Symfony\Component\Filesystem\LockHandler;
class UpdateContentsCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
$lock = new LockHandler('update:contents');
if (!$lock->lock()) {
// manage lock errors
}
// ...
}
}
In Symfony 3.2 we made the lock handler a bit easier to use thanks to the new
LockableTrait
. This trait provides a lock()
method that creates a non-
blocking lock named after the current command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\Component\Console\Command\LockableTrait;
class UpdateContentsCommand extends Command
{
use LockableTrait;
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$this->lock()) {
// manage lock errors
}
// ...
}
}
You can also create locks with custom names and even blocking locks that wait until any existing lock is released:
1 2 3 4
if (!$this->lock('custom_lock_name')) { ... }
// the second boolean argument tells whether the lock is blocking or not
if (!$this->lock('custom_lock_name', true)) { ... }
Wow, thanks for mentioning!
I'd like to point out, that first improvement mainly fixed this issue: https://github.com/symfony/symfony/issues/13019#issuecomment-67627942
No more ugly symfony demo app installation. Happened to me week ago on lecture... For the last time now! :)