titasgailius/terminal

Terminal 是 Symfony 的 Process 组件的一个优雅包装器。

1.2.0 2022-02-26 08:49 UTC

This package is auto-updated.

Last update: 2024-08-26 14:46:26 UTC


README

Terminal

Symfony 的 Process 组件的优雅包装器。

Preview

内容

安装

composer require titasgailius/terminal

执行命令

要执行一个命令,你可以使用 run 方法。首先,让我们看看如何执行一个基本的 shell 命令。

$response = Terminal::run('rm -rf vendor');

响应

run 方法返回一个 TitasGailius\Terminal\Response 实例,它提供了一系列用于检查响应的方法

$response->getExitCode() : int;
$response->ok() : bool;
$response->successful() : bool;

$response->lines() : array;
$response->output() : string;
(string) $response: string;

输出到数组

你可以通过使用 lines 方法在一个数组中获取整个命令输出

foreach ($response->lines() as $line) {
    //
}

输出流(推荐)

如果内存消耗很重要,你可以通过在响应实例上使用 foreach 循环逐行读取整个输出

foreach ($response as $line) {
    //
}

输出行

每个 $line 项都是一个 TitasGailius\Terminal\OutputLine 对象的实例,该对象提供了一系列用于检查输出行的方法。

你可以检查输出行是否是错误

$line->error(); // true|false

你可以将此对象用作字符串以获取行的内容

(string) $line;

或者,你可以使用 content 方法来获取行的内容

$line->content();

通过 Laravel Artisan 命令输出

如果你从 Laravel 的 Artisan 命令中运行 Terminal,你可以通过将命令实例传递给 output 方法将输出发送到控制台

public function handle()
{
    Terminal::output($this)->run('echo Hello, World');
}

通过 Symfony Console 命令输出

如果你从 Symfony 的 Console 命令中运行 Terminal,你可以通过将 OutputInterface 实例传递给 output 方法将输出发送到控制台

protected function execute(InputInterface $input, OutputInterface $output)
{
    Terminal::output($output)->run('echo Hello, World');
}

抛出异常

如果你希望在命令不成功时抛出异常,你可以使用 throw 方法

$response = Terminal::run(...);

$response->throw();

return (string) $response;

在发生错误时将抛出 Symfony\Component\Process\Exception\ProcessFailedException 实例。

数据

如果你需要将任何数据传递到你的命令行,最好使用 with 方法进行绑定。Terminal 可以为你转义和准备这些值。使用 {{ $key }} 语法引用这些值。

Terminal::with([
    'firstname' => 'John',
    'lastname' => 'Doe',
])->run('echo Hello, {{ $firstname}} {{ $lastname }}');

或者,你可以将键值对作为单独的参数传递。

Terminal::with('greeting', 'World')
        ->run('echo Hello, {{ $greeting }}');

工作目录

如果你想要更改执行脚本的当前工作目录,你可以使用接受路径的 in 方法

Terminal::in(storage_path('framework'))->run('rm -rf views');

超时

如果你想要给你的命令添加一个超时,你可以使用接受秒数的整数或 DateTime、DateInterval 和 Carbon 实例的 timeout 方法

Terminal::timeout(25)->run('rm -rf vendor');

使用 DateInterval

$duration = new DateInterval('PT25S');

Terminal::timeout($duration)->run('rm -rf vendor');

使用 DateTime

$date = (new DateTime)->add(new DateInterval('PT25S'));

Terminal::timeout($date)->run('rm -rf vendor');

使用 Carbon

$date = Carbon::now()->addSeconds(25);

Terminal::timeout($date)->run('rm -rf vendor');

重试

如果你希望在出现错误时自动重试命令,你可以使用 retries 方法。该 retries 方法接受两个参数:命令应尝试的次数和尝试之间等待的毫秒数

Terminal::retries(3, 100)->run('rm -rf vendor');

环境变量

默认情况下,shell 脚本会使用与当前 PHP 进程相同的环境变量来运行。如果您想使用不同的环境变量集运行脚本,可以使用 withEnvironmentVariables 方法。该方法接受一个包含环境变量键值对的数组。

Terminal::withEnvironmentVariables([
    'APP_ENV' => 'testing',
])->run('rm -rf $DIRECTORY');

命令

在某些情况下,您可能想使用 command 方法在真正执行之前定义可执行命令。

$command = Terminal::command('rm -rf vendor');

if ($inBackground) {
    $command->inBackground();
}

$command->run();

Symfony Process

您可以通过调用 process 方法来获取 Symfony\Component\Process\Process 类的底层实例。

$process = Terminal::timeout(25)->process();

您也可以从 TitasGailius\Terminal\Response 对象中获取进程实例。

$response = Terminal::run(...);

$process = $response->process();

最后,所有对 TitasGailius\Terminal\Response 实例缺失的方法调用都会自动传递到底层进程实例。

$response = Terminal::run(...);

$response->isRunning(); // "isRunning" method is passed to the \Symfony\Component\Process\Process class

扩展

extend 方法允许您定义自定义方法。

Terminal::extend('removeVendors', function ($terminal) {
  return $terminal->run('rm -rf vendors');
});

Terminal::removeVendors();

测试

Terminal 提供了一些特殊功能,帮助您轻松且具有表现力地编写测试。Terminal 的 fake 方法允许您在执行命令时指示 Terminal 返回模拟的/虚拟的响应。

伪造响应

要指示 Terminal 对于每个执行的命令都返回空响应,您可以不带参数调用 fake 方法。

Terminal::fake();

$response = Terminal::run(...);

伪造特定命令

或者,您可以将一个数组传递给 fake 方法。数组的键应代表您希望模拟的命令及其关联的响应。

Terminal::fake([
    'php artisan inspire' => 'Simplicity is the ultimate sophistication. - Leonardo da Vinci',

    'cowsay Hi, How are you' => [
        ' _________________             ',
        '< Hi, How are you >            ',
        ' -----------------             ',
        '        \   ^__^               ',
        '         \  (oo)\_______       ',
        '            (__)\       )\/\   ',
        '                ||----w |      ',
        '                ||     ||      ',
    ],
]);

响应行

除了传递字符串或行数组外,您还可以显式指定每行类型。Terminal 的 lineerror 方法可以帮助您创建更精确的响应。

Terminal::fake([
    'wp cli update' => [
        Terminal::line('Downloading WordPress files.'),
        Terminal::error('WordPress is down.'),
    ],
]);

失败响应

模拟失败响应非常简单。将您的响应行移动到 Terminal 的 response 方法,并在其上方调用 shouldFail

Terminal::fake([
    'php artisan migrate' => Terminal::response([
        'Migrating: 2012_12_12_000000_create_users_table',
        'Migrated: 2012_12_12_000000_create_users_table',
    ])->shouldFail(),
]);

检查命令

当模拟响应时,您可能偶尔希望检查 Terminal 收到的命令,以确保您的应用程序正在执行正确的命令。

您可以通过在调用 Terminal::fake 后调用 Terminal::assertExecuted 方法来实现这一点。

Terminal::fake();

Terminal::run('php artisan migrate');

Terminal::assertExecuted('php artisan migrate');

或者,您也可以检查给定的命令是否未执行。您可以通过在调用 Terminal::fake 后调用 Terminal::assertNotExecuted 方法来实现这一点。

Terminal::fake();

Terminal::assertNotExecuted('php artisan migrate');

模拟 Symfony Process

如果您需要模拟底层的 Symfony 的 Process,您可以使用 Terminal 的 response 方法。

Terminal 的 response 方法可以使用多种方式

  1. 传递响应行和一个可选的进程实例。
  2. 只传递进程实例。
$process = Mockery::mock(Process::class, function ($mock) {
    $mock->shouldReceive('getPid')
        ->twice()
        ->andReturn(123, 321);
});

Terminal::fake([
    // Empty response with a mocked \Symfony\Component\Process\Process instance.
    'factor 12' => Terminal::response($process)

    // Response lines with a mocked \Symfony\Component\Process\Process instance.
    'php artisan migrate' => Terminal::response([
        'Migrating: 2012_12_12_000000_create_users_table',
        'Migrated: 2012_12_12_000000_create_users_table',
    ], $process),
]);

$this->assertEquals(123, Terminal::run('factor 12')->getPid());
$this->assertEquals(321, Terminal::run('php artisan migrate')->getPid());

注意事项

Terminal 使用一些静态方法来提供这些美丽的测试功能。具体来说,Terminal 将模拟响应存储在静态属性中,这意味着它们在每次测试之间不会清除。

为了避免这种情况,您可以使用 Terminal::reset 方法。最佳调用位置是 PhpUnit 的 teardown 方法。

/**
 * This method is called after each test.
 */
protected function tearDown(): void
{
    parent::tearDown();

    Terminal::reset();
}

PHP 8 支持

要使用 PHP 8.x 的 Terminal,请将 Terminal 升级到 ^1.0 版本。

  1. 更新您的 composer.json 以使用 terminal 的最新版本:"titasgailius/terminal": "^1.0"
  2. 请注意,Builder::retry 现在是一个 protected 方法。
    您不太可能使用此方法。.