Skip to content

Commit

Permalink
Merge pull request #12 from henriquemoody/command
Browse files Browse the repository at this point in the history
Create Command action
  • Loading branch information
henriquemoody committed Nov 19, 2014
2 parents 4b004d2 + 0fc3d3c commit a8ab2a7
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 3 deletions.
32 changes: 29 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,27 +82,53 @@ to the constructor of this class.
```php
use Arara\Process\Action\Callback;

$action = new Callback(function () {
$callback = new Callback(function () {
echo "This will be executed in the background!" . PHP_EOL;
});
```

The Callback action provides a way to bind callbacks to be triggered by specific events:

```php
$action->bind(Callback::EVENT_SUCCESS, function () {
$callback->bind(Callback::EVENT_SUCCESS, function () {
echo "This will be executed if the action callback was successful!" . PHP_EOL;
});
```

Also, one can bind a callback to multiple events:

```php
$action->bind(Callback::EVENT_ERROR | Callback::EVENT_FAILURE, function () {
$callback->bind(Callback::EVENT_ERROR | Callback::EVENT_FAILURE, function () {
echo "It is going to be executed if the action fails or get an error" . PHP_EOL;
});
```

### Command action

You may want to run just a Linux command, for that reason there is Command action.
```php
$command = new Command('whoami');
```

Using Command action you can define arguments as second param:
```php
$command = new Command('cp', array('/path/to/source', '/path/to/destination'));
```

If you prefer arguments can be defined by a key => value array:
```php
$command = new Command(
'find',
array(
'/path/to/dir',
'-name' => '*',
'-type' => 'f',
)
);
```

Command action is based on Callback action so you can also bind triggers for events.

### Daemon action

You can create daemons using the `Arara\Process\Action\Daemon` class:
Expand Down
30 changes: 30 additions & 0 deletions examples/command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(ticks=1);

require_once __DIR__ . '/../vendor/autoload.php';

use Arara\Process\Action\Command;
use Arara\Process\Child;
use Arara\Process\Context;
use Arara\Process\Control;

try {
$command = new Command('find', array(__DIR__, '-name' => '*', '-type' => 'f'));
$command->bind(Command::EVENT_FINISH, function (Control $control, Context $context) {
$flags = JSON_UNESCAPED_SLASHES;
if (defined('JSON_PRETTY_PRINT')) {
$flags = ($flags | JSON_PRETTY_PRINT);
}

echo json_encode($context->toArray(), $flags) . PHP_EOL;
});

$child = new Child($command, new Control());
$child->start();
$child->wait();

} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL;
echo $e->getTraceAsString() . PHP_EOL;
}
91 changes: 91 additions & 0 deletions src/Arara/Process/Action/Command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

namespace Arara\Process\Action;

use Arara\Process\Context;
use Arara\Process\Control;
use RuntimeException;

/**
* Handle shell commands.
*/
class Command extends Callback
{
/**
* @var string
*/
protected $command;

/**
* @var array
*/
protected $arguments = array();

/**
* @param string $command
* @param array[optional] $arguments
* @param boolean $prefix
*/
public function __construct($command, array $arguments = array(), $prefix = true)
{
$this->command = $prefix ? '/usr/bin/env ' . $command : $command;
$this->arguments = $arguments;
}

/**
* Gets the value of command.
*
* @return string
*/
public function getCommand()
{
return $this->command;
}

/**
* Gets the value of arguments.
*
* @return array
*/
public function getArguments()
{
return $this->arguments;
}

/**
* Assemble command and arguments.
*
* @return string
*/
protected function assemble()
{
$assembled = $this->command;
foreach ($this->arguments as $key => $value) {
if (is_int($key)
&& false === strpos((string) $key, '-')) {
$assembled .= ' ' . escapeshellarg($value);
continue;
}
$assembled .= ' ' . escapeshellarg($key);
$assembled .= ' ' . escapeshellarg($value);
}

return $assembled;
}

/**
* {@inheritDoc}
*/
public function execute(Control $control, Context $context)
{
$context->command = $this->assemble();
$context->outputTail = exec(sprintf('(%s)2>&1', $context->command), $outputLines, $returnValue);
$context->outputString = implode(PHP_EOL, $outputLines);
$context->outputLines = $outputLines;
$context->returnValue = $returnValue;

if ($returnValue > 0) {
throw new \RuntimeException($context->outputTail);
}
}
}
5 changes: 5 additions & 0 deletions src/Arara/Process/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
* @property resource $stdout
* @property resource $stderr
* @property Pidfile $pidfile
* @property string $command
* @property string $outputTail
* @property string $outputString
* @property array $outputLines
* @property int $returnValue
*/
class Context
{
Expand Down
201 changes: 201 additions & 0 deletions tests/Arara/Process/Action/CommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php

namespace Arara\Process\Action;

function exec($command, &$output = null, &$return_var = null)
{
$args = func_get_args();
$return = null;

$GLOBALS['arara']['exec']['args'] = $args;

if (array_key_exists('output', $GLOBALS['arara']['exec'])) {
$output = $GLOBALS['arara']['exec']['output'];
$return = end($output);
} else {
\exec($command, $output, $return_var);
}

if (isset($GLOBALS['arara']['exec']['return_var'])) {
$return_var = $GLOBALS['arara']['exec']['return_var'];
}

return $return;
}

use Arara\Process\Context;
use Arara\Process\Control;

/**
* @covers Arara\Process\Action\Command
*/
class CommandTest extends \TestCase
{
protected function init()
{
$GLOBALS['arara']['exec']['return'] = null;
$GLOBALS['arara']['exec']['output'] = array('');
$GLOBALS['arara']['exec']['return_var'] = 0;
}

protected function finish()
{
unset($GLOBALS['arara']['exec']);
}

public function testShouldAcceptCommandAndPrefixItOnConstructor()
{
$action = new Command('whoami');

$actualCommand = $action->getCommand();
$expectedCommand = '/usr/bin/env whoami';

$this->assertEquals($expectedCommand, $actualCommand);
}

public function testShouldAcceptArgumentsOnConstructor()
{
$arguments = array(__DIR__, '-name' => '*');
$action = new Command('find', $arguments);

$this->assertEquals($arguments, $action->getArguments());
}

public function testShouldRemoveCommandPrefixOnConstructor()
{
$action = new Command('echo', array(), false);

$actualCommand = $action->getCommand();
$expectedCommand = 'echo';

$this->assertEquals($expectedCommand, $actualCommand);
}

public function testShouldAssembleCommandAndArguments()
{
$control = new Control();
$context = new Context();
$action = new Command('echo', array('Arara', 'Process'));
$action->execute($control, $context);

$actualCommand = $context->command;
$expectedCommand = "/usr/bin/env echo 'Arara' 'Process'";

$this->assertEquals($expectedCommand, $actualCommand);
}

public function testShouldAssembleCommandAndKeyValueArguments()
{
$control = new Control();
$context = new Context();
$action = new Command('echo', array('-n' => 'Process'));
$action->execute($control, $context);

$actualCommand = $context->command;
$expectedCommand = "/usr/bin/env echo '-n' 'Process'";

$this->assertEquals($expectedCommand, $actualCommand);
}

public function testShouldNotAssembleWithEnvCommand()
{
$control = new Control();
$context = new Context();
$prefixEnv = false;
$action = new Command('echo', array('Arara\Process'), $prefixEnv);
$action->execute($control, $context);

$actualCommand = $context->command;
$expectedCommand = "echo 'Arara\Process'";

$this->assertEquals($expectedCommand, $actualCommand);
}

public function testShouldExecuteCommandAndRedirectStderr()
{
$control = new Control();
$context = new Context();
$action = new Command('echo', array('Arara\Process'), false);
$action->execute($control, $context);

$actualCommand = $GLOBALS['arara']['exec']['args'][0];
$expectedCommand = "(echo 'Arara\Process')2>&1";

$this->assertEquals($expectedCommand, $actualCommand);
}

/**
* @expectedException RuntimeException
* @expectedExceptionMessage An error has occurred
*/
public function testShouldThowsAnExceptionWhenCommandGetError()
{
$GLOBALS['arara']['exec']['output'] = array('echo', 'An error has occurred');
$GLOBALS['arara']['exec']['return_var'] = 1;

$control = new Control();
$context = new Context();
$action = new Command('echo', array('Arara\nProcess'), false);
$action->execute($control, $context);
}

public function testShouldStoreLastOutputLine()
{
$GLOBALS['arara']['exec']['output'] = array('Arara', 'Process');

$control = new Control();
$context = new Context();
$action = new Command('echo', array('Arara\nProcess'));
$action->execute($control, $context);

$actualTail = $context->outputTail;
$expectedTail = 'Process';

$this->assertEquals($expectedTail, $actualTail);
}

public function testShouldStoreOutputLines()
{
$GLOBALS['arara']['exec']['output'] = array('Arara', 'Process');

$control = new Control();
$context = new Context();
$action = new Command('echo', array('Arara\nProcess'));
$action->execute($control, $context);

$actualLines = $context->outputLines;
$expectedLines = $GLOBALS['arara']['exec']['output'];

$this->assertEquals($expectedLines, $actualLines);
}

public function testShouldStoreOutputString()
{
$GLOBALS['arara']['exec']['output'] = array('Arara', 'Process');

$control = new Control();
$context = new Context();
$action = new Command('echo', array('Arara\nProcess'));
$action->execute($control, $context);

$actualString = $context->outputString;
$expectedString = 'Arara' . PHP_EOL . 'Process';

$this->assertEquals($expectedString, $actualString);
}

public function testShouldStoreReturnValue()
{
$GLOBALS['arara']['exec']['return_var'] = 0;

$control = new Control();
$context = new Context();
$action = new Command('echo', array('Arara\nProcess'));
$action->execute($control, $context);

$actualReturn = $context->returnValue;
$expectedReturn = 0;

$this->assertEquals($expectedReturn, $actualReturn);
}
}

0 comments on commit a8ab2a7

Please sign in to comment.