Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bd8ad3f

Browse files
committed
feature #32126 [Process] Allow writing portable "prepared" command lines (Simperfit)
This PR was merged into the 4.4 branch. Discussion ---------- [Process] Allow writing portable "prepared" command lines | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | #23778 <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | symfony/symfony-docs#11802 <!-- required for new features --> Hey here, it's me, again ! I've talked with @nicolas-grekas and he gave me a new way of writing this feature that will not be a problem for people using things like {{ toto }} since we are using the linux style of using envvar, the replaced arguments is only replaced when using windows. This should not break anything and work as expected! see #24763 This makes `"$FOO"` work seamlessly on Linux and on Windows to reference an env var (that can be provided when calling e.g. the "run" method) Commits ------- 3f8354f [Process] Allow writing portable "prepared" command lines
2 parents f15722d + 3f8354f commit bd8ad3f

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-5
lines changed

src/Symfony/Component/Process/Process.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,20 +291,23 @@ public function start(callable $callback = null, array $env = [])
291291
$this->hasCallback = null !== $callback;
292292
$descriptors = $this->getDescriptors();
293293

294+
if ($this->env) {
295+
$env += $this->env;
296+
}
297+
298+
$env += $this->getDefaultEnv();
299+
294300
if (\is_array($commandline = $this->commandline)) {
295301
$commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));
296302

297303
if ('\\' !== \DIRECTORY_SEPARATOR) {
298304
// exec is mandatory to deal with sending a signal to the process
299305
$commandline = 'exec '.$commandline;
300306
}
307+
} else {
308+
$commandline = $this->replacePlaceholders($commandline, $env);
301309
}
302310

303-
if ($this->env) {
304-
$env += $this->env;
305-
}
306-
$env += $this->getDefaultEnv();
307-
308311
$options = ['suppress_errors' => true];
309312

310313
if ('\\' === \DIRECTORY_SEPARATOR) {
@@ -1632,6 +1635,17 @@ private function escapeArgument(?string $argument): string
16321635
return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"';
16331636
}
16341637

1638+
private function replacePlaceholders(string $commandline, array $env)
1639+
{
1640+
return preg_replace_callback('/"\$([_a-zA-Z]++[_a-zA-Z0-9]*+)"/', function ($matches) use ($commandline, $env) {
1641+
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
1642+
throw new InvalidArgumentException(sprintf('Command line is missing a value for key %s: %s.', $matches[0], $commandline));
1643+
}
1644+
1645+
return '\\' === \DIRECTORY_SEPARATOR ? $this->escapeArgument($env[$matches[1]]) : $matches[0];
1646+
}, $commandline);
1647+
}
1648+
16351649
private function getDefaultEnv()
16361650
{
16371651
$env = [];

src/Symfony/Component/Process/Tests/ProcessTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,50 @@ public function provideEscapeArgument()
15001500
yield [1.1];
15011501
}
15021502

1503+
public function testPreparedCommand()
1504+
{
1505+
$p = Process::fromShellCommandline('echo "$abc"DEF');
1506+
$p->run(null, ['abc' => 'ABC']);
1507+
1508+
$this->assertSame('ABCDEF', rtrim($p->getOutput()));
1509+
}
1510+
1511+
public function testPreparedCommandMulti()
1512+
{
1513+
$p = Process::fromShellCommandline('echo "$abc""$def"');
1514+
$p->run(null, ['abc' => 'ABC', 'def' => 'DEF']);
1515+
1516+
$this->assertSame('ABCDEF', rtrim($p->getOutput()));
1517+
}
1518+
1519+
public function testPreparedCommandWithQuoteInIt()
1520+
{
1521+
$p = Process::fromShellCommandline('php -r "$code" "$def"');
1522+
$p->run(null, ['code' => 'echo $argv[1];', 'def' => '"DEF"']);
1523+
1524+
$this->assertSame('"DEF"', rtrim($p->getOutput()));
1525+
}
1526+
1527+
/**
1528+
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
1529+
* @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc".
1530+
*/
1531+
public function testPreparedCommandWithMissingValue()
1532+
{
1533+
$p = Process::fromShellCommandline('echo "$abc"');
1534+
$p->run(null, ['bcd' => 'BCD']);
1535+
}
1536+
1537+
/**
1538+
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
1539+
* @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc".
1540+
*/
1541+
public function testPreparedCommandWithNoValues()
1542+
{
1543+
$p = Process::fromShellCommandline('echo "$abc"');
1544+
$p->run(null, []);
1545+
}
1546+
15031547
public function testEnvArgument()
15041548
{
15051549
$env = ['FOO' => 'Foo', 'BAR' => 'Bar'];

0 commit comments

Comments
 (0)