Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feature #32126 [Process] Allow writing portable "prepared" command li…
…nes (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
  • Loading branch information
fabpot committed Jun 26, 2019
2 parents f15722d + 3f8354f commit bd8ad3f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 5 deletions.
24 changes: 19 additions & 5 deletions src/Symfony/Component/Process/Process.php
Expand Up @@ -291,20 +291,23 @@ public function start(callable $callback = null, array $env = [])
$this->hasCallback = null !== $callback;
$descriptors = $this->getDescriptors();

if ($this->env) {
$env += $this->env;
}

$env += $this->getDefaultEnv();

if (\is_array($commandline = $this->commandline)) {
$commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));

if ('\\' !== \DIRECTORY_SEPARATOR) {
// 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 = ['suppress_errors' => true];

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

private function replacePlaceholders(string $commandline, array $env)
{
return preg_replace_callback('/"\$([_a-zA-Z]++[_a-zA-Z0-9]*+)"/', function ($matches) use ($commandline, $env) {
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
throw new InvalidArgumentException(sprintf('Command line is missing a value for key %s: %s.', $matches[0], $commandline));
}

return '\\' === \DIRECTORY_SEPARATOR ? $this->escapeArgument($env[$matches[1]]) : $matches[0];
}, $commandline);
}

private function getDefaultEnv()
{
$env = [];
Expand Down
44 changes: 44 additions & 0 deletions src/Symfony/Component/Process/Tests/ProcessTest.php
Expand Up @@ -1500,6 +1500,50 @@ public function provideEscapeArgument()
yield [1.1];
}

public function testPreparedCommand()
{
$p = Process::fromShellCommandline('echo "$abc"DEF');
$p->run(null, ['abc' => 'ABC']);

$this->assertSame('ABCDEF', rtrim($p->getOutput()));
}

public function testPreparedCommandMulti()
{
$p = Process::fromShellCommandline('echo "$abc""$def"');
$p->run(null, ['abc' => 'ABC', 'def' => 'DEF']);

$this->assertSame('ABCDEF', rtrim($p->getOutput()));
}

public function testPreparedCommandWithQuoteInIt()
{
$p = Process::fromShellCommandline('php -r "$code" "$def"');
$p->run(null, ['code' => 'echo $argv[1];', 'def' => '"DEF"']);

$this->assertSame('"DEF"', rtrim($p->getOutput()));
}

/**
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
* @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc".
*/
public function testPreparedCommandWithMissingValue()
{
$p = Process::fromShellCommandline('echo "$abc"');
$p->run(null, ['bcd' => 'BCD']);
}

/**
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
* @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc".
*/
public function testPreparedCommandWithNoValues()
{
$p = Process::fromShellCommandline('echo "$abc"');
$p->run(null, []);
}

public function testEnvArgument()
{
$env = ['FOO' => 'Foo', 'BAR' => 'Bar'];
Expand Down

0 comments on commit bd8ad3f

Please sign in to comment.