Skip to content

Commit 9af8afd

Browse files
committed
Use escapeshellarg
Use escapeshellarg by default, but allow disabling it.
1 parent fbf162a commit 9af8afd

File tree

3 files changed

+57
-31
lines changed

3 files changed

+57
-31
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ or
6464
}
6565
```
6666

67+
By default, all arguments are escaped using
68+
[escapeshellarg](https://secure.php.net/manual/en/function.escapeshellarg.php).
69+
If you need to pass unescaped arguments, use `{!name!}`, like so:
70+
71+
```php
72+
Command::exec('echo {!path!}', ['path' => '$PATH']);
73+
```
74+
6775
## Testing
6876

6977
``` bash

src/Command.php

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@ private function __construct()
1313
/**
1414
* Execute command with params.
1515
*
16-
* @param string $commandLine
16+
* @param string $command
1717
* @param array $params
1818
*
1919
* @return bool|string
2020
*
2121
* @throws \Exception
2222
*/
23-
public static function exec($commandLine, array $params = array())
23+
public static function exec($command, array $params = array())
2424
{
25-
if (empty($commandLine)) {
25+
if (empty($command)) {
2626
throw new \Exception('Command line is empty');
2727
}
2828

29-
$commandLine = self::bindParams($commandLine, $params);
29+
$command = self::bindParams($command, $params);
3030

31-
exec($commandLine, $output, $code);
31+
exec($command, $output, $code);
3232

3333
if (count($output) === 0) {
3434
$output = $code;
@@ -37,7 +37,7 @@ public static function exec($commandLine, array $params = array())
3737
}
3838

3939
if ($code !== 0) {
40-
throw new \Exception($output . ' Command line: ' . $commandLine);
40+
throw new \Exception($output . ' Command line: ' . $command);
4141
}
4242

4343
return $output;
@@ -46,38 +46,26 @@ public static function exec($commandLine, array $params = array())
4646
/**
4747
* Bind params to command.
4848
*
49-
* @param string $commandLine
49+
* @param string $command
5050
* @param array $params
5151
*
5252
* @return string
5353
*/
54-
public static function bindParams($commandLine, array $params)
54+
public static function bindParams($command, array $params)
5555
{
56-
if (count($params) > 0) {
57-
$wrapper = function ($string) {
58-
return '{' . $string . '}';
59-
};
60-
$converter = function ($var) {
61-
if (is_array($var)) {
62-
$var = implode(' ', $var);
63-
}
64-
65-
return $var;
66-
};
56+
$wrappers = array();
57+
$converters = array();
58+
foreach ($params as $key => $value) {
6759

68-
$commandLine = str_replace(
69-
array_map(
70-
$wrapper,
71-
array_keys($params)
72-
),
73-
array_map(
74-
$converter,
75-
array_values($params)
76-
),
77-
$commandLine
78-
);
60+
// Escaped
61+
$wrappers[] = '{' . $key . '}';
62+
$converters[] = escapeshellarg(is_array($value) ? implode(' ', $value) : $value);
63+
64+
// Unescaped
65+
$wrappers[] = '{!' . $key . '!}';
66+
$converters[] = is_array($value) ? implode(' ', $value) : $value;
7967
}
8068

81-
return $commandLine;
69+
return str_replace($wrappers, $converters, $command);
8270
}
8371
}

tests/CommandTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,34 @@ public function testExecEmptyCommand()
4949
''
5050
);
5151
}
52+
53+
/**
54+
* Test that arguments are escaped by default
55+
*/
56+
public function testArgumentsEscapedByDefault()
57+
{
58+
$output = Command::exec(
59+
'echo {phrase}',
60+
[
61+
'phrase' => 'hello $PATH',
62+
]
63+
);
64+
65+
$this->assertEquals('hello $PATH', $output);
66+
}
67+
68+
/**
69+
* Test that unescaped arguments can be passed
70+
*/
71+
public function testUnescapedArguments()
72+
{
73+
$output = Command::exec(
74+
'echo {!phrase!}',
75+
[
76+
'phrase' => 'hello $PATH',
77+
]
78+
);
79+
80+
$this->assertRegexp('/\//', $output);
81+
}
5282
}

0 commit comments

Comments
 (0)