diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 3d9a3374f9..fc6e8a06b0 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -258,6 +258,10 @@ class Process implements \IteratorAggregate $this->hasCallback = null !== $callback; $descriptors = $this->getDescriptors(); + if ($this->env) { + $env += $this->env; + } + if (is_array($commandline = $this->commandline)) { $commandline = implode(' ', array_map(array($this, 'escapeArgument'), $commandline)); @@ -265,11 +269,10 @@ class Process implements \IteratorAggregate // exec is mandatory to deal with sending a signal to the process $commandline = 'exec '.$commandline; } + } else { + $commandline = $this->replacePlaceholders($commandline, $env); } - if ($this->env) { - $env += $this->env; - } $env += $this->getDefaultEnv(); $options = array('suppress_errors' => true); @@ -1549,6 +1552,29 @@ class Process implements \IteratorAggregate return '"'.str_replace(array('"', '^', '%', '!', "\n"), array('""', '"^^"', '"^%"', '"^!"', '!LF!'), $argument).'"'; } + private function replacePlaceholders(string $commandline, array $env) + { + $pattern = '\\' === DIRECTORY_SEPARATOR ? '!%s!' : '${%s}'; + + return preg_replace_callback('/\{\{ ?([_a-zA-Z0-9]++) ?\}\}/', function ($m) use ($pattern, $commandline, $env) { + if (!isset($env[$m[1]]) || false === $env[$m[1]]) { + foreach ($env as $k => $v) { + if (false === $v) { + unset($env[$k]); + } + } + if (!$env) { + throw new InvalidArgumentException(sprintf('Invalid command line "%s": no values provided for any placeholders.', $commandline)); + } + $env = implode('", "', array_keys($env)); + + throw new InvalidArgumentException(sprintf('Invalid command line "%s": no value provided for placeholder "%s", did you mean "%s"?', $commandline, $m[1], $env)); + } + + return sprintf($pattern, $m[1]); + }, $commandline); + } + private function getDefaultEnv() { $env = array(); diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index 9d36d247c5..2acf3fadec 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -1474,6 +1474,34 @@ EOTXT; yield array('éÉèÈàÀöä'); } + public function testPreparedCommand() + { + $p = new Process('echo {{ abc }}DEF'); + $p->run(null, array('abc' => 'ABC')); + + $this->assertSame('ABCDEF', rtrim($p->getOutput())); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid command line "echo {{ abc }}": no value provided for placeholder "abc", did you mean "bcd"? + */ + public function testPreparedCommandWithMissingValue() + { + $p = new Process('echo {{ abc }}'); + $p->run(null, array('bcd' => 'BCD')); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid command line "echo {{ abc }}": no values provided for any placeholders. + */ + public function testPreparedCommandWithNoValues() + { + $p = new Process('echo {{ abc }}'); + $p->run(); + } + public function testEnvArgument() { $env = array('FOO' => 'Foo', 'BAR' => 'Bar');