8000 merged branch Seldaek/argv-tostring (PR #7657) · symfony/symfony@0c88f22 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0c88f22

Browse files
committed
merged branch Seldaek/argv-tostring (PR #7657)
This PR was merged into the master branch. Discussion ---------- [Console] Input::__toString escaping fixes Follow up to #7648, also includes a fix for StringInput to parse newlines and other whitespace chars properly instead of normalizing them all to spaces. It was kinda needed to test it properly, so I bundled both in one. Commits ------- 93b1369 [Console] Fix StringInput parsing to accept newlines and tabs 8642b67 [Console] Fix escaping of args
2 parents 6cf491d + 93b1369 commit 0c88f22

File tree

7 files changed

+27
-20
lines changed

7 files changed

+27
-20
lines changed

src/Symfony/Component/Console/Input/ArgvInput.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,10 @@ public function getParameterOption($values, $default = false)
321321
public function __toString()
322322
{
323323
$tokens = array_map(function ($token) {
324-
$token = addcslashes($token, '"');
325-
if (false !== strpos($token, ' ')) {
326-
return '"'.$token.'"';
324+
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
325+
return $match[1] . $this->escapeToken($match[2]);
326+
} elseif ($token && $token[0] !== '-') {
327+
return $this->escapeToken($token);
327328
}
328329

329330
return $token;

src/Symfony/Component/Console/Input/ArrayInput.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,10 @@ public function __toString()
119119
{
120120
$params = array();
121121
foreach ($this->parameters as $param => $val) {
122-
$val = addcslashes($val, '"');
123-
if (false !== strpos($val, ' ')) {
124-
$val = '"'.$val.'"';
125-
}
126122
if ($param && '-' === $param[0]) {
127-
$params[] = $param . ('' != $val ? ' '.$val : $val);
123+
$params[] = $param . ('' != $val ? '='.$this->escapeToken($val) : '');
128124
} else {
129-
$params[] = $val;
125+
$params[] = $this->escapeToken($val);
130126
}
131127
}
132128

src/Symfony/Component/Console/Input/Input.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,14 @@ public function hasOption($name)
210210
{
211211
return $this->definition->hasOption($name);
212212
}
213+
214+
/**
215+
* Escapes a token through escapeshellarg if it contains unsafe chars
216+
*
217+
* @return string
218+
*/
219+
protected function escapeToken($token)
220+
{
221+
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
222+
}
213223
}

src/Symfony/Component/Console/Input/StringInput.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*/
2525
class StringInput extends ArgvInput
2626
{
27-
const REGEX_STRING = '([^ ]+?)(?: |(?<!\\\\)"|(?<!\\\\)\'|$)';
27+
const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
2828
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
2929

3030
/**
@@ -59,14 +59,12 @@ public function __construct($input, InputDefinition $definition = null)
5959
*/
6060
private function tokenize($input)
6161
{
62-
$input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input);
63-
6462
$tokens = array();
6563
$length = strlen($input);
6664
$cursor = 0;
6765
while ($cursor < $length) {
6866
if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
69-
} elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
67+
} elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
7068
$tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
7169
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
7270
$tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));

src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ public function testToString()
260260
$input = new ArgvInput(array('cli.php', '-f', 'foo'));
261261
$this->assertEquals('-f foo', (string) $input);
262262

263-
$input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d'));
264-
$this->assertEquals('-f --bar=foo "a b c d"', (string) $input);
263+
$input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C"));
264+
$this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input);
265265
}
266266

267267
/**

src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public function provideInvalidInput()
123123

124124
public function testToString()
125125
{
126-
$input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo'));
127-
$this->assertEquals('-f -b bar --foo "b a z" --lala Foo', (string) $input);
126+
$input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C"));
127+
$this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input);
128128
}
129129
}

src/Symfony/Component/Console/Tests/Input/StringInputTest.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public function getTokenizeData()
5353
array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'),
5454
array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'),
5555
array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'),
56+
array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'),
57+
array("'a'\r'b'\n'c'\t'd'", array('a','b','c','d'), '->tokenize() parses whitespace chars between args as spaces'),
5658
array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'),
5759
array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'),
5860
array('-a', array('-a'), '->tokenize() parses short options'),
@@ -80,9 +82,9 @@ public function testToString()
8082
$this->assertEquals('-f foo', (string) $input);
8183

8284
$input = new StringInput('-f --bar=foo "a b c d"');
83-
$this->assertEquals('-f --bar=foo "a b c d"', (string) $input);
85+
$this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input);
8486

85-
$input = new StringInput('-f --bar=foo \'a b c d\'');
86-
$this->assertEquals('-f --bar=foo "a b c d"', (string) $input);
87+
$input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'");
88+
$this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input);
8789
}
8890
}

0 commit comments

Comments
 (0)
0