在本系列文章的第二部分,我们介绍Symfony 3.2中针对Console Component的“DX/开发者体验改进(developer experience)”而新增的四个功能。

引入一个全新的Terminal类 

命令行的Application方法定义了几个方法,用于取得终端窗口的维度(高和宽):

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();

从技术角度讲,要得到能够对应全部终端和操作系统的这个信息,会是一个复杂、缠绕、缓慢、易错的过程。在Symfony 3.2中我们决定把所有这些逻辑转移到一个全新的Terminal类中:

1
2
3
4
use Symfony\Component\Console\Terminal;
 
$height = (new Terminal())->getHeight();
$width = (new Terminal())->getWidth();

除此之外,我们还改进了获取/设置终端维度(Terminal Dimensions)的逻辑,以优先使用环境变量。如果COLUMNLINES环境变量被定义,Terminal使用它们的值来获取维度信息。当设置终端维度时,Termail实例将创建或更新那些变量的值。

这个新的Terminal类将被用于获取/设置除了维度之外的其他终端信息。目前,这些改进已经让我们修复了一些关于“当终端窗口过小时,进度条帮助方法(progress bar helper)所出现的极端情况”。

引入一个全新的StreamableInputInterface接口 

在Symfony 2.8中我们引入了一个全新的Console命令行样式美化,简化了创建“外观一致”的命令的过程。但是,这些命令难于测试,特别是当使用ask()帮助方法来请求用户的输入时。

在Symfony 3.2中,我们引入了全新的StreamableInputInterface,并且让抽象类Symfony\Component\Console\Input\Input实现该接口。这个改变,令得对“输入流(input stream)”的中心化管理,可以在一个单一的类中完成,同时令QuestionHelper相关代码更加易于测试。

为ConsoleLogger添加了一个hasErrored()方法 

Symfony 3.2中,ConsoleLogger类包括了一个hasErrored()方法,当一条ERROR级别的信息被记录下来之后它立即返回true。这样一来,你就不必再添加任何自定义逻辑,用于决定你的命令是否应该返回一个error exit code(exit(1),错误退出代码)。

添加了一个“Lockable(可锁定的)”trait 

在Symfony 2.6中我们引入了一个lock handler,用于提供一个简单的抽象层,借助文件锁(file lock)来锁定任何东西。这个lock handler(锁定控制器)主要用于避免并发问题以防止对同一命令的多次执行:

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 管理锁定时出现的错误
        }
 
        // ...
    }
}

在Symfony 3.2中,得益于全新的LockableTrait,我们把这个锁定控制器变得简单易用了些。这个trait提供了一个lock()方法,用于创建一个在“当前命令之后”被命名的非防止锁定(non-blocking lock):

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 管理锁定时出现的错误
         }
 
        // ...
    }
}

你也可以创建自定义名字的锁定,甚至能在“既有的锁定被释放之前”防止锁定。

1
2
3
4
5
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)) { ... }