An Elegant wrapper around Symfony's Process component.
- Installation
- Executing Commands
- Response
- Data
- Working Directory
- Timeout
- Retries
- Environment Variables
- Command
- Symfony Process
- Testing
composer require titasgailius/terminal
To execute a command, you may use the run
method. First, let's examine how to execute a basic shell command.
$response = Terminal::run('rm -rf vendor');
The run
method returns an instance of TitasGailius\Terminal\Response
, which provides a variety of methods that may be used to inspect the response:
$response->code() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->lines() : array;
$response->output() : string;
(string) $response: string;
You may get the entire command output on a single array by using lines
method:
foreach ($response->lines() as $line) {
//
}
If memory consumption is important, you may read the entire output line by line, using a foreach
loop on the response instance:
foreach ($response as $line) {
//
}
Every $line
item is an instance of TitasGailius\Terminal\OutputLine
object,
which provides a variety of methods that may be used to inspect the output line.
You may inspect if the output line is an error:
$line->error(); // true|false
And you may use this object as a string to get the contents of the line:
(string) $line;
Alternatively, you may use the content
method to get the contents of the line:
$line->content();
If you would like to throw an exception when the command is not successful, you may use the throw
method:
$response = Terminal::run(...);
$response->throw();
return (string) $response;
An instance of Symfony\Component\Process\Exception\ProcessFailedException
will be thrown on error.
If you need to pass any data to your command line, it's better to bind it using the with
method.
Terminal can escape and prepare the values for you. Reference these values using the {{ $key }}
syntax.
Terminal::with([
'firstname' => 'John',
'lastname' => 'Doe',
])->run('echo Hello, {{ $firstname}} {{ $lastname }}');
Alternatively, you may pass the key-value pair in separate parameters.
Terminal::with('greeting', 'World')
->run('echo Hello, {{ $greeting }}');
If you would like to change the current working directory from which
the script is executed, you may use the in
method that accepts a path:
Terminal::in(storage_path('framework'))->run('rm -rf views');
If you would like to add a timeout to your command, you may use the timeout
method that accepts an integer in seconds or an instance of DateTime, DateInterval and Carbon:
Terminal::timeout(25)->run('rm -rf vendor');
Using DateInterval
:
$duration = new DateInterval('PT25S');
Terminal::timeout($duration)->run('rm -rf vendor');
Using DateTime
:
$date = (new DateTime)->add(new DateInterval('PT25S'));
Terminal::timeout($date)->run('rm -rf vendor');
Using Carbon
:
$date = Carbon::now()->addSeconds(25);
Terminal::timeout($date)->run('rm -rf vendor');
If you would like to automatically retry a command when an error occurs, you may use the retries
method.
The retries
method accepts two arguments: the number of times the command should be attempted
and the number of milliseconds that should wait in between attempts:
Terminal::retries(3, 100)->run('rm -rf vendor');
By default, the shell script is run with the same environment variables as the current PHP process.
If you would like to run a script with a different set of environment variables, you may use the withEnvironmentVariables
method.
The withEnvironmentVariables
method accepts an array with key-value pairs of the environment variables.
Terminal::withEnvironmentVariables([
'APP_ENV' => 'testing',
])->run('rm -rf $DIRECTORY');
In some situations, you might want to use the command
method to define the executable command before actually executing it.
$command = Terminal::command('rm -rf vendor');
if ($inBackground) {
$command->inBackground();
}
$command->run();
You may get an underlying instance of Symfony\Component\Process\Process
class by calling process
method.
$process = Terminal::timeout(25)->process();
You may also get the process instance from the TitasGailius\Terminal\Response
object.
$response = Terminal::run(...);
$process = $response->process();
Lastly, all missing method calls to the TitasGailius\Terminal\Response
instance are passed to the underlying process instance automatically.
$response = Terminal::run(...);
$response->isRunning(); // "isRunning" method is passed to the \Symfony\Component\Process\Process class
The extend
methos allows you to define custom methods.
Terminal::extend('removeVendors', function ($terminal) {
return $terminal->run('rm -rf vendors');
});
Terminal::removeVendors();
Terminal has some special features to help you easily and expressively write tests. Terminal's fake method allows you to instruct the Terminal to return stubbed / dummy response when commands are executed.
To instruct the Terminal to return empty responses for every executed command, you may call the fake
method with no arguments:
Terminal::fake();
$resposne = Terminal::run(...);
Alternatively, you may pass an array to the fake method. The array's keys should represent the commands that you wish to fake and their associated responses.
Terminal::fake([
'php artisan inspire' => 'Simplicity is the ultimate sophistication. - Leonardo da Vinci',
'cowsay Hi, How are you' => [
' _________________ ',
'< Hi, How are you > ',
' ----------------- ',
' \ ^__^ ',
' \ (oo)\_______ ',
' (__)\ )\/\ ',
' ||----w | ',
' || || ',
],
]);
Besides passing a string or an array of lines, you may explicitly specify the type of each line.
Terminal has line
and error
methods that help you create more accurate responses.
Terminal::fake([
'wp cli update' => [
Terminal::line('Downloading WordPress files.'),
Terminal::error('WordPress is down.'),
],
]);
It is very simple to stub a failed response.
Move your response line(s) to the Terminal's response
method and call shouldFail
on top of that.
Terminal::fake([
'php artisan migrate' => Terminal::response([
'Migrating: 2012_12_12_000000_create_users_table',
'Migrated: 2012_12_12_000000_create_users_table',
])->shouldFail(),
]);
When faking responses, you may occasionally wish to inspect the commands the Terminal receives in order to make sure your application is executing the correct commmands.
You may accomplish this by calling the Terminal::assertExecuted
method after calling Terminal::fake
.
Terminal::fake();
Terminal::run('php artisan migrate');
Terminal::assertExecuted('php artisan migrate');
If you need to mock the underlying Symfony's Process, you may use the Terminal's response
method.
Terminal's response
method may be used in several ways:
- Passing response line(s) and an optional process instance.
- Passing only the process instance.
$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 is using some static methods to provide these beautiful testing features. Specifically, Terminal stores the fake responses on a static property, which means they do not get cleared between each test.
To prevent this you may use the Terminal::reset
method.
The best place to call it is from the PhpUnit's teardown
method.
/**
* This method is called after each test.
*/
protected function tearDown(): void
{
parent::tearDown();
Terminal::reset();
}